#!/usr/bin/env python3
"""
Script de prueba para verificar la funcionalidad de backup/restore con ZIP.
Prueba que los archivos .db, .db-shm y .db-wal se incluyen correctamente en el backup.
"""

import os
import sys
import tempfile
import zipfile
import shutil
import sqlite3
from pathlib import Path


def test_backup_creates_zip_with_all_files():
    """Verifica que el backup crea un ZIP con .db, .db-shm y .db-wal"""
    print("\n=== Test: Backup crea ZIP con todos los archivos ===")

    # Simular una base de datos SQLite
    temp_db_dir = tempfile.mkdtemp(prefix="thel4footbal_db_test_")
    db_path = Path(temp_db_dir) / "thel4footbal.db"

    # Crear una base de datos SQLite de prueba
    conn = sqlite3.connect(str(db_path))
    cursor = conn.cursor()
    cursor.execute("CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY)")
    conn.commit()
    conn.close()

    # Crear archivos SHM y WAL simulados (pueden no existir en algunos casos)
    (db_path.parent / f"{db_path.name}-shm").write_bytes(b"test shm")
    (db_path.parent / f"{db_path.name}-wal").write_bytes(b"test wal")

    try:
        # Simular la creación del backup como lo hace el endpoint
        temp_dir = tempfile.mkdtemp(prefix="thel4footbal_backup_test_")

        # Archivos SQLite a incluir en el backup (igual que en el endpoint)
        db_files = [
            (db_path, db_path.name),
            (db_path.parent / f"{db_path.name}-shm", f"{db_path.name}-shm"),
            (db_path.parent / f"{db_path.name}-wal", f"{db_path.name}-wal"),
        ]

        # Copiar archivos existentes
        for src, dst_name in db_files:
            if src.exists():
                shutil.copy2(src, Path(temp_dir) / dst_name)
                print(f"  ✓ Archivo copiado: {src.name}")
            else:
                print(f"  ⚠ Archivo no existe (opcional): {src.name}")

        # Crear el ZIP
        zip_path = Path(temp_dir) / "test_backup.zip"
        with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:
            for src, dst_name in db_files:
                file_to_zip = Path(temp_dir) / dst_name
                if file_to_zip.exists():
                    zf.write(file_to_zip, arcname=dst_name)

        # Verificar el contenido del ZIP
        with zipfile.ZipFile(zip_path, 'r') as zf:
            file_list = zf.namelist()
            print(f"\n  Archivos en el ZIP: {file_list}")

            # Verificar que los 3 archivos están presentes
            assert any(f.endswith('.db') for f in file_list), "El ZIP debe contener un archivo .db"
            assert any(f.endswith('-shm') for f in file_list), "El ZIP debe contener un archivo .db-shm"
            assert any(f.endswith('-wal') for f in file_list), "El ZIP debe contener un archivo .db-wal"

            print(f"  ✓ ZIP contiene archivo .db principal")
            print(f"  ✓ ZIP contiene archivo .db-shm")
            print(f"  ✓ ZIP contiene archivo .db-wal")

        print("  ✓ Test PASADO: Backup ZIP creado correctamente")
        return True

    finally:
        shutil.rmtree(temp_dir, ignore_errors=True)
        shutil.rmtree(temp_db_dir, ignore_errors=True)


