"""
Tests for Prompt Domain Services.

Tests the pure domain logic in apps/prompt/domain/services.py
Only uses Python standard library - no Django imports.
"""

from typing import Any

from apps.prompt.domain.entities import ChainExecutionMode, ChainStep
from apps.prompt.domain.services import (
    ChainExecutionPlan,
    ChainExecutor,
    OutputParser,
    PlaceholderResolverProtocol,
    TemplateRenderer,
)


class MockResolver(PlaceholderResolverProtocol):
    """Mock resolver for testing"""

    def resolve(self, placeholder: str, context: dict[str, Any]) -> Any:
        """简单解析器"""
        return context.get(placeholder, f"<{placeholder}>")


class TestTemplateRenderer:
    """测试模板渲染器"""

    def test_init(self):
        """测试初始化"""
        renderer = TemplateRenderer()
        assert renderer.resolvers == {}

    def test_register_resolver(self):
        """测试注册解析器"""
        renderer = TemplateRenderer()
        resolver = MockResolver()
        renderer.register_resolver("test", resolver)
        assert "test" in renderer.resolvers
        assert renderer.resolvers["test"] == resolver

    def test_render_simple(self):
        """测试简单模板渲染"""
        renderer = TemplateRenderer()
        template = "Hello {{name}}, value is {{value}}"
        context = {"name": "World", "value": 42}

        result = renderer.render_simple(template, context)
        assert result == "Hello World, value is 42"

    def test_render_simple_with_chinese(self):
        """测试中文模板渲染"""
        renderer = TemplateRenderer()
        template = "PMI: {{PMI}}, CPI: {{CPI}}"
        context = {"PMI": "50.2", "CPI": "2.1%"}

        result = renderer.render_simple(template, context)
        assert result == "PMI: 50.2, CPI: 2.1%"

    def test_render_simple_partial(self):
        """测试部分占位符渲染"""
        renderer = TemplateRenderer()
        template = "{{name}} is {{age}} years old"
        context = {"name": "Alice"}

        result = renderer.render_simple(template, context)
        # 未提供的占位符不会被替换
        assert result == "Alice is {{age}} years old"

    def test_render_simple_empty_context(self):
        """测试空上下文"""
        renderer = TemplateRenderer()
        template = "Hello {{name}}"
        context = {}

        result = renderer.render_simple(template, context)
        assert result == "Hello {{name}}"

    def test_render_simple_multiple_same_placeholder(self):
        """测试重复占位符"""
        renderer = TemplateRenderer()
        template = "{{x}} + {{x}} = {{result}}"
        context = {"x": "5", "result": "10"}

        result = renderer.render_simple(template, context)
        assert result == "5 + 5 = 10"

    def test_extract_placeholders(self):
        """测试提取占位符"""
        renderer = TemplateRenderer()
        template = "PMI: {{PMI}}, CPI: {{CPI}}, M2: {{M2}}"
        placeholders = renderer.extract_placeholders(template)

        assert set(placeholders) == {"PMI", "CPI", "M2"}

    def test_extract_placeholders_spaces(self):
        """测试提取带空格的占位符"""
        renderer = TemplateRenderer()
        template = "Value: {{ value }}, Another: {{ another }}"
        placeholders = renderer.extract_placeholders(template)

        assert set(placeholders) == {"value", "another"}

    def test_extract_placeholders_none(self):
        """测试无占位符"""
        renderer = TemplateRenderer()
        template = "No placeholders here"
        placeholders = renderer.extract_placeholders(template)

        assert placeholders == []

    def test_extract_placeholders_complex(self):
        """测试复杂模板占位符提取"""
        renderer = TemplateRenderer()
        template = """
        经济指标：
        - PMI: {{PMI}}
        - CPI: {{CPI}}
        - M2: {{M2}}
        - SHIBOR: {{SHIBOR}}
        """
        placeholders = renderer.extract_placeholders(template)

        assert set(placeholders) == {"PMI", "CPI", "M2", "SHIBOR"}


