"""Pydantic models for AgentFunction validation.

Validates function creation with:
- JSON Schema format for parameters
- Auth type dependent fields
- URL format validation
"""

from typing import Any, Dict, List, Optional

from pydantic import Field, field_validator, model_validator

from .base import ToothFairyModel
from .enums import AuthType, RequestType
from .validators import validate_json_schema, validate_url


class AgentFunctionCreateModel(ToothFairyModel):
    """Validation model for creating a new agent function.
    
    Required fields:
        - name: Function name
        - description: Function purpose description
    
    Auth type constraints:
        - oauth: Requires authorisation_id
        - bearer/apikey: Requires authorisation_key
        - none: No auth fields needed
    
    Parameters format:
        MUST use JSON Schema: {"type": "object", "properties": {...}, "required": [...]}
        NOT deprecated list format: [{"name": "...", "type": "..."}]
    """
    
    name: str = Field(..., min_length=1, max_length=200)
    description: str = Field(..., min_length=1)
    
    url: Optional[str] = None
    request_type: Optional[RequestType] = None
    authorisation_type: AuthType = AuthType.NONE
    authorisation_key: Optional[str] = None
    authorisation_id: Optional[str] = None
    parameters: Optional[Dict[str, Any]] = None
    model: Optional[str] = None
    depends_on_before_execution: Optional[List[str]] = None
    depends_on_after_execution: Optional[List[str]] = None
    params_as_path: Optional[bool] = None
    scope: Optional[str] = None
    json_path_extractor: Optional[str] = None
    headers: Optional[Dict[str, Any]] = None
    static_args: Optional[Dict[str, Any]] = None
    custom_execution_code: Optional[str] = None
    is_mcp_server: Optional[bool] = None
    is_callback_fn: Optional[bool] = None
    is_database_script: Optional[bool] = None
    is_html_fn: Optional[bool] = None
    is_chat_fn: Optional[bool] = None
    is_hand_off_fn: Optional[bool] = None
    is_code_template: Optional[bool] = None
    is_agent_skill: Optional[bool] = None
    code_execution_environment_id: Optional[str] = None
    on_fn_call: Optional[Dict[str, Any]] = None
    hand_off_agent: Optional[str] = None
    hand_off_to_original_agent_on_completion: Optional[bool] = None
    hand_off_to_original_agent_on_failure: Optional[bool] = None
    hand_off_agent_on_completion: Optional[str] = None
    hand_off_agent_on_failure: Optional[str] = None
    agent_skill_script: Optional[str] = None
    chat_script: Optional[str] = None
    chat_action: Optional[str] = None
    html_url: Optional[str] = None
    html_script: Optional[str] = None
    is_html_redirect_fn: Optional[bool] = None
    html_form_on_successful_submit_message: Optional[str] = None
    html_form_on_failed_submit_message: Optional[str] = None
    db_schema_metadata: Optional[Dict[str, Any]] = None
    pre_execution_script: Optional[str] = None
    post_execution_script: Optional[str] = None
    db_script: Optional[str] = None
    db_connection_id: Optional[str] = None
    pre_execution_fn_id: Optional[str] = None
    pre_execution_mapping: Optional[Dict[str, Any]] = None
    oauth_connection_id: Optional[str] = None
    handed_off_to_human_on_call: Optional[bool] = None
    graphql_query: Optional[str] = None
    graphql_mutation: Optional[str] = None
    graphql_operation_name: Optional[str] = None
    users_to_hand_off_to: Optional[List[str]] = None
    is_global: Optional[bool] = None
    
    _validate_url = field_validator("url")(validate_url)
    _validate_html_url = field_validator("html_url")(validate_url)
    _validate_parameters = field_validator("parameters")(validate_json_schema)
    
    @model_validator(mode="after")
    def validate_auth_dependencies(self) -> "AgentFunctionCreateModel":
        auth_type = self.authorisation_type
        
        if auth_type == AuthType.OAUTH:
            if not self.authorisation_id and not self.oauth_connection_id:
                raise ValueError(
                    "oauth auth type requires authorisation_id or oauth_connection_id"
                )
        
        if auth_type in (AuthType.BEARER, AuthType.APIKEY, AuthType.BASIC):
            if not self.authorisation_key and not self.authorisation_id:
                pass
        
        return self
    
    def to_api_payload(self, exclude_none: bool = True) -> Dict[str, Any]:
        payload = super().to_api_payload(exclude_none=exclude_none)
        
        special_mappings = {
            "authorisation_id": "authorisationID",
            "code_execution_environment_id": "codeExecutionEnvironmentID",
            "db_connection_id": "dbConnectionID",
            "pre_execution_fn_id": "preExecutionFnID",
            "oauth_connection_id": "OAuthConnectionID",
            "is_mcp_server": "isMCPServer",
        }
        
        for snake_key, camel_key in special_mappings.items():
            if snake_key in payload:
                payload[camel_key] = payload.pop(snake_key)
        
        standard_mappings = [
            ("request_type", "requestType"),
            ("authorisation_type", "authorisationType"),
            ("authorisation_key", "authorisationKey"),
            ("depends_on_before_execution", "dependsOnBeforeExecution"),
            ("depends_on_after_execution", "dependsOnAfterExecution"),
            ("params_as_path", "paramsAsPath"),
            ("json_path_extractor", "jsonPathExtractor"),
            ("static_args", "staticArgs"),
            ("custom_execution_code", "customExecutionCode"),
            ("is_callback_fn", "isCallbackFn"),
            ("is_database_script", "isDatabaseScript"),
            ("is_html_fn", "isHtmlFn"),
            ("is_chat_fn", "isChatFn"),
            ("is_hand_off_fn", "isHandOffFn"),
            ("is_code_template", "isCodeTemplate"),
            ("is_agent_skill", "isAgentSkill"),
            ("on_fn_call", "onFnCall"),
            ("hand_off_agent", "handOffAgent"),
            ("hand_off_to_original_agent_on_completion", "handOffToOriginalAgentOnCompletion"),
            ("hand_off_to_original_agent_on_failure", "handOffToOriginalAgentOnFailure"),
            ("hand_off_agent_on_completion", "handOffAgentOnCompletion"),
            ("hand_off_agent_on_failure", "handOffAgentOnFailure"),
            ("agent_skill_script", "agentSkillScript"),
            ("chat_script", "chatScript"),
            ("chat_action", "chatAction"),
            ("html_url", "htmlUrl"),
            ("html_script", "htmlScript"),
            ("is_html_redirect_fn", "isHtmlRedirectFn"),
            ("html_form_on_successful_submit_message", "htmlFormOnSuccessfulSubmitMessage"),
            ("html_form_on_failed_submit_message", "htmlFormOnFailedSubmitMessage"),
            ("db_schema_metadata", "dbSchemaMetadata"),
            ("pre_execution_script", "preExecutionScript"),
            ("post_execution_script", "postExecutionScript"),
            ("db_script", "dbScript"),
            ("pre_execution_mapping", "preExecutionMapping"),
            ("handed_off_to_human_on_call", "handedOffToHumanOnCall"),
            ("graphql_query", "graphqlQuery"),
            ("graphql_mutation", "graphqlMutation"),
            ("graphql_operation_name", "graphqlOperationName"),
            ("users_to_hand_off_to", "usersToHandOffTo"),
            ("is_global", "isGlobal"),
        ]
        
        for snake_key, camel_key in standard_mappings:
            if snake_key in payload:
                payload[camel_key] = payload.pop(snake_key)
        
        return payload


