"""服务层测试（ChatService / QuestionService / ExpressionService）."""

import json

from src.ai.models.question import GenerateQuestionRequest
from src.ai.providers import ProviderFailoverChain
from src.ai.providers.circuit_breaker import CircuitBreaker
from src.ai.services.chat_service import ChatService
from src.ai.services.evaluation import QualityGate, RuleValidator
from src.ai.services.expression_service import ExpressionService
from src.ai.services.question_service import QuestionService

from .conftest import MockProvider


def _make_chain(provider: MockProvider) -> ProviderFailoverChain:
    return ProviderFailoverChain([provider], CircuitBreaker())


class TestChatService:
    """ChatService 测试."""

    async def test_chat_success(self) -> None:
        """聊天成功."""
        chain = _make_chain(MockProvider(response_content="你好"))
        svc = ChatService(failover_chain=chain, default_model="gpt-4o")
        data = await svc.chat(messages=[{"role": "user", "content": "hi"}])
        assert data.content == "你好"
        assert data.degraded is False
        assert data.usage.total_tokens == 30

    async def test_chat_degraded(self) -> None:
        """LLM 不可用时降级."""
        chain = _make_chain(MockProvider(fail=True))
        svc = ChatService(failover_chain=chain)
        data = await svc.chat(messages=[{"role": "user", "content": "hi"}])
        assert data.degraded is True
        assert "degraded" in data.content

    async def test_stream_chat(self) -> None:
        """流式聊天."""
        chain = _make_chain(MockProvider(stream_chunks=["hello", " world"]))
        svc = ChatService(failover_chain=chain)
        chunks = []
        async for chunk in svc.stream_chat(messages=[{"role": "user", "content": "hi"}]):
            chunks.append(chunk)
        assert len(chunks) == 2
        assert chunks[-1].done is True

    async def test_stream_chat_degraded(self) -> None:
        """流式降级."""
        chain = _make_chain(MockProvider(fail=True))
        svc = ChatService(failover_chain=chain)
        chunks = []
        async for chunk in svc.stream_chat(messages=[{"role": "user", "content": "hi"}]):
            chunks.append(chunk)
        assert chunks[-1].done is True
        assert "degraded" in chunks[-1].content

    def test_build_system_prompt_no_template(self) -> None:
        """无模板服务时返回默认 prompt."""
        chain = _make_chain(MockProvider())
        svc = ChatService(failover_chain=chain)
        prompt = svc._build_system_prompt({})
        assert "educational assistant" in prompt.lower()


class TestQuestionService:
    """QuestionService 测试."""

    async def test_generate_success(self) -> None:
        """生成题目成功."""
        output = json.dumps({
            "question": "1+1等于几？",
            "answer": "2",
            "explanation": "基础加法",
            "difficulty": "easy",
            "question_type": "short_answer",
        })
        chain = _make_chain(MockProvider(response_content=output))
        gate = QualityGate(rule_validator=RuleValidator())
        svc = QuestionService(
            failover_chain=chain,
            quality_gate=gate,
            default_model="gpt-4o",
        )
        request = GenerateQuestionRequest(
            prompt="生成加法题",
            subject="数学",
            difficulty="easy",
        )
        data = await svc.generate(request)
        assert data.question == "1+1等于几？"
        assert data.answer == "2"
        assert data.degraded is False

    async def test_generate_degraded_llm_fail(self) -> None:
        """LLM 失败降级."""
        chain = _make_chain(MockProvider(fail=True))
        svc = QuestionService(failover_chain=chain)
        request = GenerateQuestionRequest(prompt="生成题目", subject="数学")
        data = await svc.generate(request)
        assert data.degraded is True
        assert "degraded" in data.explanation

    async def test_generate_invalid_json(self) -> None:
        """LLM 输出非 JSON 降级."""
        chain = _make_chain(MockProvider(response_content="这不是JSON"))
        svc = QuestionService(failover_chain=chain)
        request = GenerateQuestionRequest(prompt="生成题目", subject="数学")
        data = await svc.generate(request)
        # 非JSON → 规则校验失败 → degraded
        assert data.degraded is True

    async def test_stream_generate(self) -> None:
        """流式生成题目."""
        output = json.dumps({"question": "题", "answer": "答"})
        chain = _make_chain(MockProvider(stream_chunks=[output]))
        svc = QuestionService(failover_chain=chain)
        request = GenerateQuestionRequest(prompt="生成题目", subject="数学")
        chunks = []
        async for chunk in svc.stream_generate(request):
            chunks.append(chunk)
        assert chunks[-1].done is True
        assert chunks[-1].complete_question is not None

    async def test_stream_generate_degraded(self) -> None:
        """流式生成降级."""
        chain = _make_chain(MockProvider(fail=True))
        svc = QuestionService(failover_chain=chain)
        request = GenerateQuestionRequest(prompt="生成题目", subject="数学")
        chunks = []
        async for chunk in svc.stream_generate(request):
            chunks.append(chunk)
        assert chunks[-1].done is True
        assert chunks[-1].complete_question is not None
        assert chunks[-1].complete_question.degraded is True

    def test_fallback_prompt(self) -> None:
        """降级 prompt."""
        chain = _make_chain(MockProvider())
        svc = QuestionService(failover_chain=chain)
        request = GenerateQuestionRequest(prompt="测试", subject="语文")
        prompt = svc._fallback_prompt(request)
        assert "语文" in prompt


class TestExpressionService:
    """ExpressionService 测试."""

    async def test_optimize_success(self) -> None:
        """优化成功."""
        output = json.dumps({
            "optimized": "优化后的文字",
            "suggestions": ["建议1"],
        })
        chain = _make_chain(MockProvider(response_content=output))
        svc = ExpressionService(failover_chain=chain)
        data = await svc.optimize(text="原始文字")
        assert data.optimized == "优化后的文字"
        assert data.suggestions == ["建议1"]
        assert data.degraded is False

    async def test_optimize_degraded(self) -> None:
        """LLM 失败降级."""
        chain = _make_chain(MockProvider(fail=True))
        svc = ExpressionService(failover_chain=chain)
        data = await svc.optimize(text="原始文字")
        assert data.degraded is True
        assert data.optimized == "原始文字"

    async def test_optimize_invalid_json(self) -> None:
        """非 JSON 输出降级."""
        chain = _make_chain(MockProvider(response_content="纯文本"))
        svc = ExpressionService(failover_chain=chain)
        data = await svc.optimize(text="原始文字")
        assert data.degraded is True
        assert "json" in data.degraded_reason.lower()

    async def test_optimize_markdown_json(self) -> None:
        """markdown 包裹 JSON 能解析."""
        output = '```json\n{"optimized": "ok", "suggestions": []}\n```'
        chain = _make_chain(MockProvider(response_content=output))
        svc = ExpressionService(failover_chain=chain)
        data = await svc.optimize(text="原始文字")
        assert data.optimized == "ok"

    def test_fallback_prompt(self) -> None:
        """降级 prompt."""
        chain = _make_chain(MockProvider())
        svc = ExpressionService(failover_chain=chain)
        prompt = svc._fallback_prompt("文字", "上下文")
        assert "文字" in prompt
        assert "上下文" in prompt
