"""Validation tests for Pydantic models in the ToothFairyAI SDK.

Tests cover:
- Required field validation
- Type validation
- Range validation (temperature, max_tokens, etc.)
- Mode-specific validation (agent modes)
- JSON Schema validation (functions)
- Auth-type dependent validation (authorisations)
- Field mapping to API format (snake_case → camelCase)

Run: python -m pytest tests/test_models_validation.py -v --tb=short --override-ini="addopts="
"""

import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

import pytest
from pydantic import ValidationError
from toothfairyai.models import (
    AgentCreateModel,
    AgentUpdateModel,
    AgentFunctionCreateModel,
    AgentFunctionUpdateModel,
    ScheduledJobCreateModel,
    CronScheduleModel,
    DocumentCreateModel,
    DocumentSearchModel,
    PromptCreateModel,
    PromptUpdateModel,
    AuthorisationCreateModel,
    AuthorisationUpdateModel,
    HookCreateModel,
    BenchmarkCreateModel,
    ConnectionCreateModel,
    ChatCreateModel,
    MessageCreateModel,
    ChannelCreateModel,
    FolderCreateModel,
    EntityCreateModel,
    MemberCreateModel,
    SecretCreateModel,
    AgentMode,
    AuthType,
    EntityType,
    RequestType,
    HostingType,
    ChannelType,
    ChannelProvider,
    MemberRole,
)


class TestAgentValidation:
    """Test Agent model validation."""
    
    def test_agent_create_minimal(self):
        """Test minimal agent creation with required fields only."""
        agent = AgentCreateModel(
            label="Test Agent",
            mode=AgentMode.CHATTER,
            interpolation_string="You are a helpful assistant that answers questions accurately and professionally. Always be polite and helpful to users. Provide detailed and thoughtful responses.",
            goals="Help users with their questions.",
            temperature=0.7,
            max_tokens=4096,
            max_history=10,
            top_k=5,
            doc_top_k=5,
        )
        assert agent.label == "Test Agent"
        assert agent.mode == AgentMode.CHATTER
        payload = agent.to_api_payload()
        assert "label" in payload
        assert "mode" in payload
        assert payload["mode"] == "chatter"
    
    def test_agent_create_with_temperature_validation(self):
        """Test temperature range validation."""
        long_prompt = "You are a helpful assistant that answers questions accurately and professionally. Always be polite and helpful to users. Provide detailed responses."
        with pytest.raises(ValidationError) as exc_info:
            AgentCreateModel(
                label="Test Agent",
                mode=AgentMode.CHATTER,
                interpolation_string=long_prompt,
                goals="Help users.",
                temperature=2.0,
                max_tokens=4096,
                max_history=10,
                top_k=5,
                doc_top_k=5,
            )
        assert "temperature" in str(exc_info.value).lower()
        
        with pytest.raises(ValidationError):
            AgentCreateModel(
                label="Test Agent",
                mode=AgentMode.CHATTER,
                interpolation_string=long_prompt,
                goals="Help users.",
                temperature=0.0,
                max_tokens=4096,
                max_history=10,
                top_k=5,
                doc_top_k=5,
            )
        
        agent = AgentCreateModel(
            label="Test Agent",
            mode=AgentMode.CHATTER,
            interpolation_string=long_prompt,
            goals="Help users.",
            temperature=0.7,
            max_tokens=4096,
            max_history=10,
            top_k=5,
            doc_top_k=5,
        )
        assert agent.temperature == 0.7
    
    def test_agent_mode_coder_requires_has_code(self):
        """Test that coder mode requires has_code=True."""
        with pytest.raises(ValidationError) as exc_info:
            AgentCreateModel(
                label="Coder Agent",
                mode=AgentMode.CODER,
                interpolation_string="You are a coding assistant that helps developers write clean, efficient code. Always follow best practices and provide clear explanations.",
                goals="Help write code.",
                temperature=0.3,
                max_tokens=4096,
                max_history=10,
                top_k=5,
                doc_top_k=5,
                has_code=False,
            )
        assert "has_code" in str(exc_info.value).lower() or "coder" in str(exc_info.value).lower()
    
    def test_agent_mode_coder_cannot_have_agentic_rag(self):
        """Test that coder mode cannot have agenticRAG enabled."""
        with pytest.raises(ValidationError) as exc_info:
            AgentCreateModel(
                label="Coder Agent",
                mode=AgentMode.CODER,
                interpolation_string="You are a coding assistant that helps developers write clean, efficient code. Always follow best practices and provide clear explanations.",
                goals="Help write code.",
                temperature=0.3,
                max_tokens=4096,
                max_history=10,
                top_k=5,
                doc_top_k=5,
                has_code=True,
                agentic_rag=True,
            )
        assert "agentic" in str(exc_info.value).lower()
    
    def test_agent_mode_voice_no_agentic_rag(self):
        """Test that voice mode should not have agenticRAG."""
        with pytest.raises(ValidationError) as exc_info:
            AgentCreateModel(
                label="Voice Agent",
                mode=AgentMode.VOICE,
                interpolation_string="You are a voice assistant that responds naturally and conversationally. Keep responses concise and engaging for voice interaction.",
                goals="Help via voice.",
                temperature=0.7,
                max_tokens=4096,
                max_history=10,
                top_k=5,
                doc_top_k=5,
                agentic_rag=True,
            )
        assert "agentic" in str(exc_info.value).lower()
    
    def test_agent_field_mapping_snake_to_camel(self):
        """Test that snake_case fields map to camelCase in API payload."""
        agent = AgentCreateModel(
            label="Test Agent",
            mode=AgentMode.RETRIEVER,
            interpolation_string="You are a helpful assistant that retrieves information accurately. Always cite your sources and be thorough in your research. Provide comprehensive answers to user queries.",
            goals="Help users.",
            temperature=0.7,
            max_tokens=4096,
            max_history=10,
            top_k=5,
            doc_top_k=5,
            agentic_rag=True,
            has_ner=True,
        )
        payload = agent.to_api_payload()
        assert "agenticRAG" in payload
        assert "hasNER" in payload
        assert "agentic_rag" not in payload
        assert "has_ner" not in payload
    
    def test_agent_update_partial(self):
        """Test partial update model allows optional fields."""
        update = AgentUpdateModel(agent_id="agent-123", label="Updated Label")
        assert update.label == "Updated Label"
        payload = update.to_api_payload()
        assert "label" in payload
        assert "id" in payload