class AgentFunctionUpdateModel(ToothFairyModel):
    """Validation model for updating an existing agent function.
    
    All fields are optional - only provided fields will be updated.
    """
    
    agent_function_id: str = Field(..., min_length=1)
    
    name: Optional[str] = Field(None, min_length=1, max_length=200)
    description: Optional[str] = Field(None, min_length=1)
    url: Optional[str] = None
    request_type: Optional[RequestType] = None
    authorisation_type: Optional[AuthType] = None
    authorisation_key: Optional[str] = None
    authorisation_id: Optional[str] = None
    parameters: Optional[Dict[str, Any]] = None
    
    _validate_url = field_validator("url")(validate_url)
    _validate_parameters = field_validator("parameters")(validate_json_schema)
    
    def to_api_payload(self, exclude_none: bool = True) -> Dict[str, Any]:
        payload = super().to_api_payload(exclude_none=exclude_none)
        
        if "agent_function_id" in payload:
            payload["id"] = payload.pop("agent_function_id")
        
        special_mappings = {
            "authorisation_id": "authorisationID",
            "is_mcp_server": "isMCPServer",
        }
        
        for snake_key, camel_key in special_mappings.items():
            if snake_key in payload:
                payload[camel_key] = payload.pop(snake_key)
        
        standard_mappings = [
            ("request_type", "requestType"),
            ("authorisation_type", "authorisationType"),
            ("authorisation_key", "authorisationKey"),
        ]
        
        for snake_key, camel_key in standard_mappings:
            if snake_key in payload:
                payload[camel_key] = payload.pop(snake_key)
        
        return payload