from rest_framework.permissions import IsAdminUser
from rest_framework.decorators import api_view, permission_classes
from django.http import JsonResponse, FileResponse
from django.conf import settings
from datetime import datetime
import os
import shutil
import sqlite3

@api_view(['POST'])
@permission_classes([IsAdminUser])
def backup_database(request):
    db_engine = settings.DATABASES['default']['ENGINE']
    backup_dir = os.path.join(settings.BASE_DIR, 'backups')
    os.makedirs(backup_dir, exist_ok=True)
    
    timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
    
    try:
        if 'sqlite' in db_engine:
            db_path = settings.DATABASES['default']['NAME']
            backup_file = os.path.join(backup_dir, f"backup_{timestamp}.db")
            shutil.copy2(db_path, backup_file)
        else:
            db_name = settings.DATABASES['default']['NAME']
            db_user = settings.DATABASES['default']['USER']
            db_password = settings.DATABASES['default']['PASSWORD']
            db_host = settings.DATABASES['default']['HOST']
            db_port = settings.DATABASES['default']['PORT']
            backup_file = os.path.join(backup_dir, f"backup_{timestamp}.sql")
            
            import subprocess
            dump_command = ['mysqldump', '-u', db_user, f'-p{db_password}', '-h', db_host, '--port', str(db_port), db_name]
            with open(backup_file, 'w') as f:
                subprocess.run(dump_command, stdout=f, stderr=subprocess.PIPE, check=True)
        
        file_size = os.path.getsize(backup_file)
        return JsonResponse({
            "message": "Database backup successful!",
            "filename": os.path.basename(backup_file),
            "size": file_size,
            "created_at": timestamp
        })
    except Exception as e:
        return JsonResponse({"error": str(e)}, status=500)


@api_view(['POST'])
@permission_classes([IsAdminUser])
def restore_database(request):
    filename = request.data.get('filename')
    if not filename:
        return JsonResponse({"error": "No backup file provided"}, status=400)
    
    backup_dir = os.path.join(settings.BASE_DIR, 'backups')
    backup_file_path = os.path.join(backup_dir, filename)
    
    if not os.path.exists(backup_file_path):
        return JsonResponse({"error": "Backup file does not exist"}, status=400)
    
    db_engine = settings.DATABASES['default']['ENGINE']
    
    try:
        if 'sqlite' in db_engine:
            db_path = settings.DATABASES['default']['NAME']
            backup_conn = sqlite3.connect(backup_file_path)
            current_conn = sqlite3.connect(db_path)
            backup_cursor = backup_conn.cursor()
            current_cursor = current_conn.cursor()
            
            # Check if database has data
            current_cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'")
            current_tables = current_cursor.fetchall()
            has_data = False
            
            if current_tables:
                for table_name, in current_tables:
                    current_cursor.execute(f"SELECT COUNT(*) FROM {table_name}")
                    count = current_cursor.fetchone()[0]
                    if count > 0:
                        has_data = True
                        break
            
            if not has_data:
                # Full restore if no data exists
                current_conn.close()
                backup_conn.close()
                shutil.copy2(backup_file_path, db_path)
                return JsonResponse({"message": "Database fully restored successfully!"})
            
            # Merge restore - only add missing records
            backup_cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'")
            tables = backup_cursor.fetchall()
            restored_count = 0
            
            for table_name, in tables:
                backup_cursor.execute(f"PRAGMA table_info({table_name})")
                columns_info = backup_cursor.fetchall()
                pk_column = next((col[1] for col in columns_info if col[5] == 1), 'id')
                column_names = [col[1] for col in columns_info]
                
                backup_cursor.execute(f"SELECT * FROM {table_name}")
                backup_records = backup_cursor.fetchall()
                
                for record in backup_records:
                    pk_value = record[next(i for i, col in enumerate(columns_info) if col[5] == 1)]
                    current_cursor.execute(f"SELECT 1 FROM {table_name} WHERE {pk_column} = ?", (pk_value,))
                    
                    if not current_cursor.fetchone():
                        placeholders = ','.join(['?' for _ in record])
                        columns = ','.join(column_names)
                        current_cursor.execute(f"INSERT INTO {table_name} ({columns}) VALUES ({placeholders})", record)
                        restored_count += 1
            
            current_conn.commit()
            backup_conn.close()
            current_conn.close()
            return JsonResponse({"message": f"Merged {restored_count} new records successfully!"})
        else:
            # MySQL merge restore
            db_name = settings.DATABASES['default']['NAME']
            db_user = settings.DATABASES['default']['USER']
            db_password = settings.DATABASES['default']['PASSWORD']
            db_host = settings.DATABASES['default']['HOST']
            db_port = settings.DATABASES['default']['PORT']
            
            import subprocess
            import tempfile
            import pymysql
            
            # Check if database has data
            conn = pymysql.connect(host=db_host, user=db_user, password=db_password, database=db_name, port=int(db_port))
            cursor = conn.cursor()
            cursor.execute("SHOW TABLES")
            tables = cursor.fetchall()
            has_data = False
            
            for table, in tables:
                cursor.execute(f"SELECT COUNT(*) FROM {table}")
                if cursor.fetchone()[0] > 0:
                    has_data = True
                    break
            cursor.close()
            conn.close()
            
            with open(backup_file_path, 'r') as f:
                backup_content = f.read()
            
            if not has_data:
                # Full restore
                restore_command = ['mysql', '-u', db_user, f'-p{db_password}', '-h', db_host, '--port', str(db_port), db_name]
                with open(backup_file_path, 'rb') as f:
                    subprocess.run(restore_command, stdin=f, stderr=subprocess.PIPE, check=True)
                return JsonResponse({"message": "Database fully restored successfully!"})
            
            # Merge restore
            modified_content = backup_content.replace('INSERT INTO', 'INSERT IGNORE INTO')
            with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.sql') as temp_file:
                temp_file.write(modified_content)
                temp_file_path = temp_file.name
            
            try:
                restore_command = ['mysql', '-u', db_user, f'-p{db_password}', '-h', db_host, '--port', str(db_port), db_name]
                with open(temp_file_path, 'rb') as f:
                    subprocess.run(restore_command, stdin=f, stderr=subprocess.PIPE, check=True)
            finally:
                os.remove(temp_file_path)
            
            return JsonResponse({"message": "Merged new records successfully!"})
    except Exception as e:
        return JsonResponse({"error": str(e)}, status=500)