class TestAgentFunctionValidation:
    """Test AgentFunction model validation."""
    
    def test_function_create_minimal(self):
        """Test minimal function creation."""
        func = AgentFunctionCreateModel(
            name="get_weather",
            description="Get the current weather for a location",
            url="https://api.weather.com/current",
        )
        assert func.name == "get_weather"
        assert func.request_type is None
    
    def test_function_parameters_json_schema_format(self):
        """Test that parameters must be valid JSON Schema format."""
        valid_params = {
            "type": "object",
            "properties": {
                "location": {"type": "string", "description": "City name"},
                "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
            },
            "required": ["location"],
        }
        func = AgentFunctionCreateModel(
            name="get_weather",
            description="Get weather",
            url="https://api.weather.com/current",
            parameters=valid_params,
        )
        assert func.parameters == valid_params
    
    def test_function_parameters_rejects_deprecated_format(self):
        """Test that deprecated array format is rejected."""
        deprecated_params = [
            {"name": "location", "type": "string"},
            {"name": "unit", "type": "string"},
        ]
        with pytest.raises(ValidationError) as exc_info:
            AgentFunctionCreateModel(
                name="get_weather",
                description="Get weather",
                url="https://api.weather.com/current",
                parameters=deprecated_params,
            )
        assert "dict" in str(exc_info.value).lower() or "type" in str(exc_info.value).lower()
    
    def test_function_field_mapping(self):
        """Test field mapping for function."""
        func = AgentFunctionCreateModel(
            name="test_function",
            description="Test function",
            url="https://api.example.com",
            authorisation_id="auth-123",
            is_mcp_server=True,
        )
        payload = func.to_api_payload()
        assert "authorisationID" in payload
        assert "isMCPServer" in payload


class TestScheduledJobValidation:
    """Test ScheduledJob model validation."""
    
    def test_cron_schedule_validation(self):
        """Test cron expression validation."""
        from toothfairyai.models import CronJobFrequency
        schedule = CronScheduleModel(
            frequency=CronJobFrequency.DAILY,
            hour=9,
            minute=30,
        )
        assert schedule.frequency == CronJobFrequency.DAILY
    
    def test_scheduled_job_create(self):
        """Test scheduled job creation."""
        from toothfairyai.models import CronJobFrequency
        job = ScheduledJobCreateModel(
            name="Daily Report",
            agent_id="agent-123",
            custom_prompt_id="prompt-123",
            schedule={"frequency": CronJobFrequency.DAILY, "hour": 9, "minute": 0},
        )
        assert job.name == "Daily Report"


class TestDocumentValidation:
    """Test Document model validation."""
    
    def test_document_create(self):
        """Test document creation."""
        doc = DocumentCreateModel(
            title="User Guide",
            user_id="user-123",
        )
        assert doc.title == "User Guide"
    
    def test_document_search_top_k_validation(self):
        """Test top_k range validation (1-50)."""
        with pytest.raises(ValidationError):
            DocumentSearchModel(
                text="search query",
                top_k=0,
            )
        
        with pytest.raises(ValidationError):
            DocumentSearchModel(
                text="search query",
                top_k=51,
            )
        
        search = DocumentSearchModel(
            text="search query",
            top_k=10,
        )
        assert search.top_k == 10


