"""Pydantic models for Agent validation.

Validates agent creation/update with:
- Mode-specific constraints (agenticRAG, hasCode, charting)
- Range validation (temperature, max_tokens, top_k)
- Field mapping handling for API payload construction
"""

from typing import Any, Dict, List, Optional

from pydantic import Field, field_validator, model_validator

from .base import ToothFairyModel
from .enums import AgentMode, AgentType, Department, ReasoningEffort, ReasoningMode
from .validators import (
    validate_temperature,
    validate_max_tokens,
    validate_top_k,
    validate_hex_color,
)


class AgentCreateModel(ToothFairyModel):
    """Validation model for creating a new agent.
    
    Required fields:
        - label: Agent name
        - mode: Operating mode (retriever, coder, chatter, planner, computer, voice)
        - interpolation_string: System prompt (min 128 chars)
        - goals: Agent objectives
        - temperature: Randomness (0.001-1.0)
        - max_tokens: Max response tokens (positive)
        - max_history: Conversation history limit
        - top_k: Top results for retrieval (1-50)
        - doc_top_k: Top documents to retrieve (1-50)
    
    Mode-specific constraints:
        - retriever: Can enable agenticRAG, hasCode, charting
        - coder: Requires hasCode=true, CANNOT have agenticRAG
        - chatter: agenticRAG must be false
        - voice: NEVER enable agenticRAG (latency-sensitive)
    """
    
    label: str = Field(..., min_length=1, max_length=200)
    mode: AgentMode
    interpolation_string: str = Field(..., min_length=128)
    goals: str = Field(..., min_length=1)
    temperature: float = Field(..., ge=0.001, le=1.0)
    max_tokens: int = Field(..., gt=0)
    max_history: int = Field(..., ge=0)
    top_k: int = Field(..., ge=1, le=50)
    doc_top_k: int = Field(..., ge=1, le=50)
    
    description: Optional[str] = None
    pertinence_passage: Optional[str] = None
    inhibition_passage: Optional[str] = None
    default_answer: Optional[str] = None
    no_knowledge_default_answer: Optional[str] = None
    subject: Optional[str] = None
    max_topics: Optional[int] = None
    min_retrieval_score: Optional[float] = None
    allowed_topics: Optional[List[str]] = None
    static_docs: Optional[List[str]] = None
    has_topics_context: Optional[bool] = None
    topic_enhancer: Optional[bool] = None
    key_words_for_knowledge_base: Optional[bool] = None
    summarisation: Optional[bool] = None
    compressor: Optional[bool] = None
    has_memory: Optional[bool] = None
    is_long_term_memory_enabled: Optional[bool] = None
    has_functions: Optional[bool] = None
    agent_functions: Optional[List[str]] = None
    llm_provider: Optional[str] = None
    llm_base_model: Optional[str] = None
    function_calling_provider: Optional[str] = None
    function_calling_model: Optional[str] = None
    has_moderation: Optional[bool] = None
    moderation_message: Optional[str] = None
    has_ner: Optional[bool] = None
    show_agent_name: Optional[bool] = None
    hide_reasoning: Optional[bool] = None
    plain_text_output: Optional[bool] = None
    extended_output: Optional[bool] = None
    show_citations: Optional[bool] = None
    icon: Optional[str] = None
    color: Optional[str] = None
    placeholder_input_message: Optional[str] = None
    message_on_launch: Optional[str] = None
    disclaimer: Optional[str] = None
    quick_questions: Optional[str] = None
    prevent_widget_usage: Optional[bool] = None
    allow_feedback: Optional[bool] = None
    restricted_access: Optional[bool] = None
    restricted_access_users: Optional[List[str]] = None
    is_multi_language_enabled: Optional[bool] = None
    advanced_language_detection: Optional[bool] = None
    allow_email_to_agent: Optional[bool] = None
    communication_services: Optional[List[str]] = None
    charting: Optional[bool] = None
    allow_images_upload: Optional[bool] = None
    allow_audios_upload: Optional[bool] = None
    allow_docs_upload: Optional[bool] = None
    has_images: Optional[bool] = None
    prompt_top_keywords: Optional[int] = None
    agentic_rag: Optional[bool] = None
    re_rank: Optional[bool] = None
    blender: Optional[bool] = None
    allow_internet_search: Optional[bool] = None
    allow_deep_internet_search: Optional[bool] = None
    has_code: Optional[bool] = None
    allow_external_api: Optional[bool] = None
    allow_images_generation: Optional[bool] = None
    allow_videos_generation: Optional[bool] = None
    chain_of_thoughts: Optional[bool] = None
    agents_pool: Optional[List[str]] = None
    max_planning_steps: Optional[int] = None
    planning_instructions: Optional[str] = None
    voice_name: Optional[str] = None
    voice_instructions: Optional[str] = None
    reasoning_budget: Optional[int] = None
    reasoning_effort: Optional[ReasoningEffort] = None
    
    _validate_temperature = field_validator("temperature")(validate_temperature)
    _validate_max_tokens = field_validator("max_tokens")(validate_max_tokens)
    _validate_top_k = field_validator("top_k")(validate_top_k)
    _validate_doc_top_k = field_validator("doc_top_k")(validate_top_k)
    _validate_color = field_validator("color")(validate_hex_color)
    
    @model_validator(mode="after")
    def validate_mode_constraints(self) -> "AgentCreateModel":
        mode = self.mode
        agentic_rag = self.agentic_rag
        has_code = self.has_code
        
        if mode == AgentMode.CODER:
            if agentic_rag is True:
                raise ValueError(
                    "coder mode CANNOT have agenticRAG enabled. "
                    "Set agentic_rag=False or None."
                )
            if has_code is not True:
                raise ValueError(
                    "coder mode REQUIRES hasCode=True. "
                    "Code execution must be enabled for coder agents."
                )
        
        if mode == AgentMode.VOICE:
            if agentic_rag is True:
                raise ValueError(
                    "voice mode CANNOT have agenticRAG enabled (latency-sensitive). "
                    "Set agentic_rag=False or None."
                )
        
        if mode == AgentMode.CHATTER:
            if agentic_rag is True:
                raise ValueError(
                    "chatter mode is for creative responses. "
                    "Set agentic_rag=False or None."
                )
        
        return self
    
    def to_api_payload(self, exclude_none: bool = True) -> Dict[str, Any]:
        payload = super().to_api_payload(exclude_none=exclude_none)
        
        field_mappings = {
            "has_ner": "hasNER",
            "agentic_rag": "agenticRAG",
            "allow_external_api": "allowExternalAPI",
        }
        
        for snake_key, camel_key in field_mappings.items():
            if snake_key in payload:
                payload[camel_key] = payload.pop(snake_key)
        
        standard_mappings = [
            ("interpolation_string", "interpolationString"),
            ("max_tokens", "maxTokens"),
            ("max_history", "maxHistory"),
            ("top_k", "topK"),
            ("doc_top_k", "docTopK"),
            ("pertinence_passage", "pertinencePassage"),
            ("inhibition_passage", "inhibitionPassage"),
            ("default_answer", "defaultAnswer"),
            ("no_knowledge_default_answer", "noKnowledgeDefaultAnswer"),
            ("min_retrieval_score", "minRetrievalScore"),
            ("allowed_topics", "allowedTopics"),
            ("static_docs", "staticDocs"),
            ("has_topics_context", "hasTopicsContext"),
            ("topic_enhancer", "topicEnhancer"),
            ("key_words_for_knowledge_base", "keyWordsForKnowledgeBase"),
            ("is_long_term_memory_enabled", "isLongTermMemoryEnabled"),
            ("has_functions", "hasFunctions"),
            ("agent_functions", "agentFunctions"),
            ("llm_provider", "llmProvider"),
            ("llm_base_model", "llmBaseModel"),
            ("function_calling_provider", "functionCallingProvider"),
            ("function_calling_model", "functionCallingModel"),
            ("has_moderation", "hasModeration"),
            ("moderation_message", "moderationMessage"),
            ("show_agent_name", "showAgentName"),
            ("hide_reasoning", "hideReasoning"),
            ("plain_text_output", "plainTextOutput"),
            ("extended_output", "extendedOutput"),
            ("show_citations", "showCitations"),
            ("placeholder_input_message", "placeholderInputMessage"),
            ("message_on_launch", "messageOnLaunch"),
            ("quick_questions", "quickQuestions"),
            ("prevent_widget_usage", "preventWidgetUsage"),
            ("allow_feedback", "allowFeedback"),
            ("restricted_access", "restrictedAccess"),
            ("restricted_access_users", "restrictedAccessUsers"),
            ("is_multi_language_enabled", "isMultiLanguageEnabled"),
            ("advanced_language_detection", "advancedLanguageDetection"),
            ("allow_email_to_agent", "allowEmailToAgent"),
            ("communication_services", "communicationServices"),
            ("allow_images_upload", "allowImagesUpload"),
            ("allow_audios_upload", "allowAudiosUpload"),
            ("allow_docs_upload", "allowDocsUpload"),
            ("has_images", "hasImages"),
            ("prompt_top_keywords", "promptTopKeywords"),
            ("re_rank", "reRank"),
            ("allow_internet_search", "allowInternetSearch"),
            ("allow_deep_internet_search", "allowDeepInternetSearch"),
            ("has_code", "hasCode"),
            ("allow_external_api", "allowExternalAPI"),
            ("allow_images_generation", "allowImagesGeneration"),
            ("allow_videos_generation", "allowVideosGeneration"),
            ("chain_of_thoughts", "chainOfThoughts"),
            ("agents_pool", "agentsPool"),
            ("max_planning_steps", "maxPlanningSteps"),
            ("planning_instructions", "planningInstructions"),
            ("voice_name", "voiceName"),
            ("voice_instructions", "voiceInstructions"),
            ("reasoning_budget", "reasoningBudget"),
            ("reasoning_effort", "reasoningEffort"),
        ]
        
        for snake_key, camel_key in standard_mappings:
            if snake_key in payload:
                payload[camel_key] = payload.pop(snake_key)
        
        return payload


