"""OutputStandardizer 单元测试"""

from datetime import datetime, timezone

import pytest

from agentkit.quality.gate import QualityCheck, QualityResult
from agentkit.quality.output import OutputMetadata, OutputStandardizer, StandardOutput
from agentkit.skills.base import Skill, SkillConfig


# ── 辅助函数 ───────────────────────────────────────────────


def _make_skill(
    name: str = "test_skill",
    output_schema: dict | None = None,
) -> Skill:
    """创建测试用 Skill 实例"""
    config = SkillConfig.from_dict({
        "name": name,
        "agent_type": "test",
        "task_mode": "llm_generate",
        "prompt": {"identity": "测试技能"},
        "output_schema": output_schema,
    })
    return Skill(config)


def _make_quality_result(passed: bool, check_count: int = 1) -> QualityResult:
    """创建测试用 QualityResult"""
    checks = [
        QualityCheck(name=f"check_{i}", passed=passed)
        for i in range(check_count)
    ]
    return QualityResult(passed=passed, checks=checks, can_retry=False)


def _make_mixed_quality_result(passed_count: int, failed_count: int) -> QualityResult:
    """创建混合通过/失败的 QualityResult"""
    checks = [
        QualityCheck(name=f"pass_{i}", passed=True)
        for i in range(passed_count)
    ] + [
        QualityCheck(name=f"fail_{i}", passed=False, message=f"fail {i}")
        for i in range(failed_count)
    ]
    total_passed = failed_count == 0
    return QualityResult(passed=total_passed, checks=checks, can_retry=False)


# ── OutputMetadata 测试 ────────────────────────────────────


class TestOutputMetadata:
    """OutputMetadata 数据类测试"""

    def test_fields(self):
        now = datetime.now(timezone.utc)
        meta = OutputMetadata(version="1.0.0", produced_at=now, quality_score=0.8)
        assert meta.version == "1.0.0"
        assert meta.produced_at == now
        assert meta.quality_score == 0.8


# ── StandardOutput 测试 ────────────────────────────────────


class TestStandardOutput:
    """StandardOutput 数据类测试"""

    def test_fields(self):
        meta = OutputMetadata(
            version="1.0.0",
            produced_at=datetime.now(timezone.utc),
            quality_score=1.0,
        )
        output = StandardOutput(skill_name="my_skill", data={"key": "value"}, metadata=meta)
        assert output.skill_name == "my_skill"
        assert output.data == {"key": "value"}
        assert output.metadata is meta


# ── OutputStandardizer.standardize 测试 ─────────────────────