class TestOutputParser:
    """测试输出解析器"""

    def test_extract_json_from_code_block(self):
        """测试从代码块中提取 JSON"""
        response = '''```json
        {
            "PMI": 50.2,
            "CPI": 2.1
        }
        ```'''
        result = OutputParser.extract_json(response)

        assert result is not None
        assert result["PMI"] == 50.2
        assert result["CPI"] == 2.1

    def test_extract_json_direct(self):
        """测试直接提取 JSON"""
        response = '{"PMI": 50.2, "CPI": 2.1}'
        result = OutputParser.extract_json(response)

        assert result is not None
        assert result["PMI"] == 50.2

    def test_extract_json_with_text(self):
        """测试从包含文本的响应中提取 JSON"""
        response = '''这是一些文本

        ```json
        {"PMI": 50.2}
        ```

        更多文本'''
        result = OutputParser.extract_json(response)

        assert result is not None
        assert result["PMI"] == 50.2

    def test_extract_json_invalid(self):
        """测试无效 JSON 返回 None"""
        response = "This is not JSON"
        result = OutputParser.extract_json(response)

        assert result is None

    def test_extract_json_invalid_code_block(self):
        """测试代码块中的无效 JSON"""
        response = '```json\n{invalid json}\n```'
        result = OutputParser.extract_json(response)

        assert result is None

    def test_extract_table_simple(self):
        """测试提取简单表格"""
        response = '''
        | Header1 | Header2 |
        |---------|---------|
        | Value1  | Value2  |
        | Value3  | Value4  |
        '''
        result = OutputParser.extract_table(response)

        assert len(result) == 3
        assert result[0] == ["Header1", "Header2"]
        assert result[1] == ["Value1", "Value2"]
        assert result[2] == ["Value3", "Value4"]

    def test_extract_table_with_text(self):
        """测试从包含文本的响应中提取表格"""
        response = '''
        Some text before

        | 指标 | 数值 |
        |------|------|
        | PMI  | 50.2 |
        | CPI  | 2.1  |

        Some text after
        '''
        result = OutputParser.extract_table(response)

        assert len(result) == 3
        assert result[0] == ["指标", "数值"]

    def test_extract_table_no_table(self):
        """测试无表格时返回空列表"""
        response = "No table here"
        result = OutputParser.extract_table(response)

        assert result == []

    def test_extract_code_blocks_specific_language(self):
        """测试提取特定语言代码块"""
        response = '''
        Some text

        ```python
        def hello():
            print("Hello")
        ```

        ```json
        {"key": "value"}
        ```
        '''
        result = OutputParser.extract_code_blocks(response, language="python")

        assert len(result) == 1
        assert "def hello()" in result[0]

    def test_extract_code_blocks_all(self):
        """测试提取所有代码块"""
        response = '''
        ```python
        print("hello")
        ```

        ```json
        {"key": "value"}
        ```
        '''
        result = OutputParser.extract_code_blocks(response)

        assert len(result) == 2
        assert 'print("hello")' in result[0]
        assert '{"key": "value"}' in result[1]

    def test_parse_function_calls_valid(self):
        """测试解析函数调用 - 当前实现返回空列表"""
        # 注意：parse_function_calls 使用特定格式，这里测试预期行为
        response = "Some text without function blocks"
        result = OutputParser.parse_function_calls(response)

        # 如果没有匹配的格式，返回空列表
        assert result == []

    def test_parse_function_calls_multiple(self):
        """测试解析多个函数调用 - 当前实现返回空列表"""
        response = "No valid function call format"
        result = OutputParser.parse_function_calls(response)

        assert result == []

    def test_parse_function_calls_invalid(self):
        """测试解析无效函数调用返回空列表"""
        response = "No function calls here"
        result = OutputParser.parse_function_calls(response)

        assert result == []


