from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks, UploadFile, File
from pydantic import BaseModel
from typing import List, Optional
from pathlib import Path
import os
import subprocess
import json
import tarfile
from datetime import datetime
import shutil
import threading
from ..auth import get_current_admin_user, User
from ..models.user import User as UserModel

router = APIRouter(prefix="/api/admin/backup", tags=["backup"])

# Global restore status
restore_status = {
    "is_restoring": False,
    "started_at": None,
    "filename": None,
    "message": None,
    "completed_at": None,
    "success": None
}
restore_lock = threading.Lock()


class BackupMetadata(BaseModel):
    filename: str
    backup_version: str
    flyway_version: str
    created_at: str
    backup_size_bytes: int
    components: List[str]


class BackupListResponse(BaseModel):
    backups: List[BackupMetadata]


BACKUP_DIR = Path("/app/backups")


def get_backup_metadata(filename: str) -> Optional[dict]:
    """Extract metadata from backup archive."""
    try:
        import tarfile
        import tempfile
        
        backup_path = BACKUP_DIR / filename
        if not backup_path.exists():
            return None
        
        with tempfile.TemporaryDirectory() as temp_dir:
            with tarfile.open(backup_path, "r:gz") as tar:
                # Find the backup_metadata.json file in the archive
                metadata_file = None
                for member in tar.getmembers():
                    if member.name.endswith('backup_metadata.json'):
                        metadata_file = member
                        break
                
                if not metadata_file:
                    print(f"Error: backup_metadata.json not found in archive")
                    return None
                
                tar.extract(metadata_file, path=temp_dir)
            
            # The file might be in a subdirectory
            metadata_path = None
            for root, dirs, files in os.walk(temp_dir):
                if 'backup_metadata.json' in files:
                    metadata_path = Path(root) / 'backup_metadata.json'
                    break
            
            if not metadata_path:
                print(f"Error: backup_metadata.json not found after extraction")
                return None
            
            with open(metadata_path, 'r') as f:
                return json.load(f)
    except Exception as e:
        print(f"Error reading backup metadata: {e}")
        return None