class TestOutputStandardizer:
    """OutputStandardizer 标准化输出测试"""

    @pytest.fixture
    def standardizer(self) -> OutputStandardizer:
        return OutputStandardizer()

    async def test_standardized_output_contains_skill_name_and_metadata(
        self, standardizer: OutputStandardizer
    ):
        """标准化输出包含 skill_name 和 metadata"""
        skill = _make_skill(name="content_gen")
        raw = {"title": "Hello", "content": "World"}
        result = await standardizer.standardize(raw, skill)
        assert isinstance(result, StandardOutput)
        assert result.skill_name == "content_gen"
        assert isinstance(result.metadata, OutputMetadata)

    async def test_metadata_contains_version_and_produced_at(
        self, standardizer: OutputStandardizer
    ):
        """metadata 包含 version 和 produced_at"""
        skill = _make_skill()
        raw = {"data": "test"}
        result = await standardizer.standardize(raw, skill)
        assert result.metadata.version == skill.config.version
        assert isinstance(result.metadata.produced_at, datetime)
        assert result.metadata.produced_at.tzinfo is not None

    async def test_produced_at_uses_utc_timezone(self, standardizer: OutputStandardizer):
        """produced_at 使用 UTC 时区"""
        skill = _make_skill()
        raw = {"data": "test"}
        result = await standardizer.standardize(raw, skill)
        assert result.metadata.produced_at.tzinfo == timezone.utc

    async def test_field_type_normalization_string_to_integer(
        self, standardizer: OutputStandardizer
    ):
        """字段类型归一化：字符串 → 整数"""
        schema = {
            "type": "object",
            "properties": {
                "count": {"type": "integer"},
            },
        }
        skill = _make_skill(output_schema=schema)
        raw = {"count": "42"}
        result = await standardizer.standardize(raw, skill)
        assert result.data["count"] == 42
        assert isinstance(result.data["count"], int)

    async def test_field_type_normalization_string_to_number(
        self, standardizer: OutputStandardizer
    ):
        """字段类型归一化：字符串 → 浮点数"""
        schema = {
            "type": "object",
            "properties": {
                "score": {"type": "number"},
            },
        }
        skill = _make_skill(output_schema=schema)
        raw = {"score": "3.14"}
        result = await standardizer.standardize(raw, skill)
        assert result.data["score"] == 3.14
        assert isinstance(result.data["score"], float)

    async def test_field_type_normalization_string_to_boolean(
        self, standardizer: OutputStandardizer
    ):
        """字段类型归一化：字符串 → 布尔值"""
        schema = {
            "type": "object",
            "properties": {
                "active": {"type": "boolean"},
            },
        }
        skill = _make_skill(output_schema=schema)
        raw = {"active": "true"}
        result = await standardizer.standardize(raw, skill)
        assert result.data["active"] is True

    async def test_empty_output_schema_no_schema_validation(
        self, standardizer: OutputStandardizer
    ):
        """无 output_schema → 不做 schema 验证"""
        skill = _make_skill(output_schema=None)
        raw = {"anything": "goes", "number": 42}
        result = await standardizer.standardize(raw, skill)
        assert result.data == raw

    async def test_quality_score_calculated_from_quality_result(
        self, standardizer: OutputStandardizer
    ):
        """quality_score 从 QualityResult 正确计算"""
        skill = _make_skill()
        raw = {"data": "test"}
        quality_result = _make_mixed_quality_result(passed_count=3, failed_count=1)
        result = await standardizer.standardize(raw, skill, quality_result)
        # 3 passed + 1 failed = 4 total, score = 3/4 = 0.75
        assert result.metadata.quality_score == 0.75

    async def test_quality_score_is_one_when_no_quality_result(
        self, standardizer: OutputStandardizer
    ):
        """无 quality_result → quality_score = 1.0"""
        skill = _make_skill()
        raw = {"data": "test"}
        result = await standardizer.standardize(raw, skill)
        assert result.metadata.quality_score == 1.0

    async def test_quality_score_all_passed(self, standardizer: OutputStandardizer):
        """所有检查通过 → quality_score = 1.0"""
        skill = _make_skill()
        raw = {"data": "test"}
        quality_result = _make_quality_result(passed=True, check_count=5)
        result = await standardizer.standardize(raw, skill, quality_result)
        assert result.metadata.quality_score == 1.0

    async def test_quality_score_all_failed(self, standardizer: OutputStandardizer):
        """所有检查失败 → quality_score = 0.0"""
        skill = _make_skill()
        raw = {"data": "test"}
        quality_result = _make_quality_result(passed=False, check_count=3)
        result = await standardizer.standardize(raw, skill, quality_result)
        assert result.metadata.quality_score == 0.0

    async def test_standard_output_data_matches_raw_when_no_normalization(
        self, standardizer: OutputStandardizer
    ):
        """无归一化需求时，StandardOutput.data 与 raw_output 一致"""
        skill = _make_skill()
        raw = {"title": "Hello", "count": 42, "active": True}
        result = await standardizer.standardize(raw, skill)
        assert result.data == raw

    async def test_type_normalization_invalid_value_kept_as_is(
        self, standardizer: OutputStandardizer
    ):
        """类型归一化失败时保留原值"""
        schema = {
            "type": "object",
            "properties": {
                "count": {"type": "integer"},
            },
        }
        skill = _make_skill(output_schema=schema)
        raw = {"count": "not_a_number"}
        result = await standardizer.standardize(raw, skill)
        # 无法转换，保留原值
        assert result.data["count"] == "not_a_number"

    async def test_quality_score_with_empty_checks(self, standardizer: OutputStandardizer):
        """空 checks 列表 → quality_score = 1.0"""
        skill = _make_skill()
        raw = {"data": "test"}
        quality_result = QualityResult(passed=True, checks=[], can_retry=False)
        result = await standardizer.standardize(raw, skill, quality_result)
        assert result.metadata.quality_score == 1.0
