"""Unit tests for runtime sentiment provider configuration."""

from __future__ import annotations

import pytest
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.exceptions import ProviderError, ValidationError
from app.models.settings import SystemSetting
from app.services import sentiment_provider_service as sps


@pytest.fixture
async def session() -> AsyncSession:
    from tests.conftest import _test_session_factory

    async with _test_session_factory() as session:
        yield session


@pytest.fixture(autouse=True)
def _clear_env_keys(monkeypatch):
    """Default: no env keys, so DB-only behavior is deterministic."""
    monkeypatch.setattr(sps.settings, "openai_api_key", "")
    monkeypatch.setattr(sps.settings, "gemini_api_key", "")


class TestGetConfig:
    async def test_defaults_no_key(self, session: AsyncSession):
        config = await sps.get_sentiment_config(session)
        assert config["provider"] == "openai"
        assert config["model"] == "gpt-4o-mini"
        assert config["api_key_configured"] is False
        assert config["api_key_source"] == "none"
        assert config["web_search"] is True
        assert set(config["valid_providers"]) == {"openai", "gemini", "deepseek", "xai", "openai_compatible"}

    async def test_never_returns_raw_key(self, session: AsyncSession):
        await sps.update_sentiment_config(session, api_key="sk-secret-123")
        config = await sps.get_sentiment_config(session)
        # No field should leak the key
        assert "sk-secret-123" not in str(config)
        assert config["api_key_configured"] is True
        assert config["api_key_source"] == "database"

    async def test_env_fallback_reported(self, session: AsyncSession, monkeypatch):
        monkeypatch.setattr(sps.settings, "openai_api_key", "sk-from-env")
        config = await sps.get_sentiment_config(session)
        assert config["api_key_configured"] is True
        assert config["api_key_source"] == "environment"


class TestUpdateConfig:
    async def test_update_provider_and_model(self, session: AsyncSession):
        result = await sps.update_sentiment_config(
            session, provider="gemini", model="gemini-2.0-flash"
        )
        assert result["provider"] == "gemini"
        assert result["model"] == "gemini-2.0-flash"

    async def test_empty_key_does_not_overwrite(self, session: AsyncSession):
        await sps.update_sentiment_config(session, api_key="sk-original")
        # Subsequent save without a key must keep the original
        await sps.update_sentiment_config(session, provider="openai", api_key="")
        result = await session.execute(
            select(SystemSetting).where(SystemSetting.key == sps.KEY_API_KEY)
        )
        assert result.scalar_one().value == "sk-original"

    async def test_rejects_invalid_provider(self, session: AsyncSession):
        with pytest.raises(ValidationError):
            await sps.update_sentiment_config(session, provider="anthropic")


class TestBuildProvider:
    async def test_raises_without_key(self, session: AsyncSession):
        with pytest.raises(ProviderError):
            await sps.build_sentiment_provider(session)

    async def test_builds_openai(self, session: AsyncSession):
        await sps.update_sentiment_config(session, provider="openai", api_key="sk-x")
        provider = await sps.build_sentiment_provider(session)
        assert type(provider).__name__ == "OpenAISentimentProvider"

    async def test_builds_gemini(self, session: AsyncSession):
        await sps.update_sentiment_config(session, provider="gemini", api_key="g-x")
        provider = await sps.build_sentiment_provider(session)
        assert type(provider).__name__ == "GeminiSentimentProvider"

    async def test_uses_env_key_when_db_empty(self, session: AsyncSession, monkeypatch):
        monkeypatch.setattr(sps.settings, "openai_api_key", "sk-env")
        await sps.update_sentiment_config(session, provider="openai")
        provider = await sps.build_sentiment_provider(session)
        assert type(provider).__name__ == "OpenAISentimentProvider"

    async def test_builds_deepseek_with_fixed_base_url(self, session: AsyncSession):
        await sps.update_sentiment_config(session, provider="deepseek", api_key="ds-x")
        provider = await sps.build_sentiment_provider(session)
        assert type(provider).__name__ == "OpenAICompatibleSentimentProvider"
        config = await sps.get_sentiment_config(session)
        assert config["base_url"] == "https://api.deepseek.com"
        assert config["web_search"] is False

    async def test_builds_xai_via_responses_web_search(self, session: AsyncSession):
        await sps.update_sentiment_config(session, provider="xai", api_key="xai-x")
        provider = await sps.build_sentiment_provider(session)
        # xAI grounds via the Responses API web_search tool
        assert type(provider).__name__ == "OpenAISentimentProvider"
        assert provider._tool_type == "web_search"
        assert provider._source == "xai"
        config = await sps.get_sentiment_config(session)
        assert config["base_url"] == "https://api.x.ai/v1"
        assert config["web_search"] is True

    async def test_openai_compatible_requires_base_url(self, session: AsyncSession):
        await sps.update_sentiment_config(session, provider="openai_compatible", api_key="x")
        with pytest.raises(ProviderError):
            await sps.build_sentiment_provider(session)

    async def test_openai_compatible_with_base_url(self, session: AsyncSession):
        await sps.update_sentiment_config(
            session,
            provider="openai_compatible",
            api_key="x",
            model="llama-3.1-70b",
            base_url="https://openrouter.ai/api/v1",
        )
        provider = await sps.build_sentiment_provider(session)
        assert type(provider).__name__ == "OpenAICompatibleSentimentProvider"
