"""U11+U12 测试: Evolution 生命周期集成 + EvolutionConfig

覆盖：
- EvolutionConfig 默认值与自定义值
- SkillConfig 的 evolution 字段
- ConfigDrivenAgent 集成 EvolutionMixin
- 生命周期钩子触发进化
- 进化失败不影响主任务流程
"""

from datetime import datetime, timezone
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from agentkit.core.protocol import TaskMessage, TaskResult, TaskStatus


# ── Helpers ──────────────────────────────────────────────


def _make_task(**overrides) -> TaskMessage:
    defaults = dict(
        task_id="test-task-001",
        agent_name="test_agent",
        task_type="generate",
        priority=1,
        input_data={"query": "hello"},
        callback_url=None,
        created_at=datetime.now(timezone.utc),
    )
    defaults.update(overrides)
    return TaskMessage(**defaults)


def _make_task_result(**overrides) -> TaskResult:
    defaults = dict(
        task_id="test-task-001",
        agent_name="test_agent",
        status=TaskStatus.COMPLETED,
        output_data={"result": "ok"},
        error_message=None,
        started_at=datetime.now(timezone.utc),
        completed_at=datetime.now(timezone.utc),
    )
    defaults.update(overrides)
    return TaskResult(**defaults)


# ── EvolutionConfig 测试 ──────────────────────────────────


class TestEvolutionConfig:
    """U12: EvolutionConfig 数据类测试"""

    def test_default_values(self):
        """默认 EvolutionConfig — enabled=False"""
        from agentkit.skills.base import EvolutionConfig

        config = EvolutionConfig()
        assert config.enabled is False
        assert config.reflect_on_failure is True
        assert config.auto_apply is False
        assert config.min_quality_threshold == 0.5

    def test_from_dict_all_fields(self):
        """EvolutionConfig 从字典创建 — 所有字段设置"""
        from agentkit.skills.base import EvolutionConfig

        config = EvolutionConfig(
            enabled=True,
            reflect_on_failure=False,
            auto_apply=True,
            min_quality_threshold=0.8,
        )
        assert config.enabled is True
        assert config.reflect_on_failure is False
        assert config.auto_apply is True
        assert config.min_quality_threshold == 0.8

    def test_from_dict_partial(self):
        """EvolutionConfig 部分字段 — 缺失字段使用默认值"""
        from agentkit.skills.base import EvolutionConfig

        config = EvolutionConfig(enabled=True)
        assert config.enabled is True
        assert config.reflect_on_failure is True  # default
        assert config.auto_apply is False  # default
        assert config.min_quality_threshold == 0.5  # default


# ── SkillConfig evolution 字段测试 ─────────────────────────


class TestSkillConfigEvolution:
    """U12: SkillConfig 的 evolution 字段"""

    def test_skill_config_without_evolution(self):
        """SkillConfig 无 evolution — 默认 enabled=False"""
        from agentkit.skills.base import SkillConfig

        config = SkillConfig(
            name="test_agent",
            agent_type="test",
            task_mode="llm_generate",
            prompt={"identity": "test", "instructions": "test"},
        )
        assert config.evolution.enabled is False

    def test_skill_config_with_evolution(self):
        """SkillConfig 有 evolution 配置 — 正确解析"""
        from agentkit.skills.base import SkillConfig

        config = SkillConfig(
            name="test_agent",
            agent_type="test",
            task_mode="llm_generate",
            prompt={"identity": "test", "instructions": "test"},
            evolution={"enabled": True, "auto_apply": True, "min_quality_threshold": 0.7},
        )
        assert config.evolution.enabled is True
        assert config.evolution.auto_apply is True
        assert config.evolution.min_quality_threshold == 0.7

    def test_skill_config_to_dict_includes_evolution(self):
        """SkillConfig.to_dict 包含 evolution 字段"""
        from agentkit.skills.base import SkillConfig

        config = SkillConfig(
            name="test_agent",
            agent_type="test",
            task_mode="llm_generate",
            prompt={"identity": "test", "instructions": "test"},
            evolution={"enabled": True},
        )
        d = config.to_dict()
        assert "evolution" in d
        assert d["evolution"]["enabled"] is True
        assert d["evolution"]["reflect_on_failure"] is True
        assert d["evolution"]["auto_apply"] is False
        assert d["evolution"]["min_quality_threshold"] == 0.5

    def test_skill_config_from_dict_with_evolution(self):
        """SkillConfig.from_dict 正确解析 evolution"""
        from agentkit.skills.base import SkillConfig

        data = {
            "name": "test_agent",
            "agent_type": "test",
            "task_mode": "llm_generate",
            "prompt": {"identity": "test", "instructions": "test"},
            "evolution": {"enabled": True, "reflect_on_failure": False},
        }
        config = SkillConfig.from_dict(data)
        assert config.evolution.enabled is True
        assert config.evolution.reflect_on_failure is False


