"""第一道防线：规则校验器.

检查 LLM 输出是否符合基本规则：
  1. JSON 格式有效
  2. 必填字段存在（question/answer）
  3. 难度匹配请求
  4. 题型在允许列表内
  5. 无明显格式问题（空内容、过长内容等）
"""

import json
from dataclasses import dataclass, field

import structlog

logger = structlog.get_logger()

# 允许的难度和题型
VALID_DIFFICULTIES = {"easy", "medium", "hard"}
VALID_QUESTION_TYPES = {
    "single_choice",
    "multi_choice",
    "fill_blank",
    "short_answer",
    "essay",
}

# 内容长度限制
MIN_QUESTION_LENGTH = 5
MAX_QUESTION_LENGTH = 2000
MIN_ANSWER_LENGTH = 1
MAX_ANSWER_LENGTH = 5000


@dataclass
class ValidationResult:
    """规则校验结果."""

    passed: bool
    errors: list[str] = field(default_factory=list)
    warnings: list[str] = field(default_factory=list)
    parsed: dict | None = None  # 解析后的 JSON dict

    @property
    def score(self) -> float:
        """规则校验得分（0.0-1.0）."""
        if not self.passed:
            return 0.0
        # 有警告但通过，得分 0.8
        if self.warnings:
            return 0.8
        return 1.0


class RuleValidator:
    """规则校验器."""

    def validate(
        self,
        llm_output: str,
        expected_difficulty: str = "",
        expected_question_type: str = "",
    ) -> ValidationResult:
        """校验 LLM 输出.

        Args:
            llm_output: LLM 原始输出字符串
            expected_difficulty: 期望的难度
            expected_question_type: 期望的题型

        Returns:
            ValidationResult
        """
        result = ValidationResult(passed=True)

        # 1. 解析 JSON
        parsed = self._parse_json(llm_output)
        if parsed is None:
            result.passed = False
            result.errors.append("输出不是有效的 JSON 格式")
            return result
        result.parsed = parsed

        # 2. 检查必填字段
        self._check_required_fields(parsed, result)
        if not result.passed:
            return result

        # 3. 检查内容长度
        self._check_content_length(parsed, result)

        # 4. 检查难度匹配
        if expected_difficulty:
            self._check_difficulty(parsed, expected_difficulty, result)

        # 5. 检查题型
        if expected_question_type:
            self._check_question_type(parsed, expected_question_type, result)

        return result

    def _parse_json(self, output: str) -> dict | None:
        """解析 JSON，容忍 markdown 代码块包裹."""
        output = output.strip()
        # 去除 markdown 代码块标记
        if output.startswith("```"):
            lines = output.split("\n")
            # 去除首行（```json 或 ```）
            lines = lines[1:]
            # 去除末尾 ```
            if lines and lines[-1].strip() == "```":
                lines = lines[:-1]
            output = "\n".join(lines)
        try:
            parsed = json.loads(output)
        except json.JSONDecodeError as exc:
            logger.debug("json_parse_failed", error=str(exc), output=output[:200])
            return None
        if not isinstance(parsed, dict):
            logger.debug("json_not_dict", type=type(parsed).__name__)
            return None
        return parsed

    def _check_required_fields(self, parsed: dict, result: ValidationResult) -> None:
        """检查必填字段."""
        question = parsed.get("question", "")
        answer = parsed.get("answer", "")
        if not question:
            result.passed = False
            result.errors.append("缺少必填字段: question")
        if not answer:
            result.passed = False
            result.errors.append("缺少必填字段: answer")

    def _check_content_length(self, parsed: dict, result: ValidationResult) -> None:
        """检查内容长度."""
        question = str(parsed.get("question", ""))
        answer = str(parsed.get("answer", ""))

        if len(question) < MIN_QUESTION_LENGTH:
            result.warnings.append(f"question 过短（{len(question)} 字符）")
        if len(question) > MAX_QUESTION_LENGTH:
            result.warnings.append(f"question 过长（{len(question)} 字符）")
        if len(answer) < MIN_ANSWER_LENGTH:
            result.warnings.append(f"answer 过短（{len(answer)} 字符）")
        if len(answer) > MAX_ANSWER_LENGTH:
            result.warnings.append(f"answer 过长（{len(answer)} 字符）")

    def _check_difficulty(self, parsed: dict, expected: str, result: ValidationResult) -> None:
        """检查难度匹配."""
        actual = str(parsed.get("difficulty", "")).lower()
        if actual not in VALID_DIFFICULTIES:
            result.warnings.append(f"difficulty 值无效: {actual}")
        elif actual != expected.lower():
            result.warnings.append(
                f"difficulty 不匹配: 期望 {expected}, 实际 {actual}",
            )

    def _check_question_type(self, parsed: dict, expected: str, result: ValidationResult) -> None:
        """检查题型."""
        actual = str(parsed.get("question_type", "")).lower()
        if actual and actual not in VALID_QUESTION_TYPES:
            result.warnings.append(f"question_type 值无效: {actual}")
        elif actual and actual != expected.lower():
            result.warnings.append(
                f"question_type 不匹配: 期望 {expected}, 实际 {actual}",
            )
