#!/usr/bin/env python3
"""
Backup script for StreamA2026
Creates a backup of PostgreSQL database, Redis, Caddy SSL, and configuration files.
"""
import os
import subprocess
import tarfile
import json
import tempfile
from datetime import datetime
from pathlib import Path

# Configuration
BACKUP_DIR = os.getenv('BACKUP_DIR', '/app/backups')
POSTGRES_HOST = os.getenv('POSTGRES_HOST', 'postgres')
POSTGRES_PORT = os.getenv('POSTGRES_PORT', '5432')
POSTGRES_DB = os.getenv('POSTGRES_DB', 'streama2026')
POSTGRES_USER = os.getenv('POSTGRES_USER', 'streama')
POSTGRES_PASSWORD = os.getenv('POSTGRES_PASSWORD', 'stream')
REDIS_HOST = os.getenv('REDIS_HOST', 'redis')
REDIS_PORT = os.getenv('REDIS_PORT', '6379')
REDIS_PASSWORD = os.getenv('REDIS_PASSWORD', 'redis')

# Create temporary directory
import tempfile
TEMP_DIR = Path(tempfile.gettempdir()) / 'streama_backup'
TEMP_DIR.mkdir(exist_ok=True)

def get_flyway_version():
    """Get current Flyway migration version."""
    try:
        result = subprocess.run(
            ['docker', 'exec', 'streama2026-flyway', 'flyway', 'info'],
            capture_output=True,
            text=True
        )
        # Parse Flyway info to get current version
        for line in result.stdout.split('\n'):
            if '|' in line and 'Current version' in line.lower():
                parts = line.split('|')
                if len(parts) > 3:
                    return parts[3].strip()
        return "unknown"
    except Exception as e:
        print(f"Error getting Flyway version: {e}")
        return "unknown"

def backup_postgresql():
    """Backup PostgreSQL database using pg_dump."""
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
    sql_file = TEMP_DIR / f'postgres_backup_{timestamp}.sql'
    
    try:
        cmd = [
            'docker', 'exec', 'streama2026-postgres',
            'pg_dump', '-U', POSTGRES_USER, '-d', POSTGRES_DB
        ]
        
        with open(sql_file, 'w') as f:
            subprocess.run(cmd, stdout=f, check=True)
        
        print(f"PostgreSQL backup saved to {sql_file}")
        return str(sql_file)
    except Exception as e:
        print(f"PostgreSQL backup failed: {e}")
        return None

def backup_redis():
    """Backup Redis database using redis-cli."""
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
    rdb_file = TEMP_DIR / f'redis_backup_{timestamp}.rdb'
    
    try:
        # Redis needs to be in a container, so we copy the dump file
        cmd = [
            'docker', 'cp',
            'streama2026-redis:/data/dump.rdb',
            str(rdb_file)
        ]
        
        subprocess.run(cmd, check=True)
        print(f"Redis backup saved to {rdb_file}")
        return str(rdb_file)
    except Exception as e:
        print(f"Redis backup failed: {e}")
        return None

def backup_caddy():
    """Backup Caddy SSL certificates and configuration."""
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
    caddy_backup_file = TEMP_DIR / f'caddy_backup_{timestamp}.tar.gz'
    
    try:
        # Backup Caddy data and config volumes
        with tarfile.open(caddy_backup_file, 'w:gz') as tar:
            # Backup Caddy data volume from container
            try:
                caddy_data_temp = TEMP_DIR / 'caddy_data_temp'
                caddy_data_temp.mkdir(exist_ok=True)
                cmd = [
                    'docker', 'cp',
                    'streama2026-caddy:/data',
                    str(caddy_data_temp)
                ]
                subprocess.run(cmd, check=True)
                tar.add(caddy_data_temp, arcname='caddy_data')
                print("Caddy data volume backed up")
            except Exception as e:
                print(f"Caddy data volume backup skipped: {e}")
            
            # Backup Caddy config volume from container
            try:
                caddy_config_temp = TEMP_DIR / 'caddy_config_temp'
                caddy_config_temp.mkdir(exist_ok=True)
                cmd = [
                    'docker', 'cp',
                    'streama2026-caddy:/config',
                    str(caddy_config_temp)
                ]
                subprocess.run(cmd, check=True)
                tar.add(caddy_config_temp, arcname='caddy_config')
                print("Caddy config volume backed up")
            except Exception as e:
                print(f"Caddy config volume backup skipped: {e}")
        
        print(f"Caddy backup saved to {caddy_backup_file}")
        return str(caddy_backup_file)
    except Exception as e:
        print(f"Caddy backup failed: {e}")
        return None

def backup_config():
    """Backup configuration files."""
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
    config_backup_file = TEMP_DIR / f'config_backup_{timestamp}.tar.gz'
    
    try:
        with tarfile.open(config_backup_file, 'w:gz') as tar:
            # Backup .env file
            if os.path.exists('.env'):
                tar.add('.env', arcname='.env')
            
            # Backup docker-compose.yml
            if os.path.exists('docker-compose.yml'):
                tar.add('docker-compose.yml', arcname='docker-compose.yml')
            
            # Backup nginx config
            if os.path.exists('nginx'):
                tar.add('nginx', arcname='nginx')
        
        print(f"Config backup saved to {config_backup_file}")
        return str(config_backup_file)
    except Exception as e:
        print(f"Config backup failed: {e}")
        return None

def create_backup():
    """Create a complete backup."""
    print("Starting backup process...")
    
    # Create backup directory
    Path(BACKUP_DIR).mkdir(parents=True, exist_ok=True)
    
    # Get Flyway version
    flyway_version = get_flyway_version()
    print(f"Flyway version: {flyway_version}")
    
    # Backup components
    components = []
    files_to_backup = []
    
    # PostgreSQL
    postgres_file = backup_postgresql()
    if postgres_file:
        components.append('postgresql')
        files_to_backup.append(postgres_file)
    
    # Redis
    redis_file = backup_redis()
    if redis_file:
        components.append('redis')
        files_to_backup.append(redis_file)
    
    # Caddy
    caddy_file = backup_caddy()
    if caddy_file:
        components.append('caddy')
        files_to_backup.append(caddy_file)
    
    # Config
    config_file = backup_config()
    if config_file:
        components.append('config')
        files_to_backup.append(config_file)
    
    # Create backup archive
    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
    backup_filename = f'streama-backup-{timestamp}.tar.gz'
    backup_path = Path(BACKUP_DIR) / backup_filename
    
    try:
        with tarfile.open(backup_path, 'w:gz') as tar:
            # Add all backup files
            for file_path in files_to_backup:
                if file_path and os.path.exists(file_path):
                    tar.add(file_path, arcname=os.path.basename(file_path))
            
            # 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_DIR / 'backup_metadata.json'
            with open(metadata_path, 'w') as f:
                json.dump(metadata, f, indent=2)
            tar.add(metadata_path, arcname='backup_metadata.json')
        
        print(f"Backup created successfully: {backup_filename}")
        print(f"Backup size: {backup_path.stat().st_size / (1024 * 1024):.2f} MB")
        
        # Cleanup temporary files
        for file_path in files_to_backup:
            if file_path and os.path.exists(file_path):
                os.remove(file_path)
        if os.path.exists(metadata_path):
            os.remove(metadata_path)
        
        return backup_filename
        
    except Exception as e:
        print(f"Failed to create backup archive: {e}")
        return None

if __name__ == '__main__':
    result = create_backup()
    if result:
        print(f"Backup completed successfully: {result}")
        exit(0)
    else:
        print("Backup failed")
        exit(1)
