"""
Unit tests for FlowEngine script generation.
[AC-IDS-03, AC-IDS-05, AC-IDS-11, AC-IDS-13] Test intent-driven script generation in FlowEngine.

NOTE: These tests are for features not yet implemented in FlowEngine.
The _generate_step_content method and llm_client parameter are planned for AC-IDS-04.
"""

import uuid
from unittest.mock import AsyncMock, MagicMock, patch

import pytest
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.entities import (
    FlowAdvanceResult,
    FlowInstance,
    FlowInstanceStatus,
    ScriptFlow,
    ScriptMode,
)
from app.services.flow.engine import FlowEngine


@pytest.mark.skip(reason="FlowEngine._generate_step_content not implemented yet - AC-IDS-04")
class TestFlowEngineScriptGeneration:
    """[AC-IDS-03, AC-IDS-05, AC-IDS-11] Test cases for script generation in FlowEngine."""

    @pytest.fixture
    def mock_session(self):
        """Create mock database session."""
        session = MagicMock(spec=AsyncSession)
        session.execute = AsyncMock()
        session.add = MagicMock()
        session.flush = AsyncMock()
        return session

    @pytest.fixture
    def mock_llm_client(self):
        """Create mock LLM client."""
        client = MagicMock()
        client.generate_text = AsyncMock(return_value="您好，请问您贵姓？")
        return client

    @pytest.fixture
    def sample_flow_fixed(self):
        """Create sample flow with fixed mode steps."""
        flow = ScriptFlow(
            id=uuid.uuid4(),
            tenant_id="test_tenant",
            name="测试流程",
            steps=[
                {
                    "step_no": 1,
                    "content": "您好，请问有什么可以帮您？",
                    "script_mode": "fixed",
                    "wait_input": True,
                },
                {
                    "step_no": 2,
                    "content": "感谢您的咨询，再见！",
                    "script_mode": "fixed",
                    "wait_input": False,
                },
            ],
            is_enabled=True,
        )
        return flow

    @pytest.fixture
    def sample_flow_flexible(self):
        """Create sample flow with flexible mode steps."""
        flow = ScriptFlow(
            id=uuid.uuid4(),
            tenant_id="test_tenant",
            name="灵活话术流程",
            steps=[
                {
                    "step_no": 1,
                    "script_mode": "flexible",
                    "intent": "获取用户姓名",
                    "intent_description": "礼貌询问用户姓名",
                    "script_constraints": ["必须礼貌", "语气自然"],
                    "content": "请问怎么称呼您？",
                    "wait_input": True,
                },
                {
                    "step_no": 2,
                    "script_mode": "flexible",
                    "intent": "确认用户需求",
                    "script_constraints": ["简洁明了"],
                    "content": "请问您需要什么帮助？",
                    "wait_input": True,
                },
            ],
            is_enabled=True,
        )
        return flow

    @pytest.fixture
    def sample_flow_template(self):
        """Create sample flow with template mode steps."""
        flow = ScriptFlow(
            id=uuid.uuid4(),
            tenant_id="test_tenant",
            name="模板话术流程",
            steps=[
                {
                    "step_no": 1,
                    "script_mode": "template",
                    "content": "您好{user_name}，请问您{inquiry_style}？",
                    "wait_input": True,
                },
            ],
            is_enabled=True,
        )
        return flow

    @pytest.fixture
    def sample_flow_mixed(self):
        """Create sample flow with mixed mode steps."""
        flow = ScriptFlow(
            id=uuid.uuid4(),
            tenant_id="test_tenant",
            name="混合模式流程",
            steps=[
                {
                    "step_no": 1,
                    "script_mode": "fixed",
                    "content": "您好，欢迎咨询！",
                    "wait_input": False,
                },
                {
                    "step_no": 2,
                    "script_mode": "flexible",
                    "intent": "获取用户姓名",
                    "content": "请问怎么称呼您？",
                    "wait_input": True,
                },
                {
                    "step_no": 3,
                    "script_mode": "template",
                    "content": "好的{user_name}，请问您有什么需要帮助的吗？",
                    "wait_input": True,
                },
            ],
            is_enabled=True,
        )
        return flow

    @pytest.mark.asyncio
    async def test_generate_step_content_fixed_mode(self, mock_session, sample_flow_fixed):
        """Test that fixed mode returns content directly."""
        engine = FlowEngine(session=mock_session, llm_client=None)

        result = await engine._generate_step_content(
            step=sample_flow_fixed.steps[0],
            context=None,
            history=None,
        )

        assert result == "您好，请问有什么可以帮您？"

    @pytest.mark.asyncio
    async def test_generate_step_content_flexible_mode(self, mock_session, mock_llm_client, sample_flow_flexible):
        """Test that flexible mode generates script via LLM."""
        engine = FlowEngine(session=mock_session, llm_client=mock_llm_client)

        result = await engine._generate_step_content(
            step=sample_flow_flexible.steps[0],
            context={"inputs": []},
            history=[{"role": "user", "content": "你好"}],
        )

        assert result == "您好，请问您贵姓？"
        mock_llm_client.generate_text.assert_called_once()

    @pytest.mark.asyncio
    async def test_generate_step_content_flexible_no_intent(self, mock_session, sample_flow_flexible):
        """Test that flexible mode without intent falls back to fixed."""
        engine = FlowEngine(session=mock_session, llm_client=None)

        step = {
            "step_no": 1,
            "script_mode": "flexible",
            "content": "fallback content",
        }

        result = await engine._generate_step_content(
            step=step,
            context=None,
            history=None,
        )

        assert result == "fallback content"

    @pytest.mark.asyncio
    async def test_generate_step_content_template_mode(self, mock_session, sample_flow_template):
        """Test that template mode fills variables from context."""
        engine = FlowEngine(session=mock_session, llm_client=None)

        result = await engine._generate_step_content(
            step=sample_flow_template.steps[0],
            context={
                "user_name": "张先生",
                "inquiry_style": "想咨询产品",
            },
            history=None,
        )

        assert result == "您好张先生，请问您想咨询产品？"

    @pytest.mark.asyncio
    async def test_generate_step_content_unknown_mode(self, mock_session):
        """Test that unknown mode falls back to fixed."""
        engine = FlowEngine(session=mock_session, llm_client=None)

        step = {
            "step_no": 1,
            "script_mode": "unknown",
            "content": "fallback content",
        }

        result = await engine._generate_step_content(
            step=step,
            context=None,
            history=None,
        )

        assert result == "fallback content"

    @pytest.mark.asyncio
    async def test_generate_step_content_default_fixed(self, mock_session):
        """Test that missing script_mode defaults to fixed."""
        engine = FlowEngine(session=mock_session, llm_client=None)

        step = {
            "step_no": 1,
            "content": "default fixed content",
        }

        result = await engine._generate_step_content(
            step=step,
            context=None,
            history=None,
        )

        assert result == "default fixed content"

    def test_script_mode_enum_values(self):
        """Test ScriptMode enum has correct values."""
        assert ScriptMode.FIXED.value == "fixed"
        assert ScriptMode.FLEXIBLE.value == "flexible"
        assert ScriptMode.TEMPLATE.value == "template"


