# -*- coding: utf-8 -*-
"""
单元测试 - common/structs 模块 - Pydantic模型测试
"""

from unittest.mock import patch

from app.common.structs import (
    AgentConfig,
    AgentOptions,
    AgentInput,
    DEFAULT_INPUTS,
)


class TestAgentConfig:
    """测试 AgentConfig Pydantic 模型"""

    def test_default_initialization(self):
        """测试默认初始化"""
        config = AgentConfig()

        assert config.input is None
        assert config.llms is None
        assert config.skills is not None  # SkillVo() due to validator
        assert config.data_source == {}
        assert config.system_prompt is None
        assert config.is_dolphin_mode is False
        assert config.dolphin is None
        assert config.pre_dolphin == []
        assert config.post_dolphin == []
        assert config.output == {}
        assert config.memory == {}
        assert config.related_question == {}
        assert config.plan_mode is None
        assert config.agent_id is None
        assert config.conversation_id is not None  # Auto-generated by validator
        assert config.session_id is None
        assert config.output_vars is None
        assert config.incremental_output is False

    def test_with_all_fields(self):
        """测试设置所有字段"""
        config = AgentConfig(
            input={"key": "value"},
            llms=[{"model": "gpt-4"}],
            skills=None,  # Will be converted to SkillVo()
            data_source={"type": "test"},
            system_prompt="Test prompt",
            is_dolphin_mode=True,
            dolphin="test_dolphin",
            pre_dolphin=[{"config": "value"}],
            post_dolphin=[{"config": "value2"}],
            output={"result": "value"},
            memory={"context": "data"},
            related_question={"question": "test"},
            plan_mode={"is_enabled": True},
            agent_id="agent_123",
            conversation_id="conv_456",
            session_id="session_789",
            output_vars=["var1", "var2"],
            incremental_output=True,
        )

        assert config.input == {"key": "value"}
        assert config.llms == [{"model": "gpt-4"}]
        assert config.skills is not None
        assert config.data_source == {"type": "test"}
        assert config.system_prompt == "Test prompt"
        assert config.is_dolphin_mode is True
        assert config.dolphin == "test_dolphin"
        assert config.pre_dolphin == [{"config": "value"}]
        assert config.post_dolphin == [{"config": "value2"}]
        assert config.output == {"result": "value"}
        assert config.memory == {"context": "data"}
        assert config.related_question == {"question": "test"}
        assert config.plan_mode == {"is_enabled": True}
        assert config.agent_id == "agent_123"
        assert config.conversation_id == "conv_456"
        assert config.session_id == "session_789"
        assert config.output_vars == ["var1", "var2"]
        assert config.incremental_output is True

    def test_validate_skills_with_none(self):
        """测试 validate_skills 验证器 - None 转换为 SkillVo"""
        config = AgentConfig(skills=None)

        # Should be converted to SkillVo object
        assert config.skills is not None
        assert hasattr(config.skills, "tools")
        assert hasattr(config.skills, "agents")

    def test_validate_skills_with_dict(self):
        """测试 validate_skills 验证器 - 字典转换"""
        config = AgentConfig(skills={"tools": [], "agents": []})

        # Should be converted to SkillVo object
        assert config.skills is not None
        assert hasattr(config.skills, "tools")

    def test_validate_skills_with_skillvo_instance(self):
        """测试 validate_skills 验证器 - 已是 SkillVo 实例"""
        from app.domain.vo.agentvo.agent_config_vos import SkillVo

        skill_vo = SkillVo()
        config = AgentConfig(skills=skill_vo)

        # Should return the same SkillVo instance
        assert config.skills is skill_vo

    def test_set_conversation_id_auto_generate(self):
        """测试 set_conversation_id 验证器 - 自动生成ID"""
        with patch("app.common.structs.snow_id") as mock_snow_id:
            mock_snow_id.return_value = "test_snow_id_123"

            config = AgentConfig(conversation_id=None)

            assert config.conversation_id == "test_snow_id_123"

    def test_set_conversation_id_with_value(self):
        """测试 set_conversation_id 验证器 - 使用提供的值"""
        config = AgentConfig(conversation_id="provided_conv_id")

        assert config.conversation_id == "provided_conv_id"

    def test_set_conversation_id_with_empty_string(self):
        """测试 set_conversation_id 验证器 - 空字符串时自动生成"""
        with patch("app.common.structs.snow_id") as mock_snow_id:
            mock_snow_id.return_value = "test_snow_id_456"

            config = AgentConfig(conversation_id="")

            assert config.conversation_id == "test_snow_id_456"

    def test_handle_none_for_pre_dolphin(self):
        """测试 handle_none 验证器 - pre_dolphin None 转换为 []"""
        config = AgentConfig(pre_dolphin=None)

        assert config.pre_dolphin == []

    def test_handle_none_for_post_dolphin(self):
        """测试 handle_none 验证器 - post_dolphin None 转换为 []"""
        config = AgentConfig(post_dolphin=None)

        assert config.post_dolphin == []

    def test_is_plan_mode_enabled(self):
        """测试 is_plan_mode 方法 - 启用状态"""
        config = AgentConfig(plan_mode={"is_enabled": True})

        assert config.is_plan_mode() is True

    def test_is_plan_mode_disabled(self):
        """测试 is_plan_mode 方法 - 禁用状态"""
        config = AgentConfig(plan_mode={"is_enabled": False})

        assert config.is_plan_mode() is False

    def test_is_plan_mode_none(self):
        """测试 is_plan_mode 方法 - plan_mode 为 None"""
        config = AgentConfig(plan_mode=None)

        # When plan_mode is None, is_plan_mode() returns None
        assert config.is_plan_mode() is None

    def test_is_plan_mode_missing_key(self):
        """测试 is_plan_mode 方法 - 缺少 is_enabled 键"""
        # Pydantic will validate that the dict value is a boolean
        # So we need to pass a valid boolean value for other_key
        config = AgentConfig(plan_mode={"other_key": True})

        # get() will return None for missing key, which is falsy
        assert config.is_plan_mode() is False or config.is_plan_mode() is None

    def test_append_task_plan_agent_with_plan_mode(self):
        """测试 append_task_plan_agent 方法 - plan mode 启用"""
        config = AgentConfig(plan_mode={"is_enabled": True})

        config.append_task_plan_agent()

        # Should add Task_Plan_Agent to skills.agents
        assert config.skills is not None
        assert config.skills.agents is not None
        assert len(config.skills.agents) > 0

        # Find the Task_Plan_Agent
        task_plan_agent = None
        for agent in config.skills.agents:
            if hasattr(agent, "agent_key") and agent.agent_key == "Task_Plan_Agent":
                task_plan_agent = agent
                break

        assert task_plan_agent is not None

    def test_append_task_plan_agent_without_plan_mode(self):
        """测试 append_task_plan_agent 方法 - plan mode 禁用"""
        config = AgentConfig(plan_mode={"is_enabled": False})

        config.append_task_plan_agent()

        # Should not add Task_Plan_Agent
        assert config.skills is not None
        # Should be empty since we didn't add any agents
        assert len(config.skills.agents) == 0

    def test_append_task_plan_agent_creates_skills_if_none(self):
        """测试 append_task_plan_agent 方法 - skills 为 None 时创建"""
        config = AgentConfig(plan_mode={"is_enabled": True}, skills=None)

        config.append_task_plan_agent()

        # Skills should be created
        assert config.skills is not None
        assert len(config.skills.agents) > 0

    def test_append_task_plan_agent_with_existing_skills(self):
        """测试 append_task_plan_agent 方法 - skills 已存在"""
        from app.domain.vo.agentvo.agent_config_vos import SkillVo

        config = AgentConfig(
            plan_mode={"is_enabled": True},
            skills=SkillVo(),  # Already a SkillVo instance
        )

        config.append_task_plan_agent()

        # Should add to existing skills
        assert config.skills is not None
        assert len(config.skills.agents) > 0