@router.post("/create", status_code=status.HTTP_201_CREATED)
async def create_backup(
    background_tasks: BackgroundTasks,
    current_user: UserModel = Depends(get_current_admin_user)
):
    """Create a manual backup."""
    try:
        # Create backup directory
        BACKUP_DIR.mkdir(parents=True, exist_ok=True)
        
        # Get Flyway version from flyway_schema_history table
        from sqlalchemy import create_engine, text
        flyway_version = "unknown"
        try:
            db_url = os.getenv('DATABASE_URL')
            if db_url:
                engine = create_engine(db_url)
                with engine.connect() as conn:
                    result = conn.execute(text("SELECT version FROM flyway_schema_history ORDER BY installed_rank DESC LIMIT 1"))
                    row = result.fetchone()
                    if row:
                        flyway_version = row[0]
        except Exception as e:
            print(f"Error getting Flyway version: {e}")
        
        # Create timestamp
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        backup_filename = f'streama-backup-{timestamp}.tar.gz'
        backup_path = BACKUP_DIR / backup_filename
        
        # Create temporary directory for backup files
        import tempfile
        with tempfile.TemporaryDirectory() as temp_dir:
            temp_path = Path(temp_dir)
            components = []
            
            # Backup PostgreSQL using pg_dump (installed in container)
            try:
                sql_file = temp_path / f'postgres_backup_{timestamp}.sql'
                cmd = [
                    'pg_dump',
                    '-h', os.getenv('POSTGRES_HOST', 'postgres'),
                    '-U', os.getenv('POSTGRES_USER', 'streama'),
                    '-d', os.getenv('POSTGRES_DB', 'streama2026'),
                    '-f', str(sql_file)
                ]
                # Set PGPASSWORD environment variable for pg_dump
                env = os.environ.copy()
                env['PGPASSWORD'] = os.getenv('POSTGRES_PASSWORD', 'stream')
                subprocess.run(cmd, env=env, check=True)
                components.append('postgresql')
            except Exception as e:
                print(f"PostgreSQL backup failed: {e}")
            
            # Backup Redis using redis-cli (installed in container)
            try:
                rdb_file = temp_path / f'redis_backup_{timestamp}.rdb'
                # Redis dump is already saved, we just need to copy it from the volume
                # Since we can't access the volume directly, we skip this for now
                # Or we could use redis-cli to save a dump
                cmd = [
                    'redis-cli',
                    '-h', os.getenv('REDIS_HOST', 'redis'),
                    '-a', os.getenv('REDIS_PASSWORD', 'redis'),
                    'BGSAVE'
                ]
                subprocess.run(cmd, check=True)
                components.append('redis')
            except Exception as e:
                print(f"Redis backup failed: {e}")
            
            # Backup Caddy SSL certificates and configuration
            try:
                caddy_backup_file = temp_path / f'caddy_backup_{timestamp}.tar.gz'
                with tarfile.open(caddy_backup_file, 'w:gz') as tar:
                    # Backup Caddy data volume (SSL certificates)
                    if os.path.exists('/caddy_data'):
                        tar.add('/caddy_data', arcname='caddy_data')
                    # Backup Caddy config volume
                    if os.path.exists('/caddy_config'):
                        tar.add('/caddy_config', arcname='caddy_config')
                components.append('caddy')
            except Exception as e:
                print(f"Caddy backup failed: {e}")
            
            # Backup Config files
            try:
                config_backup_file = temp_path / f'config_backup_{timestamp}.tar.gz'
                with tarfile.open(config_backup_file, 'w:gz') as tar:
                    if os.path.exists('/app/.env'):
                        tar.add('/app/.env', arcname='.env')
                    if os.path.exists('/app/docker-compose.yml'):
                        tar.add('/app/docker-compose.yml', arcname='docker-compose.yml')
                    if os.path.exists('/app/nginx'):
                        tar.add('/app/nginx', arcname='nginx')
                components.append('config')
            except Exception as e:
                print(f"Config backup failed: {e}")
            
            # Create backup archive
            with tarfile.open(backup_path, 'w:gz') as tar:
                # Add all backup files
                for file in temp_path.glob('*'):
                    if file.is_file():
                        tar.add(file, arcname=file.name)
                
                # Create metadata
                metadata = {
                    'backup_version': '2.0',
                    'flyway_version': flyway_version,
                    'created_at': datetime.now().isoformat(),
                    'components': components,
                    'backup_size_bytes': backup_path.stat().st_size if backup_path.exists() else 0
                }
                
                # Add metadata as JSON file
                metadata_path = temp_path / 'backup_metadata.json'
                with open(metadata_path, 'w') as f:
                    json.dump(metadata, f, indent=2)
                tar.add(metadata_path, arcname='backup_metadata.json')
        
        metadata = get_backup_metadata(backup_filename)
        
        return {
            "message": "Backup created successfully",
            "filename": backup_filename,
            "metadata": metadata
        }
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Backup creation failed: {str(e)}"
        )


@router.get("", response_model=BackupListResponse)
async def list_backups(
    current_user: UserModel = Depends(get_current_admin_user)
):
    """List all available backups."""
    try:
        if not BACKUP_DIR.exists():
            return BackupListResponse(backups=[])
        
        backups = []
        for backup_file in sorted(BACKUP_DIR.glob("streama-backup-*.tar.gz"), reverse=True):
            metadata = get_backup_metadata(backup_file.name)
            if metadata:
                backups.append(BackupMetadata(
                    filename=backup_file.name,
                    backup_version=metadata.get("backup_version", "unknown"),
                    flyway_version=metadata.get("flyway_version", "unknown"),
                    created_at=metadata.get("created_at", "unknown"),
                    backup_size_bytes=metadata.get("backup_size_bytes", 0),
                    components=metadata.get("components", [])
                ))
        
        return BackupListResponse(backups=backups)
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to list backups: {str(e)}"
        )


@router.get("/{filename}")
async def download_backup(
    filename: str,
    current_user: UserModel = Depends(get_current_admin_user)
):
    """Download a backup file."""
    try:
        backup_path = BACKUP_DIR / filename
        if not backup_path.exists():
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Backup file not found"
            )
        
        from fastapi.responses import FileResponse
        return FileResponse(
            backup_path,
            media_type="application/gzip",
            filename=filename
        )
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to download backup: {str(e)}"
        )


@router.delete("/{filename}")
async def delete_backup(
    filename: str,
    current_user: UserModel = Depends(get_current_admin_user)
):
    """Delete a backup file."""
    try:
        backup_path = BACKUP_DIR / filename
        if not backup_path.exists():
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Backup file not found"
            )
        
        backup_path.unlink()
        return {"message": "Backup deleted successfully"}
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to delete backup: {str(e)}"
        )