@pytest.mark.skip(reason="FlowEngine._generate_step_content not implemented yet - AC-IDS-04")
class TestFlowEngineBackwardCompatibility:
    """[AC-IDS-13] Test backward compatibility with existing flows."""

    @pytest.fixture
    def mock_session(self):
        """Create mock database session."""
        session = MagicMock(spec=AsyncSession)
        session.execute = AsyncMock()
        session.add = MagicMock()
        session.flush = AsyncMock()
        return session

    @pytest.fixture
    def legacy_flow(self):
        """Create legacy flow without script_mode field."""
        flow = ScriptFlow(
            id=uuid.uuid4(),
            tenant_id="test_tenant",
            name="旧版流程",
            steps=[
                {
                    "step_no": 1,
                    "content": "您好，请问有什么可以帮您？",
                    "wait_input": True,
                },
                {
                    "step_no": 2,
                    "content": "感谢您的咨询！",
                    "wait_input": False,
                },
            ],
            is_enabled=True,
        )
        return flow

    @pytest.mark.asyncio
    async def test_legacy_flow_defaults_to_fixed(self, mock_session, legacy_flow):
        """Test that legacy flow without script_mode uses fixed mode."""
        engine = FlowEngine(session=mock_session, llm_client=None)

        for step in legacy_flow.steps:
            result = await engine._generate_step_content(
                step=step,
                context=None,
                history=None,
            )
            assert result == step["content"]

    @pytest.mark.asyncio
    async def test_legacy_flow_with_missing_fields(self, mock_session):
        """Test that steps with missing optional fields work correctly."""
        engine = FlowEngine(session=mock_session, llm_client=None)

        step = {
            "step_no": 1,
            "content": "simple content",
        }

        result = await engine._generate_step_content(
            step=step,
            context=None,
            history=None,
        )

        assert result == "simple content"


@pytest.mark.skip(reason="FlowEngine._generate_step_content not implemented yet - AC-IDS-04")
class TestFlowEngineFallback:
    """[AC-IDS-05] Test fallback mechanism for script generation."""

    @pytest.fixture
    def mock_session(self):
        """Create mock database session."""
        session = MagicMock(spec=AsyncSession)
        session.execute = AsyncMock()
        session.add = MagicMock()
        session.flush = AsyncMock()
        return session

    @pytest.mark.asyncio
    async def test_flexible_mode_fallback_on_no_llm(self, mock_session):
        """Test that flexible mode falls back when no LLM client."""
        engine = FlowEngine(session=mock_session, llm_client=None)

        step = {
            "step_no": 1,
            "script_mode": "flexible",
            "intent": "获取用户姓名",
            "content": "请问怎么称呼您？",
        }

        result = await engine._generate_step_content(
            step=step,
            context=None,
            history=None,
        )

        assert result == "请问怎么称呼您？"

    @pytest.mark.asyncio
    async def test_template_mode_missing_variable_placeholder(self, mock_session):
        """Test that missing template variables use placeholders."""
        engine = FlowEngine(session=mock_session, llm_client=None)

        step = {
            "step_no": 1,
            "script_mode": "template",
            "content": "您好{unknown_var}，请问有什么可以帮您？",
        }

        result = await engine._generate_step_content(
            step=step,
            context=None,
            history=None,
        )

        assert result == "您好[unknown_var]，请问有什么可以帮您？"