class TestPromptValidation:
    """Test Prompt model validation."""
    
    def test_prompt_create(self):
        """Test prompt creation."""
        prompt = PromptCreateModel(
            label="Customer Support Prompt",
            interpolation_string="You are a helpful customer support agent. Always be polite and professional. Answer questions accurately. If you don't know something, say so. Provide helpful suggestions when possible.",
        )
        assert prompt.label == "Customer Support Prompt"
    
    def test_prompt_interpolation_string_min_length(self):
        """Test interpolation_string minimum length (128 chars)."""
        short_string = "You are helpful."
        with pytest.raises(ValidationError) as exc_info:
            PromptCreateModel(
                label="Short Prompt",
                interpolation_string=short_string,
            )
        assert "128" in str(exc_info.value)


class TestAuthorisationValidation:
    """Test Authorisation model validation."""
    
    def test_authorisation_oauth_requires_client_credentials(self):
        """Test that OAuth auth type requires client_id and client_secret."""
        with pytest.raises(ValidationError) as exc_info:
            AuthorisationCreateModel(
                name="OAuth Connection",
                auth_type=AuthType.OAUTH,
            )
        assert "client_id" in str(exc_info.value).lower()
    
    def test_authorisation_bearer_requires_token(self):
        """Test that bearer auth type requires token_secret."""
        with pytest.raises(ValidationError) as exc_info:
            AuthorisationCreateModel(
                name="Bearer Connection",
                auth_type=AuthType.BEARER,
            )
        assert "token" in str(exc_info.value).lower()
    
    def test_authorisation_apikey_requires_token(self):
        """Test that apikey auth type requires token_secret."""
        auth = AuthorisationCreateModel(
            name="API Key Connection",
            auth_type=AuthType.APIKEY,
            token_secret="my-secret-key",
        )
        assert auth.token_secret == "my-secret-key"


class TestHookValidation:
    """Test Hook model validation."""
    
    def test_hook_create(self):
        """Test hook creation."""
        hook = HookCreateModel(
            name="Pre-processing Hook",
            function_name="preprocess_input",
        )
        assert hook.name == "Pre-processing Hook"


class TestBenchmarkValidation:
    """Test Benchmark model validation."""
    
    def test_benchmark_create(self):
        """Test benchmark creation."""
        benchmark = BenchmarkCreateModel(
            name="Agent Performance Test",
            questions=[
                {"question": "What is 2+2?", "expected_answer": "4"},
                {"question": "Capital of France?", "expected_answer": "Paris"},
            ],
        )
        assert benchmark.name == "Agent Performance Test"


class TestConnectionValidation:
    """Test Connection model validation."""
    
    def test_connection_create(self):
        """Test connection creation."""
        conn = ConnectionCreateModel(
            name="Azure OpenAI Connection",
            type=HostingType.AZURE_OPENAI,
        )
        assert conn.type == HostingType.AZURE_OPENAI


class TestChatValidation:
    """Test Chat model validation."""
    
    def test_chat_create(self):
        """Test chat creation."""
        chat = ChatCreateModel(
            name="Support Chat",
            customer_id="customer-123",
        )
        assert chat.name == "Support Chat"
    
    def test_message_create(self):
        """Test message creation."""
        msg = MessageCreateModel(
            chat_id="chat-123",
            text="Hello, how can I help?",
        )
        assert msg.text == "Hello, how can I help?"


class TestChannelValidation:
    """Test Channel model validation."""
    
    def test_channel_create(self):
        """Test channel creation."""
        channel = ChannelCreateModel(
            name="WhatsApp Support",
            channel=ChannelType.WHATSAPP,
            provider=ChannelProvider.TWILIO,
        )
        assert channel.channel == ChannelType.WHATSAPP


class TestFolderValidation:
    """Test Folder model validation."""
    
    def test_folder_create(self):
        """Test folder creation."""
        folder = FolderCreateModel(
            name="Documentation",
        )
        assert folder.name == "Documentation"


class TestEntityValidation:
    """Test Entity model validation."""
    
    def test_entity_create(self):
        """Test entity creation."""
        entity = EntityCreateModel(
            label="Product Category",
            entity_type=EntityType.TOPIC,
        )
        assert entity.entity_type == EntityType.TOPIC
    
    def test_entity_hex_color_validation(self):
        """Test hex color validation for background_color."""
        with pytest.raises(ValidationError):
            EntityCreateModel(
                label="Test Entity",
                entity_type=EntityType.TOPIC,
                background_color="red",
            )


class TestMemberValidation:
    """Test Member model validation."""
    
    def test_member_create(self):
        """Test member creation."""
        member = MemberCreateModel(
            user_id="user-123",
            role=MemberRole.ADMIN,
        )
        assert member.role == MemberRole.ADMIN


class TestSecretValidation:
    """Test Secret model validation."""
    
    def test_secret_create(self):
        """Test secret creation."""
        secret = SecretCreateModel(
            authorisation_id="auth-123",
            password_secret_value="my-secret-value",
        )
        assert secret.authorisation_id == "auth-123"


if __name__ == "__main__":
    pytest.main([__file__, "-v", "--tb=short", "--override-ini=addopts="])