class AgentUpdateModel(ToothFairyModel):
    """Validation model for updating an existing agent.
    
    All fields are optional - only provided fields will be updated.
    Mode constraints are NOT re-validated on update (mode cannot be changed).
    """
    
    agent_id: str = Field(..., min_length=1)
    
    label: Optional[str] = Field(None, min_length=1, max_length=200)
    description: Optional[str] = None
    temperature: Optional[float] = Field(None, ge=0.001, le=1.0)
    max_tokens: Optional[int] = Field(None, gt=0)
    max_history: Optional[int] = Field(None, ge=0)
    top_k: Optional[int] = Field(None, ge=1, le=50)
    doc_top_k: Optional[int] = Field(None, ge=1, le=50)
    charting: Optional[bool] = None
    
    _validate_temperature = field_validator("temperature")(validate_temperature)
    _validate_max_tokens = field_validator("max_tokens")(validate_max_tokens)
    _validate_top_k = field_validator("top_k")(validate_top_k)
    _validate_doc_top_k = field_validator("doc_top_k")(validate_top_k)
    
    def to_api_payload(self, exclude_none: bool = True) -> Dict[str, Any]:
        payload = super().to_api_payload(exclude_none=exclude_none)
        
        if "agent_id" in payload:
            payload["id"] = payload.pop("agent_id")
        
        standard_mappings = [
            ("max_tokens", "maxTokens"),
            ("max_history", "maxHistory"),
            ("top_k", "topK"),
            ("doc_top_k", "docTopK"),
        ]
        
        for snake_key, camel_key in standard_mappings:
            if snake_key in payload:
                payload[camel_key] = payload.pop(snake_key)
        
        return payload