@api_view(['GET'])
@permission_classes([IsAdminUser])
def list_backups(request):
    backup_dir = os.path.join(settings.BASE_DIR, 'backups')
    
    if not os.path.exists(backup_dir):
        return JsonResponse({"backups": []})
    
    db_engine = settings.DATABASES['default']['ENGINE']
    extension = '.db' if 'sqlite' in db_engine else '.sql'
    
    backups = []
    for filename in os.listdir(backup_dir):
        if filename.endswith(extension):
            file_path = os.path.join(backup_dir, filename)
            stat = os.stat(file_path)
            backups.append({
                "filename": filename,
                "size": stat.st_size,
                "created_at": datetime.fromtimestamp(stat.st_mtime).strftime('%Y-%m-%d %H:%M:%S')
            })
    
    backups.sort(key=lambda x: x['created_at'], reverse=True)
    return JsonResponse({"backups": backups})


@api_view(['DELETE'])
@permission_classes([IsAdminUser])
def delete_backup(request):
    filename = request.data.get('filename')
    if not filename:
        return JsonResponse({"error": "No backup file provided"}, status=400)
    
    backup_dir = os.path.join(settings.BASE_DIR, 'backups')
    backup_file_path = os.path.join(backup_dir, filename)
    
    if not os.path.exists(backup_file_path):
        return JsonResponse({"error": "Backup file does not exist"}, status=400)
    
    try:
        os.remove(backup_file_path)
        return JsonResponse({"message": "Backup file deleted successfully!"})
    except Exception as e:
        return JsonResponse({"error": str(e)}, status=500)


@api_view(['POST'])
@permission_classes([IsAdminUser])
def download_backup(request):
    filename = request.data.get('filename')
    if not filename:
        return JsonResponse({"error": "No backup file provided"}, status=400)
    
    backup_dir = os.path.join(settings.BASE_DIR, 'backups')
    backup_file_path = os.path.join(backup_dir, filename)
    
    if not os.path.exists(backup_file_path):
        return JsonResponse({"error": "Backup file does not exist"}, status=404)
    
    response = FileResponse(open(backup_file_path, 'rb'), as_attachment=True, filename=filename)
    response['Content-Disposition'] = f'attachment; filename="{filename}"'
    return response