# ── ConfigDrivenAgent evolution 集成测试 ──────────────────


class TestConfigDrivenAgentEvolution:
    """U11: ConfigDrivenAgent 集成 EvolutionMixin"""

    def _make_agent_config(self, evolution=None):
        from agentkit.core.config_driven import AgentConfig

        config = AgentConfig(
            name="test_agent",
            agent_type="test",
            task_mode="llm_generate",
            prompt={"identity": "test", "instructions": "test"},
        )
        if evolution is not None:
            config.evolution = evolution
        return config

    def _make_skill_config(self, evolution=None):
        from agentkit.skills.base import SkillConfig

        return SkillConfig(
            name="test_agent",
            agent_type="test",
            task_mode="llm_generate",
            prompt={"identity": "test", "instructions": "test"},
            evolution=evolution,
        )

    def test_agent_without_evolution_config(self):
        """Agent 无 evolution 配置 — _evolution_enabled=False"""
        from agentkit.core.config_driven import ConfigDrivenAgent

        config = self._make_agent_config()
        agent = ConfigDrivenAgent(config=config)
        assert agent._evolution_enabled is False

    def test_agent_with_evolution_enabled(self):
        """Agent 有 evolution 且 enabled=True — _evolution_enabled=True"""
        from agentkit.core.config_driven import ConfigDrivenAgent

        config = self._make_agent_config(evolution={"enabled": True})
        agent = ConfigDrivenAgent(config=config)
        assert agent._evolution_enabled is True

    def test_agent_with_evolution_disabled(self):
        """Agent 有 evolution 但 enabled=False — _evolution_enabled=False"""
        from agentkit.core.config_driven import ConfigDrivenAgent

        config = self._make_agent_config(evolution={"enabled": False})
        agent = ConfigDrivenAgent(config=config)
        assert agent._evolution_enabled is False

    async def test_on_task_complete_evolution_disabled(self):
        """on_task_complete 进化禁用 — 不调用 evolve_after_task"""
        from agentkit.core.config_driven import ConfigDrivenAgent

        config = self._make_agent_config()
        agent = ConfigDrivenAgent(config=config)

        task = _make_task()
        output = {"result": "ok"}

        # Should not raise and should not call evolve_after_task
        await agent.on_task_complete(task, output)

    async def test_on_task_complete_evolution_enabled(self):
        """on_task_complete 进化启用 — 调用 evolve_after_task"""
        from agentkit.core.config_driven import ConfigDrivenAgent

        config = self._make_agent_config(evolution={"enabled": True})
        agent = ConfigDrivenAgent(config=config)

        task = _make_task()
        output = {"result": "ok"}

        with patch.object(agent, "evolve_after_task", new_callable=AsyncMock) as mock_evolve:
            await agent.on_task_complete(task, output)
            mock_evolve.assert_called_once()
            # Verify the TaskResult passed to evolve_after_task
            call_args = mock_evolve.call_args
            result_arg = call_args[0][1]  # second positional arg is TaskResult
            assert result_arg.status == TaskStatus.COMPLETED
            assert result_arg.output_data == output

    async def test_on_task_failed_evolution_enabled(self):
        """on_task_failed 进化启用 — 调用 evolve_after_task"""
        from agentkit.core.config_driven import ConfigDrivenAgent

        config = self._make_agent_config(evolution={"enabled": True})
        agent = ConfigDrivenAgent(config=config)

        task = _make_task()
        error = ValueError("test error")

        with patch.object(agent, "evolve_after_task", new_callable=AsyncMock) as mock_evolve:
            await agent.on_task_failed(task, error)
            mock_evolve.assert_called_once()
            # Verify the TaskResult passed to evolve_after_task
            call_args = mock_evolve.call_args
            result_arg = call_args[0][1]  # second positional arg is TaskResult
            assert result_arg.status == TaskStatus.FAILED
            assert result_arg.error_message == "test error"

    async def test_evolution_failure_does_not_break_task(self):
        """进化失败不影响任务完成"""
        from agentkit.core.config_driven import ConfigDrivenAgent

        config = self._make_agent_config(evolution={"enabled": True})
        agent = ConfigDrivenAgent(config=config)

        task = _make_task()
        output = {"result": "ok"}

        with patch.object(agent, "evolve_after_task", new_callable=AsyncMock, side_effect=RuntimeError("evolution crashed")):
            # Should NOT raise — evolution failure is caught
            await agent.on_task_complete(task, output)

    async def test_evolution_failure_on_task_failed_does_not_break(self):
        """进化失败不影响 on_task_failed"""
        from agentkit.core.config_driven import ConfigDrivenAgent

        config = self._make_agent_config(evolution={"enabled": True})
        agent = ConfigDrivenAgent(config=config)

        task = _make_task()
        error = ValueError("task error")

        with patch.object(agent, "evolve_after_task", new_callable=AsyncMock, side_effect=RuntimeError("evolution crashed")):
            # Should NOT raise
            await agent.on_task_failed(task, error)

    def test_skill_config_evolution_propagated(self):
        """SkillConfig 的 evolution 配置传递到 ConfigDrivenAgent"""
        from agentkit.core.config_driven import ConfigDrivenAgent

        config = self._make_skill_config(evolution={"enabled": True})
        agent = ConfigDrivenAgent(config=config)
        assert agent._evolution_enabled is True


