"""
Test encrypted database management.
"""

import os
import shutil
import tempfile
from pathlib import Path

import pytest
from sqlalchemy import text

from src.local_deep_research.database.auth_db import get_auth_db_session
from src.local_deep_research.database.encrypted_db import DatabaseManager
from src.local_deep_research.database.models.auth import User


@pytest.fixture
def temp_data_dir():
    """Create a temporary data directory for testing."""
    temp_dir = tempfile.mkdtemp()
    yield Path(temp_dir)
    shutil.rmtree(temp_dir)


@pytest.fixture
def db_manager(temp_data_dir, monkeypatch):
    """Create a DatabaseManager with test configuration."""
    monkeypatch.setenv("LDR_DATA_DIR", str(temp_data_dir))
    manager = DatabaseManager()
    manager.data_dir = temp_data_dir / "encrypted_databases"
    manager.data_dir.mkdir(parents=True, exist_ok=True)
    return manager


@pytest.fixture
def auth_user(temp_data_dir, monkeypatch):
    """Create a test user in auth database."""
    monkeypatch.setenv("LDR_DATA_DIR", str(temp_data_dir))

    # Initialize auth database
    from src.local_deep_research.database.auth_db import init_auth_database

    init_auth_database()

    # Create user
    auth_db = get_auth_db_session()
    user = User(username="testuser")
    auth_db.add(user)
    auth_db.commit()
    auth_db.close()

    return user


class TestDatabaseManager:
    """Test the DatabaseManager class."""

    def test_user_db_path(self, db_manager):
        """Test generating user database paths."""
        path = db_manager._get_user_db_path("testuser")

        assert path.parent == db_manager.data_dir
        assert path.name.startswith("ldr_user_")
        assert path.name.endswith(".db")

        # Same username should generate same path
        path2 = db_manager._get_user_db_path("testuser")
        assert path == path2

        # Different username should generate different path
        path3 = db_manager._get_user_db_path("otheruser")
        assert path != path3

    def test_create_user_database(self, db_manager, auth_user):
        """Test creating an encrypted database for a user."""
        engine = db_manager.create_user_database("testuser", "testpassword123")

        assert engine is not None
        assert "testuser" in db_manager.connections

        # Test that database is encrypted and accessible
        with engine.connect() as conn:
            result = conn.execute(
                text("SELECT name FROM sqlite_master WHERE type='table'")
            )
            tables = [row[0] for row in result]
            assert len(tables) > 0  # Should have created tables

        # Database file should exist
        db_path = db_manager._get_user_db_path("testuser")
        assert db_path.exists()

    def test_create_duplicate_database(self, db_manager, auth_user):
        """Test that creating duplicate database fails."""
        db_manager.create_user_database("testuser", "testpassword123")

        with pytest.raises(ValueError, match="Database already exists"):
            db_manager.create_user_database("testuser", "differentpassword")

    def test_open_user_database(self, db_manager, auth_user):
        """Test opening an existing encrypted database."""
        # Create database first
        db_manager.create_user_database("testuser", "testpassword123")
        db_manager.close_user_database("testuser")

        # Open it
        engine = db_manager.open_user_database("testuser", "testpassword123")
        assert engine is not None

        # Test access
        with engine.connect() as conn:
            conn.execute(text("SELECT 1"))

    def test_open_with_wrong_password(self, db_manager, auth_user):
        """Test that opening with wrong password fails."""
        db_manager.create_user_database("testuser", "correctpassword")
        db_manager.close_user_database("testuser")

        engine = db_manager.open_user_database("testuser", "wrongpassword")
        assert engine is None

    def test_open_nonexistent_database(self, db_manager):
        """Test opening a database that doesn't exist."""
        engine = db_manager.open_user_database("nonexistent", "password")
        assert engine is None

    def test_close_user_database(self, db_manager, auth_user):
        """Test closing a user's database connection."""
        db_manager.create_user_database("testuser", "testpassword123")
        assert "testuser" in db_manager.connections

        db_manager.close_user_database("testuser")
        assert "testuser" not in db_manager.connections

    def test_get_session(self, db_manager, auth_user):
        """Test getting a database session."""
        db_manager.create_user_database("testuser", "testpassword123")

        session = db_manager.get_session("testuser")
        assert session is not None

        # get_session returns a new session each time in the current implementation
        session2 = db_manager.get_session("testuser")
        assert session2 is not None

    def test_check_database_integrity(self, db_manager, auth_user):
        """Test checking database integrity."""
        db_manager.create_user_database("testuser", "testpassword123")

        is_valid = db_manager.check_database_integrity("testuser")
        assert is_valid is True

        # Test with non-existent user
        is_valid = db_manager.check_database_integrity("nonexistent")
        assert is_valid is False

    @pytest.mark.skipif(
        os.environ.get("CI") == "true"
        or os.environ.get("GITHUB_ACTIONS") == "true",
        reason="Password change with encrypted DB re-keying is complex to test in CI",
    )
    def test_change_password(self, db_manager, auth_user):
        """Test changing database encryption password."""
        db_manager.create_user_database("testuser", "oldpassword")

        # Change password
        success = db_manager.change_password(
            "testuser", "oldpassword", "newpassword"
        )
        assert success is True

        # Try to open with new password
        engine = db_manager.open_user_database("testuser", "newpassword")
        assert engine is not None

        # Old password should fail
        db_manager.close_user_database("testuser")
        engine = db_manager.open_user_database("testuser", "oldpassword")
        assert engine is None

    @pytest.mark.skipif(
        os.environ.get("CI") == "true"
        or os.environ.get("GITHUB_ACTIONS") == "true",
        reason="Password change with encrypted DB re-keying is complex to test in CI",
    )
    def test_change_password_wrong_old(self, db_manager, auth_user):
        """Test changing password with wrong old password."""
        db_manager.create_user_database("testuser", "correctpassword")

        success = db_manager.change_password(
            "testuser", "wrongpassword", "newpassword"
        )
        assert success is False

    def test_user_exists(self, db_manager, auth_user):
        """Test checking if user exists."""
        # User exists in auth DB
        assert db_manager.user_exists("testuser") is True

        # User doesn't exist
        assert db_manager.user_exists("nonexistent") is False

    def test_memory_usage(self, db_manager, auth_user):
        """Test getting memory usage statistics."""
        stats = db_manager.get_memory_usage()
        assert stats["active_connections"] == 0
        assert stats["active_sessions"] == 0
        assert stats["estimated_memory_mb"] == 0

        # Create connections
        db_manager.create_user_database("testuser", "password1")
        db_manager.get_session("testuser")

        stats = db_manager.get_memory_usage()
        assert stats["active_connections"] == 1
        assert stats["active_sessions"] == 0  # Sessions are not tracked
        assert stats["estimated_memory_mb"] == 3.5

    def test_pragmas_applied(self, db_manager, auth_user):
        """Test that SQLCipher pragmas are correctly applied."""
        engine = db_manager.create_user_database("testuser", "testpassword123")

        with engine.connect() as conn:
            # Check journal mode
            result = conn.execute(text("PRAGMA journal_mode"))
            assert result.scalar() == "wal"

            # Check cipher settings
            result = conn.execute(text("PRAGMA kdf_iter"))
            # Default KDF iterations
            assert result.scalar() == "256000"

            result = conn.execute(text("PRAGMA cipher_page_size"))
            # Default page size is 16384 (16KB)
            assert result.scalar() == "16384"
