"""
Backup and Restore API endpoints for database management.
Admin-only access required.
"""
import os
import subprocess
import gzip
import shutil
from datetime import datetime
from typing import List
import boto3
from botocore.exceptions import ClientError
from dotenv import load_dotenv

load_dotenv()


def get_environment() -> str:
    """Detect environment based on FRONTEND_URL or deployment environment"""
    # Check FRONTEND_URL first
    frontend_url = os.getenv('FRONTEND_URL', '')
    if 'myrecipes.dvirlabs.com' in frontend_url or 'my-recipes.dvirlabs.com' in frontend_url:
        return 'prod'
    
    # Check if explicitly set via ENV variable
    env_var = os.getenv('ENVIRONMENT', '').lower()
    if env_var in ['prod', 'production']:
        return 'prod'
    
    # Check database host (if not localhost, likely production)
    db_host = os.getenv('DB_HOST', 'localhost')
    if db_host not in ['localhost', '127.0.0.1', 'postgres']:
        return 'prod'
    
    # Default to dev
    return 'dev'


def get_r2_bucket() -> str:
    """Get R2 bucket name based on environment"""
    env = get_environment()
    return f'my-recipes-db-bkp-{env}'


def get_r2_client():
    """Get configured R2 client"""
    return boto3.client(
        's3',
        endpoint_url=os.getenv('R2_ENDPOINT'),
        aws_access_key_id=os.getenv('R2_ACCESS_KEY'),
        aws_secret_access_key=os.getenv('R2_SECRET_KEY'),
        region_name='auto'
    )


def create_db_dump() -> str:
    """Create a database dump file"""
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
    env = get_environment()
    backup_dir = os.path.join(os.path.dirname(__file__), 'backups')
    os.makedirs(backup_dir, exist_ok=True)
    
    dump_file = os.path.join(backup_dir, f'recipes_db_{env}_{timestamp}.sql')
    
    db_host = os.getenv('DB_HOST', 'localhost')
    db_port = os.getenv('DB_PORT', '5432')
    db_name = os.getenv('DB_NAME', 'recipes_db')
    db_user = os.getenv('DB_USER', 'postgres')
    db_password = os.getenv('DB_PASSWORD', 'postgres')
    
    env = os.environ.copy()
    env['PGPASSWORD'] = db_password
    
    cmd = [
        'pg_dump',
        '-h', db_host,
        '-p', db_port,
        '-U', db_user,
        '-d', db_name,
        '--no-owner',
        '--no-acl',
        '-f', dump_file
    ]
    
    result = subprocess.run(cmd, env=env, capture_output=True, text=True)
    
    if result.returncode != 0:
        raise Exception(f"pg_dump failed: {result.stderr}")
    
    return dump_file


def compress_file(file_path: str) -> str:
    """Compress a file with gzip"""
    compressed_path = f"{file_path}.gz"
    
    with open(file_path, 'rb') as f_in:
        with gzip.open(compressed_path, 'wb', compresslevel=9) as f_out:
            shutil.copyfileobj(f_in, f_out)
    
    os.remove(file_path)
    return compressed_path


def upload_to_r2(file_path: str) -> str:
    """Upload file to R2"""
    s3_client = get_r2_client()
    bucket_name = get_r2_bucket()
    file_name = os.path.basename(file_path)
    
    try:
        s3_client.upload_file(file_path, bucket_name, file_name)
        return file_name
    except ClientError as e:
        raise Exception(f"R2 upload failed: {str(e)}")


def list_r2_backups() -> List[dict]:
    """List all backups in R2"""
    s3_client = get_r2_client()
    bucket_name = get_r2_bucket()
    
    try:
        response = s3_client.list_objects_v2(Bucket=bucket_name)
        
        if 'Contents' not in response:
            return []
        
        backups = []
        for obj in response['Contents']:
            backups.append({
                'filename': obj['Key'],
                'size': obj['Size'],
                'last_modified': obj['LastModified'].isoformat()
            })
        
        backups.sort(key=lambda x: x['last_modified'], reverse=True)
        return backups
        
    except ClientError as e:
        raise Exception(f"Failed to list R2 backups: {str(e)}")


def download_from_r2(filename: str) -> str:
    """Download a backup from R2"""
    s3_client = get_r2_client()
    bucket_name = get_r2_bucket()
    
    backup_dir = os.path.join(os.path.dirname(__file__), 'backups')
    os.makedirs(backup_dir, exist_ok=True)
    
    local_path = os.path.join(backup_dir, filename)
    
    try:
        s3_client.download_file(bucket_name, filename, local_path)
        return local_path
    except ClientError as e:
        raise Exception(f"R2 download failed: {str(e)}")


def decompress_file(compressed_path: str) -> str:
    """Decompress a gzipped file"""
    if not compressed_path.endswith('.gz'):
        raise ValueError("File must be gzipped (.gz)")
    
    decompressed_path = compressed_path[:-3]
    
    with gzip.open(compressed_path, 'rb') as f_in:
        with open(decompressed_path, 'wb') as f_out:
            shutil.copyfileobj(f_in, f_out)
    
    return decompressed_path


def restore_database(sql_file: str) -> None:
    """Restore database from SQL file"""
    db_host = os.getenv('DB_HOST', 'localhost')
    db_port = os.getenv('DB_PORT', '5432')
    db_name = os.getenv('DB_NAME', 'recipes_db')
    db_user = os.getenv('DB_USER', 'postgres')
    db_password = os.getenv('DB_PASSWORD', 'postgres')
    
    env = os.environ.copy()
    env['PGPASSWORD'] = db_password
    
    # Drop all tables first
    drop_cmd = [
        'psql',
        '-h', db_host,
        '-p', db_port,
        '-U', db_user,
        '-d', db_name,
        '-c', 'DROP SCHEMA public CASCADE; CREATE SCHEMA public;'
    ]
    
    drop_result = subprocess.run(drop_cmd, env=env, capture_output=True, text=True)
    
    if drop_result.returncode != 0:
        raise Exception(f"Failed to drop schema: {drop_result.stderr}")
    
    # Restore from backup
    restore_cmd = [
        'psql',
        '-h', db_host,
        '-p', db_port,
        '-U', db_user,
        '-d', db_name,
        '-f', sql_file
    ]
    
    restore_result = subprocess.run(restore_cmd, env=env, capture_output=True, text=True)
    
    if restore_result.returncode != 0:
        raise Exception(f"Database restore failed: {restore_result.stderr}")


def perform_backup() -> dict:
    """Perform complete backup process"""
    try:
        # Create dump
        dump_file = create_db_dump()
        
        # Compress
        compressed_file = compress_file(dump_file)
        
        # Upload to R2
        r2_filename = upload_to_r2(compressed_file)
        
        # Get file size
        file_size = os.path.getsize(compressed_file)
        
        # Clean up local file
        os.remove(compressed_file)
        
        return {
            'success': True,
            'filename': r2_filename,
            'size': file_size,
            'timestamp': datetime.now().isoformat()
        }
    except Exception as e:
        return {
            'success': False,
            'error': str(e)
        }


def perform_restore(filename: str) -> dict:
    """Perform complete restore process"""
    try:
        # Download from R2
        compressed_file = download_from_r2(filename)
        
        # Decompress
        sql_file = decompress_file(compressed_file)
        
        # Restore database
        restore_database(sql_file)
        
        # Clean up
        os.remove(compressed_file)
        os.remove(sql_file)
        
        return {
            'success': True,
            'filename': filename,
            'timestamp': datetime.now().isoformat()
        }
    except Exception as e:
        return {
            'success': False,
            'error': str(e)
        }
