"""聊天服务（非流式 + 流式）.

编排 LLM Provider FailoverChain + Prompt 模板 + 用量记录。
"""

from collections.abc import AsyncGenerator
from dataclasses import dataclass
from typing import Any

import structlog

from ..errors import AILLMUnavailableError
from ..models.chat import ChatData, Usage
from ..prompt_service import PromptTemplateService
from ..providers import ProviderFailoverChain

logger = structlog.get_logger()


@dataclass
class ChatChunkData:
    """流式聊天 chunk."""

    content: str
    done: bool = False


class ChatService:
    """聊天服务."""

    def __init__(
        self,
        failover_chain: ProviderFailoverChain,
        prompt_service: PromptTemplateService | None = None,
        default_model: str = "gpt-4o-mini",
    ) -> None:
        self._chain = failover_chain
        self._prompts = prompt_service
        self._default_model = default_model

    async def chat(
        self,
        messages: list[dict[str, str]],
        model: str = "",
        temperature: float = 0.7,
        user_id: str = "",
        session_id: str | None = None,
        data_scope: str = "",
    ) -> ChatData:
        """非流式聊天.

        Returns:
            ChatData（含降级标记）
        """
        model = model or self._default_model
        try:
            response = await self._chain.chat(messages, model, temperature)
            return ChatData(
                content=response.content,
                model=response.model,
                usage=Usage(
                    prompt_tokens=response.usage.get("prompt_tokens", 0),
                    completion_tokens=response.usage.get("completion_tokens", 0),
                    total_tokens=response.usage.get("total_tokens", 0),
                ),
                degraded=False,
                degraded_reason="",
            )
        except AILLMUnavailableError as exc:
            logger.warning("chat_degraded", reason=str(exc))
            return ChatData(
                content=f"[degraded] LLM unavailable: {exc}",
                model=model,
                usage=Usage(),
                degraded=True,
                degraded_reason=str(exc),
            )

    async def stream_chat(
        self,
        messages: list[dict[str, str]],
        model: str = "",
        temperature: float = 0.7,
        user_id: str = "",
        session_id: str | None = None,
        data_scope: str = "",
    ) -> AsyncGenerator[ChatChunkData, None]:
        """流式聊天.

        Yields:
            ChatChunkData
        """
        model = model or self._default_model
        try:
            async for chunk in self._chain.stream_chat(messages, model, temperature):
                done = chunk.finish_reason == "stop" or chunk.finish_reason == "end_turn"
                yield ChatChunkData(content=chunk.delta, done=done)
        except AILLMUnavailableError as exc:
            logger.warning("stream_chat_degraded", reason=str(exc))
            yield ChatChunkData(content=f"[degraded] {exc}", done=True)
        except Exception as exc:  # noqa: BLE001
            logger.error("stream_chat_error", error=str(exc))
            yield ChatChunkData(content=f"[error] {exc}", done=True)

    def _build_system_prompt(self, context: dict[str, Any]) -> str:
        """渲染系统 prompt 模板."""
        if self._prompts is None:
            return "You are a helpful educational assistant."
        try:
            return self._prompts.render("chat_system", context)
        except Exception:  # noqa: BLE001
            return "You are a helpful educational assistant."