class TestAgentOptions:
    """测试 AgentOptions Pydantic 模型"""

    def test_default_initialization(self):
        """测试默认初始化"""
        options = AgentOptions()

        assert options.output_vars is None
        assert options.incremental_output is None
        assert options.data_source is None
        assert options.llm_config is None
        assert options.tmp_files is None

    def test_with_all_fields(self):
        """测试设置所有字段"""
        options = AgentOptions(
            output_vars=["var1", "var2"],
            incremental_output=True,
            data_source={"type": "test"},
            llm_config={"model": "gpt-4"},
            tmp_files=["file1", "file2"],
        )

        assert options.output_vars == ["var1", "var2"]
        assert options.incremental_output is True
        assert options.data_source == {"type": "test"}
        assert options.llm_config == {"model": "gpt-4"}
        assert options.tmp_files == ["file1", "file2"]


class TestAgentInput:
    """测试 AgentInput Pydantic 模型"""

    def test_default_initialization(self):
        """测试默认初始化"""
        agent_input = AgentInput(query="test query")

        assert agent_input.query == "test query"
        assert agent_input.history is None
        assert agent_input.tool == {}
        assert agent_input.header == {}
        assert agent_input.self_config == {}

    def test_with_all_fields(self):
        """测试设置所有字段"""
        agent_input = AgentInput(
            query="test query",
            history=[{"role": "user", "content": "hello"}],
            tool={"name": "test_tool"},
            header={"auth": "token"},
            self_config={"key": "value"},
        )

        assert agent_input.query == "test query"
        assert len(agent_input.history) == 1
        assert agent_input.tool == {"name": "test_tool"}
        assert agent_input.header == {"auth": "token"}
        assert agent_input.self_config == {"key": "value"}

    def test_get_value_defined_field(self):
        """测试 get_value 方法 - 获取定义的字段"""
        agent_input = AgentInput(query="test")

        value = agent_input.get_value("query")

        assert value == "test"

    def test_get_value_undefined_field(self):
        """测试 get_value 方法 - 获取未定义的字段返回 None"""
        agent_input = AgentInput(query="test")

        value = agent_input.get_value("nonexistent_field")

        assert value is None

    def test_get_value_undefined_field_with_default(self):
        """测试 get_value 方法 - 获取未定义的字段返回默认值"""
        agent_input = AgentInput(query="test")

        value = agent_input.get_value("nonexistent_field", "default_value")

        assert value == "default_value"

    def test_get_value_extra_field(self):
        """测试 get_value 方法 - 获取额外字段"""
        agent_input = AgentInput(query="test", custom_field="custom_value")

        value = agent_input.get_value("custom_field")

        assert value == "custom_value"

    def test_set_value_defined_field(self):
        """测试 set_value 方法 - 设置定义的字段"""
        agent_input = AgentInput(query="initial")

        agent_input.set_value("query", "updated")

        assert agent_input.query == "updated"

    def test_set_value_new_field(self):
        """测试 set_value 方法 - 设置新字段"""
        agent_input = AgentInput(query="test")

        agent_input.set_value("new_field", "new_value")

        # Should be able to get the new value
        assert agent_input.get_value("new_field") == "new_value"

    def test_model_dump_with_empty_tool(self):
        """测试 model_dump 方法 - tool 为空时移除"""
        agent_input = AgentInput(query="test", tool={})

        dumped = agent_input.model_dump()

        assert "tool" not in dumped
        assert "query" in dumped

    def test_model_dump_with_tool(self):
        """测试 model_dump 方法 - tool 有值时保留"""
        agent_input = AgentInput(query="test", tool={"name": "test_tool"})

        dumped = agent_input.model_dump()

        assert "tool" in dumped
        assert dumped["tool"] == {"name": "test_tool"}

    def test_model_dump_with_extra_fields(self):
        """测试 model_dump 方法 - 包含额外字段"""
        agent_input = AgentInput(query="test", custom_field="custom_value")

        dumped = agent_input.model_dump()

        assert "custom_field" in dumped
        assert dumped["custom_field"] == "custom_value"

    def test_extra_fields_allowed(self):
        """测试允许额外字段"""
        # Should not raise validation error
        agent_input = AgentInput(
            query="test", custom_field1="value1", custom_field2="value2"
        )

        assert agent_input.custom_field1 == "value1"
        assert agent_input.custom_field2 == "value2"


class TestModuleConstants:
    """测试模块级常量"""

    def test_default_inputs_constant(self):
        """测试 DEFAULT_INPUTS 常量"""
        assert DEFAULT_INPUTS is not None
        assert isinstance(DEFAULT_INPUTS, list)
        assert "history" in DEFAULT_INPUTS
        assert "tool" in DEFAULT_INPUTS
        assert "header" in DEFAULT_INPUTS
        assert "self_config" in DEFAULT_INPUTS
