"""
Unit tests for RoleBasedFieldProvider service.
[AC-MRS-04,05,10,11,12,13,14] 验证按角色查询字段定义功能
"""

import pytest
from unittest.mock import AsyncMock, MagicMock, patch
from sqlalchemy.ext.asyncio import AsyncSession

from app.services.mid.role_based_field_provider import (
    RoleBasedFieldProvider,
    InvalidRoleError,
)
from app.models.entities import (
    MetadataFieldDefinition,
    MetadataFieldStatus,
    SlotDefinition,
)
from app.schemas.metadata import VALID_FIELD_ROLES


class TestRoleBasedFieldProvider:
    """[AC-MRS-04,05,10] RoleBasedFieldProvider 测试"""

    @pytest.fixture
    def mock_session(self):
        """Mock AsyncSession"""
        session = MagicMock(spec=AsyncSession)
        session.execute = AsyncMock()
        return session

    @pytest.fixture
    def provider(self, mock_session):
        """Create provider instance"""
        return RoleBasedFieldProvider(mock_session)

    def test_validate_role_valid(self, provider):
        """[AC-MRS-04] 验证有效角色"""
        for role in VALID_FIELD_ROLES:
            result = provider._validate_role(role)
            assert result == role

    def test_validate_role_invalid(self, provider):
        """[AC-MRS-05] 验证无效角色抛出异常"""
        with pytest.raises(InvalidRoleError) as exc_info:
            provider._validate_role("invalid_role")
        
        assert "Invalid role 'invalid_role'" in str(exc_info.value)
        assert exc_info.value.valid_roles == VALID_FIELD_ROLES

    @pytest.mark.asyncio
    async def test_get_fields_by_role(self, provider, mock_session):
        """[AC-MRS-04] 按角色获取字段定义"""
        mock_field = MagicMock(spec=MetadataFieldDefinition)
        mock_field.id = "test-id"
        mock_field.field_key = "grade"
        mock_field.label = "年级"
        mock_field.field_roles = ["resource_filter", "slot"]
        mock_field.status = MetadataFieldStatus.ACTIVE.value
        
        mock_result = MagicMock()
        mock_result.scalars.return_value.all.return_value = [mock_field]
        mock_session.execute.return_value = mock_result
        
        fields = await provider.get_fields_by_role(
            "test-tenant",
            "resource_filter"
        )
        
        assert len(fields) == 1
        assert fields[0].field_key == "grade"
        assert "resource_filter" in fields[0].field_roles

    @pytest.mark.asyncio
    async def test_get_fields_by_role_invalid_role(self, provider):
        """[AC-MRS-05] 无效角色返回 400 错误"""
        with pytest.raises(InvalidRoleError):
            await provider.get_fields_by_role(
                "test-tenant",
                "invalid_role"
            )

    @pytest.mark.asyncio
    async def test_get_fields_by_role_include_deprecated(self, provider, mock_session):
        """[AC-MRS-04] 包含已废弃字段"""
        mock_active = MagicMock(spec=MetadataFieldDefinition)
        mock_active.field_key = "active_field"
        mock_active.status = MetadataFieldStatus.ACTIVE.value
        
        mock_deprecated = MagicMock(spec=MetadataFieldDefinition)
        mock_deprecated.field_key = "deprecated_field"
        mock_deprecated.status = MetadataFieldStatus.DEPRECATED.value
        
        mock_result = MagicMock()
        mock_result.scalars.return_value.all.return_value = [mock_active, mock_deprecated]
        mock_session.execute.return_value = mock_result
        
        fields = await provider.get_fields_by_role(
            "test-tenant",
            "resource_filter",
            include_deprecated=True
        )
        
        assert len(fields) == 2

    @pytest.mark.asyncio
    async def test_get_slot_definitions_by_role(self, provider, mock_session):
        """[AC-MRS-10] 按角色获取槽位定义"""
        mock_field = MagicMock(spec=MetadataFieldDefinition)
        mock_field.id = MagicMock()
        mock_field.id.__str__ = lambda self: "field-id-123"
        mock_field.field_key = "grade"
        mock_field.label = "年级"
        mock_field.type = "string"
        mock_field.required = True
        mock_field.options = None
        mock_field.default_value = None
        mock_field.scope = ["kb_document"]
        mock_field.is_filterable = True
        mock_field.is_rank_feature = False
        mock_field.field_roles = ["slot"]
        mock_field.status = MetadataFieldStatus.ACTIVE.value
        
        mock_slot = MagicMock(spec=SlotDefinition)
        mock_slot.id = MagicMock()
        mock_slot.id.__str__ = lambda self: "slot-id-456"
        mock_slot.tenant_id = "test-tenant"
        mock_slot.slot_key = "grade"
        mock_slot.type = "string"
        mock_slot.required = True
        mock_slot.extract_strategy = "llm"
        mock_slot.validation_rule = None
        mock_slot.ask_back_prompt = "请输入年级"
        mock_slot.default_value = None
        mock_slot.linked_field_id = mock_field.id
        mock_slot.created_at = None
        mock_slot.updated_at = None
        
        field_result = MagicMock()
        field_result.scalars.return_value.all.return_value = [mock_field]
        
        slot_result = MagicMock()
        slot_result.scalars.return_value.all.return_value = [mock_slot]
        
        mock_session.execute.side_effect = [field_result, slot_result]
        
        slots = await provider.get_slot_definitions_by_role("test-tenant", "slot")
        
        assert len(slots) >= 1

    @pytest.mark.asyncio
    async def test_get_resource_filter_fields(self, provider, mock_session):
        """[AC-MRS-11] 获取资源过滤角色字段"""
        mock_field = MagicMock(spec=MetadataFieldDefinition)
        mock_field.field_key = "category"
        mock_field.field_roles = ["resource_filter"]
        
        mock_result = MagicMock()
        mock_result.scalars.return_value.all.return_value = [mock_field]
        mock_session.execute.return_value = mock_result
        
        fields = await provider.get_resource_filter_fields("test-tenant")
        
        assert len(fields) == 1
        assert "resource_filter" in fields[0].field_roles

    @pytest.mark.asyncio
    async def test_get_slot_fields(self, provider, mock_session):
        """[AC-MRS-12] 获取槽位角色字段"""
        mock_field = MagicMock(spec=MetadataFieldDefinition)
        mock_field.field_key = "user_name"
        mock_field.field_roles = ["slot"]
        
        mock_result = MagicMock()
        mock_result.scalars.return_value.all.return_value = [mock_field]
        mock_session.execute.return_value = mock_result
        
        fields = await provider.get_slot_fields("test-tenant")
        
        assert len(fields) == 1
        assert "slot" in fields[0].field_roles

    @pytest.mark.asyncio
    async def test_get_routing_signal_fields(self, provider, mock_session):
        """[AC-MRS-13] 获取路由信号角色字段"""
        mock_field = MagicMock(spec=MetadataFieldDefinition)
        mock_field.field_key = "priority"
        mock_field.field_roles = ["routing_signal"]
        
        mock_result = MagicMock()
        mock_result.scalars.return_value.all.return_value = [mock_field]
        mock_session.execute.return_value = mock_result
        
        fields = await provider.get_routing_signal_fields("test-tenant")
        
        assert len(fields) == 1
        assert "routing_signal" in fields[0].field_roles

    @pytest.mark.asyncio
    async def test_get_prompt_var_fields(self, provider, mock_session):
        """[AC-MRS-14] 获取提示词变量角色字段"""
        mock_field = MagicMock(spec=MetadataFieldDefinition)
        mock_field.field_key = "user_name"
        mock_field.field_roles = ["prompt_var"]
        
        mock_result = MagicMock()
        mock_result.scalars.return_value.all.return_value = [mock_field]
        mock_session.execute.return_value = mock_result
        
        fields = await provider.get_prompt_var_fields("test-tenant")
        
        assert len(fields) == 1
        assert "prompt_var" in fields[0].field_roles


class TestInvalidRoleError:
    """[AC-MRS-05] InvalidRoleError 测试"""

    def test_error_message(self):
        """验证错误消息格式"""
        error = InvalidRoleError("bad_role")
        
        assert error.role == "bad_role"
        assert error.valid_roles == VALID_FIELD_ROLES
        assert "Invalid role 'bad_role'" in str(error)
        assert "resource_filter" in str(error)