def test_restore_extracts_all_files():
    """Verifica que el restore extrae correctamente todos los archivos del ZIP"""
    print("\n=== Test: Restore extrae todos los archivos del ZIP ===")

    # Crear un ZIP de prueba con archivos simulados
    temp_dir = tempfile.mkdtemp(prefix="thel4footbal_restore_test_")
    test_db_dir = Path(temp_dir) / "test_db"
    test_db_dir.mkdir()

    try:
        # Crear una base de datos SQLite de prueba
        test_db_path = test_db_dir / "test.db"
        conn = sqlite3.connect(str(test_db_path))
        cursor = conn.cursor()
        cursor.execute("CREATE TABLE IF NOT EXISTS test_table (id INTEGER PRIMARY KEY, name TEXT)")
        cursor.execute("INSERT INTO test_table (name) VALUES ('test_data')")
        conn.commit()
        conn.close()

        # Crear archivos SHM y WAR simulados
        (test_db_dir / "test.db-shm").write_bytes(b"test shm data")
        (test_db_dir / "test.db-wal").write_bytes(b"test wal data")

        # Crear el ZIP
        zip_path = Path(temp_dir) / "test_restore.zip"
        with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:
            for file_path in test_db_dir.iterdir():
                if file_path.is_file():
                    zf.write(file_path, arcname=file_path.name)

        # Simular el proceso de restore
        extract_dir = Path(temp_dir) / "extracted"
        extract_dir.mkdir()

        with zipfile.ZipFile(zip_path, 'r') as zf:
            zf.extractall(extract_dir)

        # Verificar archivos extraídos
        extracted_files = list(extract_dir.iterdir())
        print(f"  Archivos extraídos: {[f.name for f in extracted_files]}")

        # Verificar que el .db es válido
        extracted_db = extract_dir / "test.db"
        assert extracted_db.exists(), "El archivo .db debe existir"

        with open(extracted_db, 'rb') as f:
            header = f.read(16)
            assert header.startswith(b"SQLite format 3"), "Debe ser un archivo SQLite válido"

        print("  ✓ Archivo .db es SQLite válido")

        # Verificar SHM y WAL
        assert (extract_dir / "test.db-shm").exists(), "El archivo .db-shm debe existir"
        assert (extract_dir / "test.db-wal").exists(), "El archivo .db-wal debe existir"

        print("  ✓ Archivos .db-shm y .db-wal presentes")
        print("  ✓ Test PASADO: Restore extrae correctamente")
        return True

    finally:
        shutil.rmtree(temp_dir, ignore_errors=True)


def test_zip_format_validation():
    """Verifica la validación del formato ZIP"""
    print("\n=== Test: Validación de formato ZIP ===")

    # Crear un ZIP inválido (sin archivo .db)
    temp_dir = tempfile.mkdtemp(prefix="thel4footbal_invalid_zip_")
    invalid_zip_path = Path(temp_dir) / "invalid.zip"

    with zipfile.ZipFile(invalid_zip_path, 'w') as zf:
        zf.writestr("readme.txt", "Este es un archivo invalido")

    try:
        with zipfile.ZipFile(invalid_zip_path, 'r') as zf:
            file_list = zf.namelist()
            db_main_file = None
            for fname in file_list:
                if fname.endswith('.db') and not fname.startswith('__') and '/' not in fname:
                    db_main_file = fname
                    break

            assert db_main_file is None, "El ZIP no debería contener un archivo .db"
            print("  ✓ ZIP inválido correctamente detectado (sin .db)")

        # Crear un ZIP válido (con archivo .db)
        valid_zip_path = Path(temp_dir) / "valid.zip"
        test_db_path = Path(temp_dir) / "test.db"

        conn = sqlite3.connect(str(test_db_path))
        conn.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)")
        conn.commit()
        conn.close()

        with zipfile.ZipFile(valid_zip_path, 'w') as zf:
            zf.write(test_db_path, arcname="test.db")

        with zipfile.ZipFile(valid_zip_path, 'r') as zf:
            file_list = zf.namelist()
            db_main_file = None
            for fname in file_list:
                if fname.endswith('.db') and not fname.startswith('__') and '/' not in fname:
                    db_main_file = fname
                    break

            assert db_main_file is not None, "El ZIP debería contener un archivo .db"
            print(f"  ✓ ZIP válido correctamente detectado: {db_main_file}")

        print("  ✓ Test PASADO: Validación de formato ZIP correcta")
        return True

    finally:
        shutil.rmtree(temp_dir, ignore_errors=True)


if __name__ == "__main__":
    print("=" * 60)
    print("TEST DE FUNCIONALIDAD BACKUP/RESTORE CON ZIP")
    print("=" * 60)

    tests = [
        test_zip_format_validation,
        test_backup_creates_zip_with_all_files,
        test_restore_extracts_all_files,
    ]

    passed = 0
    failed = 0

    for test in tests:
        try:
            if test():
                passed += 1
        except Exception as e:
            failed += 1
            print(f"  ✗ Test FALLIDO: {e}")

    print("\n" + "=" * 60)
    print(f"RESULTADOS: {passed} pasados, {failed} fallidos")
    print("=" * 60)

    sys.exit(0 if failed == 0 else 1)