class TestChainExecutionPlan:
    """测试链式执行计划"""

    def test_init(self):
        """测试初始化"""
        plan = ChainExecutionPlan()
        assert plan.steps == []

    def test_add_step(self):
        """测试添加步骤"""
        plan = ChainExecutionPlan()
        step = ChainExecutionPlan.ExecutionStep(
            step_id="step1",
            template_id="tpl1",
            order=1
        )
        plan.add_step(step)

        assert len(plan.steps) == 1
        assert plan.steps[0].step_id == "step1"

    def test_get_parallel_groups_no_groups(self):
        """测试获取并行分组（无分组）"""
        plan = ChainExecutionPlan()
        step1 = ChainExecutionPlan.ExecutionStep(
            step_id="step1",
            template_id="tpl1",
            order=1
        )
        step2 = ChainExecutionPlan.ExecutionStep(
            step_id="step2",
            template_id="tpl2",
            order=2
        )
        plan.add_step(step1)
        plan.add_step(step2)

        groups = plan.get_parallel_groups()
        assert "__serial__" in groups
        assert len(groups["__serial__"]) == 2

    def test_get_parallel_groups_with_groups(self):
        """测试获取并行分组（有分组）"""
        plan = ChainExecutionPlan()
        step1 = ChainExecutionPlan.ExecutionStep(
            step_id="step1",
            template_id="tpl1",
            order=1,
            parallel_group="group_a"
        )
        step2 = ChainExecutionPlan.ExecutionStep(
            step_id="step2",
            template_id="tpl2",
            order=1,
            parallel_group="group_a"
        )
        plan.add_step(step1)
        plan.add_step(step2)

        groups = plan.get_parallel_groups()
        assert "group_a" in groups
        assert len(groups["group_a"]) == 2


class TestChainExecutor:
    """测试链式执行器"""

    def test_init(self):
        """测试初始化"""
        executor = ChainExecutor()
        assert executor._plan is None

    def test_build_serial_plan(self):
        """测试构建串行执行计划"""
        executor = ChainExecutor()
        steps = [
            ChainStep(
                step_id="step1",
                template_id="tpl1",
                step_name="Step 1",
                order=1,
                input_mapping={}
            ),
            ChainStep(
                step_id="step2",
                template_id="tpl2",
                step_name="Step 2",
                order=2,
                input_mapping={}
            ),
        ]

        plan = executor.build_execution_plan(steps, ChainExecutionMode.SERIAL)

        assert len(plan.steps) == 2
        # 第一个步骤没有依赖
        assert plan.steps[0].dependencies == []
        # 第二个步骤依赖第一个
        assert plan.steps[1].dependencies == ["step1"]

    def test_build_parallel_plan(self):
        """测试构建并行执行计划"""
        executor = ChainExecutor()
        steps = [
            ChainStep(
                step_id="step1",
                template_id="tpl1",
                step_name="Step 1",
                order=1,
                input_mapping={},
                parallel_group="group_a"
            ),
            ChainStep(
                step_id="step2",
                template_id="tpl2",
                step_name="Step 2",
                order=1,
                input_mapping={},
                parallel_group="group_a"
            ),
            ChainStep(
                step_id="step3",
                template_id="tpl3",
                step_name="Step 3",
                order=2,
                input_mapping={}
            ),
        ]

        plan = executor.build_execution_plan(steps, ChainExecutionMode.PARALLEL)

        assert len(plan.steps) == 3
        # group_a 的步骤没有依赖
        assert plan.steps[0].parallel_group == "group_a"
        assert plan.steps[0].dependencies == []
        # step3 依赖 group_a 的所有步骤
        assert set(plan.steps[2].dependencies) == {"step1", "step2"}

    def test_build_tool_calling_plan(self):
        """测试构建工具调用执行计划"""
        executor = ChainExecutor()
        steps = [
            ChainStep(
                step_id="step1",
                template_id="tpl1",
                step_name="Tool Call",
                order=1,
                input_mapping={},
                enable_tool_calling=True
            ),
        ]

        plan = executor.build_execution_plan(steps, ChainExecutionMode.TOOL_CALLING)

        assert len(plan.steps) == 1
        assert plan.steps[0].dependencies == []

    def test_build_hybrid_plan(self):
        """测试构建混合执行计划"""
        executor = ChainExecutor()
        steps = [
            ChainStep(
                step_id="step1",
                template_id="tpl1",
                step_name="Step 1",
                order=1,
                input_mapping={},
                parallel_group="group_a"
            ),
        ]

        plan = executor.build_execution_plan(steps, ChainExecutionMode.HYBRID)

        assert len(plan.steps) == 1
        # 混合模式使用并行计划逻辑
        assert plan.steps[0].parallel_group == "group_a"