# ── EvolutionMixin 集成测试 ───────────────────────────────


class TestEvolutionMixinIntegration:
    """U11: EvolutionMixin 方法集成到 ConfigDrivenAgent"""

    def _make_agent_with_evolution(self):
        from agentkit.core.config_driven import AgentConfig, ConfigDrivenAgent

        config = AgentConfig(
            name="test_agent",
            agent_type="test",
            task_mode="llm_generate",
            prompt={"identity": "test", "instructions": "test"},
        )
        config.evolution = {"enabled": True}
        return ConfigDrivenAgent(config=config)

    def test_agent_has_get_evolution_history(self):
        """Agent 继承 get_evolution_history 方法"""
        from agentkit.core.config_driven import ConfigDrivenAgent
        from agentkit.evolution.lifecycle import EvolutionMixin

        agent = self._make_agent_with_evolution()
        assert hasattr(agent, "get_evolution_history")
        assert callable(agent.get_evolution_history)

    def test_agent_has_set_current_module(self):
        """Agent 继承 set_current_module 方法"""
        from agentkit.core.config_driven import ConfigDrivenAgent
        from agentkit.evolution.lifecycle import EvolutionMixin

        agent = self._make_agent_with_evolution()
        assert hasattr(agent, "set_current_module")
        assert callable(agent.set_current_module)

    def test_get_evolution_history_empty_initially(self):
        """get_evolution_history 初始返回空列表"""
        agent = self._make_agent_with_evolution()
        history = agent.get_evolution_history()
        assert history == []

    def test_set_current_module_works(self):
        """set_current_module 正常工作"""
        from agentkit.evolution.prompt_optimizer import Module, Signature

        agent = self._make_agent_with_evolution()
        signature = Signature(
            input_fields={"query": "user query"},
            output_fields={"result": "result"},
            instruction="test instructions",
        )
        module = Module(name="test_module", signature=signature)
        agent.set_current_module(module)
        assert agent._current_module is not None
        assert agent._current_module.name == "test_module"

    def test_mro_correct(self):
        """MRO 正确: ConfigDrivenAgent → BaseAgent → EvolutionMixin"""
        from agentkit.core.config_driven import ConfigDrivenAgent
        from agentkit.core.base import BaseAgent
        from agentkit.evolution.lifecycle import EvolutionMixin

        mro = ConfigDrivenAgent.__mro__
        # BaseAgent should come before EvolutionMixin in MRO
        base_idx = mro.index(BaseAgent)
        mixin_idx = mro.index(EvolutionMixin)
        assert base_idx < mixin_idx, f"BaseAgent (idx={base_idx}) should come before EvolutionMixin (idx={mixin_idx}) in MRO"
