"""表达优化服务.

编排 LLM FailoverChain + Prompt 模板，优化文字表达的清晰度、简洁度和语气。
降级采用方案 B（总裁裁决 §2.6）：LLM 不可用时返回 degraded 响应。
"""

import json
from typing import Any

import structlog

from ..errors import AILLMUnavailableError
from ..models.expression import OptimizedExpressionData
from ..prompt_service import PromptTemplateService
from ..providers import ProviderFailoverChain

logger = structlog.get_logger()


class ExpressionService:
    """表达优化服务."""

    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 optimize(
        self,
        text: str,
        context: str = "",
        model: str = "",
        temperature: float = 0.5,
        user_id: str = "",
    ) -> OptimizedExpressionData:
        """优化文字表达.

        Args:
            text: 原始文字
            context: 上下文（可选）
            model: 模型名（空则用默认）
            temperature: 温度参数（表达优化用较低温度保持稳定）
            user_id: 用户 ID（用于用量记录）

        Returns:
            OptimizedExpressionData（含降级标记）
        """
        model = model or self._default_model
        prompt = self._render_prompt(text, context)
        messages = [
            {"role": "system", "content": "你是一个专业的文字编辑助手。"},
            {"role": "user", "content": prompt},
        ]

        try:
            response = await self._chain.chat(messages, model, temperature)
        except AILLMUnavailableError as exc:
            logger.warning("expression_optimize_degraded", reason=str(exc))
            return OptimizedExpressionData(
                optimized=text,
                suggestions=[],
                degraded=True,
                degraded_reason=str(exc),
            )

        # 解析 JSON 输出
        parsed = self._parse_json(response.content)
        if parsed is None:
            # JSON 解析失败，返回原始内容作为 optimized
            logger.warning(
                "expression_optimize_json_failed",
                output=response.content[:200],
            )
            return OptimizedExpressionData(
                optimized=response.content,
                suggestions=[],
                degraded=True,
                degraded_reason="llm output is not valid json",
            )

        return OptimizedExpressionData(
            optimized=str(parsed.get("optimized", text)),
            suggestions=list(parsed.get("suggestions", [])),
            degraded=False,
            degraded_reason="",
        )

    def _render_prompt(self, text: str, context: str) -> str:
        """渲染表达优化 prompt 模板."""
        if self._prompts is None:
            return self._fallback_prompt(text, context)
        try:
            return self._prompts.render(
                "optimize_expression",
                {"text": text, "context": context},
            )
        except Exception:  # noqa: BLE001
            return self._fallback_prompt(text, context)

    def _fallback_prompt(self, text: str, context: str) -> str:
        """模板不可用时的降级 prompt."""
        ctx = f"\n上下文：{context}" if context else ""
        return (
            f"请优化以下文字的表达，提升清晰度和简洁度：\n{text}{ctx}\n"
            "请按 JSON 格式输出："
            '{"optimized":"优化后文字","suggestions":["建议1","建议2"]}'
        )

    def _parse_json(self, output: str) -> dict[str, Any] | None:
        """解析 JSON，容忍 markdown 代码块包裹."""
        output = output.strip()
        if output.startswith("```"):
            lines = output.split("\n")[1:]
            if lines and lines[-1].strip() == "```":
                lines = lines[:-1]
            output = "\n".join(lines)
        try:
            parsed = json.loads(output)
        except json.JSONDecodeError:
            return None
        return parsed if isinstance(parsed, dict) else None