@router.post("/upload")
async def upload_backup(
    file: UploadFile = File(...),
    current_user: UserModel = Depends(get_current_admin_user)
):
    """Upload a backup file."""
    try:
        # Ensure backup directory exists
        BACKUP_DIR.mkdir(parents=True, exist_ok=True)
        
        # Validate file extension
        if not file.filename.endswith('.tar.gz'):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Only .tar.gz files are allowed"
            )
        
        # Save file to backup directory
        backup_path = BACKUP_DIR / file.filename
        with backup_path.open("wb") as buffer:
            shutil.copyfileobj(file.file, buffer)
        
        return {
            "message": "Backup uploaded successfully",
            "filename": file.filename
        }
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to upload backup: {str(e)}"
        )


@router.post("/restore/{filename}")
async def restore_backup(
    filename: str,
    current_user: UserModel = Depends(get_current_admin_user)
):
    """Restore from a backup file."""
    try:
        print(f"[API] Restore requested for: {filename}")
        backup_path = BACKUP_DIR / filename
        if not backup_path.exists():
            print(f"[API] Backup file not found: {backup_path}")
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Backup file not found"
            )
        
        # Check if restore is already in progress
        with restore_lock:
            if restore_status["is_restoring"]:
                raise HTTPException(
                    status_code=status.HTTP_409_CONFLICT,
                    detail="Restore already in progress"
                )
            
            # Set restore status to in progress
            restore_status["is_restoring"] = True
            restore_status["started_at"] = datetime.now().isoformat()
            restore_status["filename"] = filename
            restore_status["message"] = "Restore in progress"
            restore_status["completed_at"] = None
            restore_status["success"] = None
        
        print(f"[API] Starting restore process in background thread...")
        print(f"[API] Restore started at: {restore_status['started_at']}")
        
        def run_restore():
            """Run restore script in background thread."""
            try:
                env = os.environ.copy()
                env['BACKUP_DIR'] = '/app/backups'
                env['POSTGRES_HOST'] = os.getenv('POSTGRES_HOST', 'postgres')
                env['POSTGRES_PORT'] = os.getenv('POSTGRES_PORT', '5432')
                env['POSTGRES_DB'] = os.getenv('POSTGRES_DB', 'streama2026')
                env['POSTGRES_USER'] = os.getenv('POSTGRES_USER', 'streama')
                env['POSTGRES_PASSWORD'] = os.getenv('POSTGRES_PASSWORD', 'streama')
                env['REDIS_HOST'] = os.getenv('REDIS_HOST', 'redis')
                env['REDIS_PORT'] = os.getenv('REDIS_PORT', '6379')
                env['REDIS_PASSWORD'] = os.getenv('REDIS_PASSWORD', 'redis')
                
                print(f"[API] Executing restore script...")
                result = subprocess.run(
                    ["python3", "/app/scripts/restore.py", filename],
                    capture_output=True,
                    text=True,
                    cwd="/app",
                    env=env
                )
                
                print(f"[API] Restore script completed with return code: {result.returncode}")
                print(f"[API] Restore stdout: {result.stdout}")
                if result.stderr:
                    print(f"[API] Restore stderr: {result.stderr}")
                
                # Update restore status
                with restore_lock:
                    restore_status["is_restoring"] = False
                    restore_status["completed_at"] = datetime.now().isoformat()
                    restore_status["success"] = result.returncode == 0
                    restore_status["message"] = "Restore completed successfully" if result.returncode == 0 else f"Restore failed: {result.stderr}"
                
                print(f"[API] Restore completed at: {restore_status['completed_at']}")
                print(f"[API] Restore success: {restore_status['success']}")
                
            except Exception as e:
                print(f"[API] Background restore failed: {e}")
                with restore_lock:
                    restore_status["is_restoring"] = False
                    restore_status["completed_at"] = datetime.now().isoformat()
                    restore_status["success"] = False
                    restore_status["message"] = f"Restore failed: {str(e)}"
        
        thread = threading.Thread(target=run_restore)
        thread.start()
        
        return {
            "message": "Restore started in background. System is now in maintenance mode.",
            "filename": filename,
            "started_at": restore_status["started_at"]
        }
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Restore failed to start: {str(e)}"
        )


@router.get("/restore/status")
async def get_restore_status(
    current_user: UserModel = Depends(get_current_admin_user)
):
    """Get current restore status."""
    with restore_lock:
        return restore_status
