"""Prompt 模板服务（Jinja2 渲染 + YAML 模板加载）.

设计依据 02-architecture-design.md §8：
  - 模板文件位于 prompts/*.yaml
  - 每个 YAML 含 name/version/description/template 四字段
  - template 字段为 Jinja2 模板字符串
  - 渲染时传入上下文变量
"""

from pathlib import Path
from typing import Any

import structlog
import yaml
from jinja2 import Environment, StrictUndefined, Template, select_autoescape

from .errors import AIError, ErrorCode

logger = structlog.get_logger()

# 模板目录（相对于 src/ai/）
TEMPLATES_DIR = Path(__file__).parent / "prompts"


class PromptTemplate:
    """单个 prompt 模板."""

    def __init__(self, name: str, version: str, description: str, template_str: str) -> None:
        self.name = name
        self.version = version
        self.description = description
        self._template: Template = Environment(
            autoescape=select_autoescape([]),
            variable_start_string="{{",
            variable_end_string="}}",
            undefined=StrictUndefined,
        ).from_string(template_str)

    def render(self, context: dict[str, Any] | None = None) -> str:
        """渲染模板.

        Args:
            context: 模板变量字典

        Returns:
            渲染后的 prompt 字符串

        Raises:
            AIError(AI_PROMPT_RENDER_FAILED): 渲染失败（变量缺失等）
        """
        try:
            return self._template.render(context or {})
        except Exception as exc:
            logger.error(
                "prompt_render_failed",
                template=self.name,
                error=str(exc),
            )
            raise AIError(
                ErrorCode.AI_PROMPT_RENDER_FAILED,
                f"Failed to render template '{self.name}': {exc}",
            ) from exc


class PromptTemplateService:
    """Prompt 模板服务.

    启动时加载所有 YAML 模板到内存，运行时按 name 查找渲染。
    """

    def __init__(self, templates_dir: Path | None = None) -> None:
        self._templates_dir = templates_dir or TEMPLATES_DIR
        self._templates: dict[str, PromptTemplate] = {}

    def load(self) -> None:
        """加载所有模板文件."""
        if not self._templates_dir.exists():
            logger.warning(
                "prompts_dir_not_found",
                path=str(self._templates_dir),
            )
            return

        for yaml_file in self._templates_dir.glob("*.yaml"):
            try:
                self._load_file(yaml_file)
            except Exception as exc:  # noqa: BLE001
                logger.error(
                    "prompt_template_load_failed",
                    file=str(yaml_file),
                    error=str(exc),
                )

        logger.info("prompt_templates_loaded", count=len(self._templates))

    def _load_file(self, path: Path) -> None:
        """加载单个 YAML 模板文件."""
        with path.open(encoding="utf-8") as f:
            data = yaml.safe_load(f)

        name = data.get("name") or path.stem
        version = str(data.get("version", "1.0"))
        description = data.get("description", "")
        template_str = data.get("template", "")

        if not template_str:
            raise ValueError(f"Template '{name}' has empty template field")

        self._templates[name] = PromptTemplate(name, version, description, template_str)

    def get(self, name: str) -> PromptTemplate:
        """获取模板.

        Raises:
            AIError(AI_PROMPT_TEMPLATE_NOT_FOUND): 模板不存在
        """
        template = self._templates.get(name)
        if template is None:
            raise AIError(
                ErrorCode.AI_PROMPT_TEMPLATE_NOT_FOUND,
                f"Template '{name}' not found",
                {"available": list(self._templates.keys())},
            )
        return template

    def render(self, name: str, context: dict[str, Any] | None = None) -> str:
        """渲染指定模板.

        Args:
            name: 模板名称
            context: 模板变量

        Returns:
            渲染后的 prompt 字符串
        """
        template = self.get(name)
        return template.render(context)

    def list_templates(self) -> list[dict[str, str]]:
        """列出所有已加载模板."""
        return [
            {
                "name": t.name,
                "version": t.version,
                "description": t.description,
            }
            for t in self._templates.values()
        ]
