"""
Data backup service for enterprise data protection.
"""
from sqlalchemy.orm import Session
from typing import Optional, Dict, Any
from datetime import datetime, timedelta
from pathlib import Path
import json
import subprocess
import os
import shutil
from ...shared.config.settings import settings
from ...shared.config.logging_config import get_logger

logger = get_logger(__name__)

def is_running_in_docker() -> bool:
    """Check if the application is running inside a Docker container."""
    # Check for .dockerenv file (most reliable method)
    if os.path.exists('/.dockerenv'):
        return True
    # Check for Docker in cgroup
    try:
        with open('/proc/self/cgroup', 'r') as f:
            if 'docker' in f.read():
                return True
    except (FileNotFoundError, IOError):
        pass
    # Check environment variable
    if os.environ.get('DOCKER_CONTAINER') == 'true':
        return True
    return False

class BackupService:
    """Service for managing database backups."""
    
    BACKUP_DIR = Path('backups')
    RETENTION_DAYS = 30  # Keep backups for 30 days
    
    @staticmethod
    def ensure_backup_dir():
        """Ensure backup directory exists."""
        BackupService.BACKUP_DIR.mkdir(parents=True, exist_ok=True)
    
    @staticmethod
    def check_mysqldump_available() -> bool:
        """Check if mysqldump is available on the system."""
        return shutil.which('mysqldump') is not None
    
    @staticmethod
    def create_backup(db_name: str, db_user: str, db_password: str, db_host: str, db_port: str) -> Dict[str, Any]:
        """
        Create a database backup.
        Returns backup metadata.
        """
        BackupService.ensure_backup_dir()
        
        # Check if mysqldump is available
        if not BackupService.check_mysqldump_available():
            error_msg = (
                "mysqldump is not installed or not in PATH. "
                "Please install MySQL client tools: "
                "Ubuntu/Debian: sudo apt-get install mysql-client "
                "or CentOS/RHEL: sudo yum install mysql"
            )
            logger.error(error_msg)
            raise FileNotFoundError(error_msg)
        
        timestamp = datetime.utcnow().strftime('%Y%m%d_%H%M%S')
        backup_filename = f'backup_{db_name}_{timestamp}.sql'
        backup_path = BackupService.BACKUP_DIR / backup_filename
        
        try:
            # Detect Docker environment
            in_docker = is_running_in_docker()
            
            # Determine the correct host to use
            # In Docker, if host is localhost, try to use the MySQL service name or host.docker.internal
            actual_host = db_host
            if in_docker:
                if db_host in ('localhost', '127.0.0.1'):
                    # Try to use MySQL service name from docker-compose (common names)
                    mysql_service_name = os.environ.get('MYSQL_SERVICE_NAME', 'mysql')
                    actual_host = mysql_service_name
                    logger.info(f"Running in Docker, using MySQL service name: {actual_host}")
                else:
                    logger.info(f"Running in Docker, using configured host: {actual_host}")
            else:
                logger.info(f"Running on host, using configured host: {actual_host}")
            
            # Use mysqldump for MySQL/MariaDB
            # Note: In production, use proper credentials management
            env = os.environ.copy()
            if db_password:
                env['MYSQL_PWD'] = db_password
            
            # Get full path to mysqldump
            mysqldump_path = shutil.which('mysqldump')
            if not mysqldump_path:
                raise FileNotFoundError("mysqldump not found in PATH")
            
            # Build mysqldump command
            # Always use TCP protocol to avoid socket issues, especially in Docker
            cmd = [
                mysqldump_path,
                '--protocol=TCP',  # Force TCP/IP connection
                f'--user={db_user}',
                f'--host={actual_host}',
                f'--port={db_port}',
                '--single-transaction',
                '--routines',
                '--triggers',
                '--skip-lock-tables',  # Useful in Docker environments
                db_name
            ]
            
            logger.info(f"Executing mysqldump: {' '.join(cmd[:3])}... [password hidden]")
            
            with open(backup_path, 'w') as f:
                result = subprocess.run(
                    cmd,
                    stdout=f,
                    stderr=subprocess.PIPE,
                    env=env,
                    text=True,
                    timeout=300  # 5 minute timeout
                )
            
            if result.returncode != 0:
                error_msg = result.stderr or 'Unknown error'
                # Provide more helpful error messages
                if 'Can\'t connect' in error_msg or '2002' in error_msg or '2003' in error_msg:
                    if in_docker:
                        enhanced_error = (
                            f"Connection failed. Running in Docker environment.\n"
                            f"Attempted to connect to: {actual_host}:{db_port}\n"
                            f"Original error: {error_msg}\n"
                            f"Tip: Ensure MySQL service is accessible from this container. "
                            f"If using docker-compose, the service name should be 'mysql' or set MYSQL_SERVICE_NAME env var."
                        )
                    else:
                        enhanced_error = (
                            f"Connection failed to MySQL server.\n"
                            f"Attempted to connect to: {actual_host}:{db_port}\n"
                            f"Original error: {error_msg}\n"
                            f"Tip: Ensure MySQL server is running and accessible at {actual_host}:{db_port}"
                        )
                    raise Exception(f'Backup failed: {enhanced_error}')
                raise Exception(f'Backup failed: {error_msg}')
            
            # Get backup size
            backup_size = backup_path.stat().st_size
            
            # Create metadata
            metadata = {
                'filename': backup_filename,
                'path': str(backup_path),
                'size_bytes': backup_size,
                'size_mb': round(backup_size / (1024 * 1024), 2),
                'created_at': datetime.utcnow().isoformat(),
                'database': db_name,
                'status': 'success'
            }
            
            # Save metadata
            metadata_path = backup_path.with_suffix('.json')
            with open(metadata_path, 'w') as f:
                json.dump(metadata, f, indent=2)
            
            logger.info(f'Backup created: {backup_filename} ({metadata["size_mb"]} MB)')
            return metadata
            
        except Exception as e:
            logger.error(f'Backup failed: {str(e)}', exc_info=True)
            raise
    
    @staticmethod
    def list_backups() -> list[Dict[str, Any]]:
        """List all available backups."""
        BackupService.ensure_backup_dir()
        
        backups = []
        for backup_file in BackupService.BACKUP_DIR.glob('backup_*.sql'):
            metadata_file = backup_file.with_suffix('.json')
            if metadata_file.exists():
                try:
                    with open(metadata_file, 'r') as f:
                        metadata = json.load(f)
                        backups.append(metadata)
                except Exception as e:
                    logger.warning(f'Error reading backup metadata {metadata_file}: {str(e)}')
        
        # Sort by creation date (newest first)
        backups.sort(key=lambda x: x.get('created_at', ''), reverse=True)
        return backups
    
    @staticmethod
    def cleanup_old_backups() -> int:
        """Remove backups older than retention period."""
        BackupService.ensure_backup_dir()
        
        cutoff_date = datetime.utcnow() - timedelta(days=BackupService.RETENTION_DAYS)
        removed_count = 0
        
        for backup_file in BackupService.BACKUP_DIR.glob('backup_*.sql'):
            metadata_file = backup_file.with_suffix('.json')
            if metadata_file.exists():
                try:
                    with open(metadata_file, 'r') as f:
                        metadata = json.load(f)
                        created_at = datetime.fromisoformat(metadata.get('created_at', ''))
                        
                        if created_at < cutoff_date:
                            backup_file.unlink()
                            metadata_file.unlink()
                            removed_count += 1
                            logger.info(f'Removed old backup: {backup_file.name}')
                except Exception as e:
                    logger.warning(f'Error processing backup {backup_file}: {str(e)}')
        
        return removed_count
    
    @staticmethod
    def get_backup_info(backup_filename: str) -> Optional[Dict[str, Any]]:
        """Get information about a specific backup."""
        backup_path = BackupService.BACKUP_DIR / backup_filename
        metadata_path = backup_path.with_suffix('.json')
        
        if not metadata_path.exists():
            return None
        
        try:
            with open(metadata_path, 'r') as f:
                return json.load(f)
        except Exception as e:
            logger.error(f'Error reading backup info: {str(e)}')
            return None

backup_service = BackupService()

