"""Tests for settings and API key database models."""

from datetime import datetime, timezone

import pytest
from sqlalchemy import create_engine
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import sessionmaker

from local_deep_research.database.models import (
    APIKey,
    Base,
    Setting,
    SettingType,
    UserSettings,
)


class TestSettingsModels:
    """Test suite for settings-related models."""

    @pytest.fixture
    def engine(self):
        """Create an in-memory SQLite database for testing."""
        engine = create_engine("sqlite:///:memory:")
        Base.metadata.create_all(engine)
        return engine

    @pytest.fixture
    def session(self, engine):
        """Create a database session for testing."""
        Session = sessionmaker(bind=engine)
        session = Session()
        yield session
        session.close()

    def test_setting_creation(self, session):
        """Test creating various types of settings."""
        # App setting
        app_setting = Setting(
            key="app.theme",
            value={"mode": "dark", "accent": "blue"},
            type=SettingType.APP,
            name="Theme",
            category="appearance",
            description="Application theme settings",
            ui_element="select",
            options=["light", "dark", "auto"],
            visible=True,
            editable=True,
        )

        # LLM setting
        llm_setting = Setting(
            key="llm.temperature",
            value=0.7,
            type=SettingType.LLM,
            name="Temperature",
            category="model",
            description="LLM response temperature",
            ui_element="slider",
            min_value=0.0,
            max_value=2.0,
            step=0.1,
            visible=True,
            editable=True,
        )

        # Search setting
        search_setting = Setting(
            key="search.max_results",
            value=20,
            type=SettingType.SEARCH,
            name="Max Results",
            category="search",
            description="Maximum search results per query",
            ui_element="number",
            min_value=1,
            max_value=100,
            visible=True,
            editable=True,
        )

        # Report setting
        report_setting = Setting(
            key="report.format",
            value="markdown",
            type=SettingType.REPORT,
            name="Report Format",
            category="output",
            description="Default report format",
            ui_element="select",
            options=["markdown", "html", "pdf"],
            visible=True,
            editable=True,
        )

        session.add_all(
            [app_setting, llm_setting, search_setting, report_setting]
        )
        session.commit()

        # Verify settings
        all_settings = session.query(Setting).all()
        assert len(all_settings) == 4

        llm = session.query(Setting).filter_by(key="llm.temperature").first()
        assert llm.value == 0.7
        assert llm.type == SettingType.LLM
        assert llm.min_value == 0.0
        assert llm.max_value == 2.0

    def test_unique_key_constraint(self, session):
        """Test that setting keys must be unique."""
        setting1 = Setting(
            key="api.timeout",
            value=30,
            type=SettingType.APP,
            name="API Timeout",
            category="api",
        )

        setting2 = Setting(
            key="api.timeout",  # Duplicate key
            value=60,
            type=SettingType.APP,
            name="API Timeout",
            category="api",
        )

        session.add(setting1)
        session.commit()

        session.add(setting2)
        with pytest.raises(IntegrityError):
            session.commit()

    def test_secret_settings(self, session):
        """Test handling of secret settings (though Setting model doesn't have is_secret)."""
        # Using APIKey for secrets instead
        api_key = APIKey(
            provider="openai",
            key="sk-abc123...",  # Would be encrypted by SQLCipher
            description="OpenAI API key for GPT models",
            is_active=True,
        )

        session.add(api_key)
        session.commit()

        # Verify API key
        saved = session.query(APIKey).filter_by(provider="openai").first()
        assert saved is not None
        assert saved.is_active is True
        assert saved.key == "sk-abc123..."
        # In real usage, the key would be encrypted by SQLCipher

    def test_system_settings(self, session):
        """Test system-wide settings that shouldn't be user-editable."""
        system_setting = Setting(
            key="system.version",
            value="1.0.0",
            type=SettingType.APP,
            name="System Version",
            category="system",
            description="Current system version",
            visible=True,
            editable=False,  # System setting - not editable
        )

        session.add(system_setting)
        session.commit()

        saved = session.query(Setting).filter_by(key="system.version").first()
        assert saved.editable is False
        assert saved.visible is True

    def test_user_settings(self, session):
        """Test UserSettings for user-specific preferences."""
        # User preferences
        preferences = [
            UserSettings(
                key="preferred_model",
                value={"provider": "openai", "model": "gpt-4"},
                category="preferences",
                description="User's preferred LLM model",
            ),
            UserSettings(
                key="search_history_enabled",
                value=True,
                category="privacy",
                description="Whether to save search history",
            ),
            UserSettings(
                key="ui_language",
                value="en",
                category="localization",
                description="User interface language",
            ),
        ]

        session.add_all(preferences)
        session.commit()

        # Query by category
        privacy_settings = (
            session.query(UserSettings).filter_by(category="privacy").all()
        )
        assert len(privacy_settings) == 1
        assert privacy_settings[0].value is True

    def test_api_key_management(self, session):
        """Test APIKey model for secure API key storage."""
        # Add multiple API keys
        keys = [
            APIKey(
                provider="openai",
                key="sk-openai...",
                description="OpenAI GPT models",
                is_active=True,
            ),
            APIKey(
                provider="anthropic",
                key="sk-ant...",
                description="Claude models",
                is_active=True,
            ),
            APIKey(
                provider="google",
                key="AIza...",
                description="Google search API",
                is_active=False,  # Disabled
            ),
        ]

        session.add_all(keys)
        session.commit()

        # Query active keys
        active_keys = session.query(APIKey).filter_by(is_active=True).all()
        assert len(active_keys) == 2

        # Test unique provider constraint
        duplicate = APIKey(
            provider="openai",  # Already exists
            key="sk-different...",
            description="Duplicate provider",
        )

        session.add(duplicate)
        with pytest.raises(IntegrityError):
            session.commit()

    def test_setting_categories(self, session):
        """Test organizing settings by category."""
        categories = ["appearance", "performance", "security", "advanced"]

        for i, cat in enumerate(categories):
            for j in range(3):
                setting = Setting(
                    key=f"{cat}.setting_{j}",
                    value=f"value_{j}",
                    type=SettingType.APP,
                    name=f"{cat.title()} Setting {j}",
                    category=cat,
                    visible=True if i < 3 else False,  # Hide advanced
                )
                session.add(setting)

        session.commit()

        # Query by category
        appearance = (
            session.query(Setting).filter_by(category="appearance").all()
        )
        assert len(appearance) == 3

        # Query visible settings
        visible = session.query(Setting).filter_by(visible=True).count()
        assert visible == 9  # 3 categories * 3 settings each

    def test_setting_value_types(self, session):
        """Test different value types stored in JSON columns."""
        settings = [
            UserSettings(key="string_val", value="text value"),
            UserSettings(key="int_val", value=42),
            UserSettings(key="float_val", value=3.14),
            UserSettings(key="bool_val", value=True),
            UserSettings(key="list_val", value=[1, 2, 3]),
            UserSettings(key="dict_val", value={"a": 1, "b": 2}),
            UserSettings(key="null_val", value=None),
        ]

        session.add_all(settings)
        session.commit()

        # Verify different types
        assert (
            session.query(UserSettings)
            .filter_by(key="string_val")
            .first()
            .value
            == "text value"
        )
        assert (
            session.query(UserSettings).filter_by(key="int_val").first().value
            == 42
        )
        assert (
            session.query(UserSettings).filter_by(key="float_val").first().value
            == 3.14
        )
        assert (
            session.query(UserSettings).filter_by(key="bool_val").first().value
            is True
        )
        assert session.query(UserSettings).filter_by(
            key="list_val"
        ).first().value == [1, 2, 3]
        assert session.query(UserSettings).filter_by(
            key="dict_val"
        ).first().value == {"a": 1, "b": 2}
        assert (
            session.query(UserSettings).filter_by(key="null_val").first().value
            is None
        )

    def test_api_key_rotation(self, session):
        """Test API key rotation and usage tracking."""
        # Create initial key
        api_key = APIKey(
            provider="openai",
            key="sk-old...",
            description="OpenAI API key",
            is_active=True,
            usage_count=100,
        )

        session.add(api_key)
        session.commit()

        # Simulate key usage
        api_key.usage_count += 1
        api_key.last_used = datetime.now(timezone.utc)
        session.commit()

        # Rotate key (deactivate old, add new)
        api_key.is_active = False

        new_key = APIKey(
            provider="openai_new",  # Different provider name to avoid constraint
            key="sk-new...",
            description="OpenAI API key (rotated)",
            is_active=True,
        )

        session.add(new_key)
        session.commit()

        # Verify rotation
        old_key = session.query(APIKey).filter_by(provider="openai").first()
        assert old_key.is_active is False
        assert old_key.usage_count == 101

        active_key = (
            session.query(APIKey)
            .filter_by(is_active=True, provider="openai_new")
            .first()
        )
        assert active_key.key == "sk-new..."

    def test_user_settings_defaults(self, session):
        """Test default values for user settings."""
        # Get a non-existent setting
        setting = (
            session.query(UserSettings)
            .filter_by(key="non_existent_setting")
            .first()
        )

        assert setting is None

        # Create setting with defaults
        default_setting = UserSettings(
            key="new_feature_enabled",
            value=False,  # Default to disabled
            category="features",
            description="New experimental feature",
        )

        session.add(default_setting)
        session.commit()

        saved = (
            session.query(UserSettings)
            .filter_by(key="new_feature_enabled")
            .first()
        )
        assert saved.value is False
        assert saved.created_at is not None
        assert saved.updated_at is not None

    def test_setting_update_tracking(self, session):
        """Test that updated_at is properly tracked."""
        setting = Setting(
            key="test.update",
            value="initial",
            type=SettingType.APP,
            name="Test Update",
            category="test",
        )

        session.add(setting)
        session.commit()

        # Update the setting
        setting.value = "updated"
        session.commit()

        # Note: onupdate might not trigger in SQLite without proper configuration
        # In production with PostgreSQL, this would work automatically
