"""Agent Function manager for handling agent functions operations."""

from typing import TYPE_CHECKING, Any, Dict, List, Optional

from ..types import (
    AgentFunction,
    AgentFunctionChatAction,
    AuthorisationType,
    FunctionRequestType,
    FunctionScope,
    ListResponse,
)
from ..utils import convert_kwargs_to_api
from .field_mappings import AGENT_FUNCTION_FIELD_MAPPINGS, AGENT_FUNCTION_EXPLICIT_PARAMS

if TYPE_CHECKING:
    from ..client import ToothFairyClient


class AgentFunctionManager:
    """Manager for agent functions operations.

    This manager provides methods to create, update, and manage agent functions.

    Example:
        >>> client = ToothFairyClient(api_key="...", workspace_id="...")
        >>> agent_function = client.agent_functions.create(...)
    """

    def __init__(self, client: "ToothFairyClient"):
        """Initialize the AgentFunctionManager.

        Args:
            client: The ToothFairyClient instance.
        """
        self._client = client

    def create(
        self,
        name: str,
        description: str,
        url: Optional[str] = None,
        request_type: Optional[FunctionRequestType] = None,
        authorisation_type: AuthorisationType = "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[FunctionScope] = 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[AgentFunctionChatAction] = 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,
    ) -> AgentFunction:
        """Create a new agent function.

        Args:
            name: Human-readable name of the function.
            description: Detailed description of function purpose (required).
            url: Endpoint URL for the function.
            request_type: HTTP method (GET, POST, PUT, PATCH, DELETE).
            authorisation_type: Authentication method (none, bearer, apikey, basic, oauth2).
            authorisation_key: Authentication key or token.
            authorisation_id: ID of authorisation configuration.
            parameters: Function parameters schema (AWSJSON).
            model: Model to use for function execution.
            depends_on_before_execution: List of function IDs to execute before this function.
            depends_on_after_execution: List of function IDs to execute after this function.
            params_as_path: Whether to pass parameters as path parameters.
            scope: Function scope (workspace, global).
            json_path_extractor: JSONPath expression to extract data from response.
            headers: HTTP headers for the request (AWSJSON).
            static_args: Static arguments passed to function (AWSJSON).
            custom_execution_code: Custom code for function execution.
            is_mcp_server: Whether this is an MCP server.
            is_callback_fn: Whether this is a callback function.
            is_database_script: Whether this is a database script.
            is_html_fn: Whether this is an HTML function.
            is_chat_fn: Whether this is a chat function.
            is_hand_off_fn: Whether this is a hand-off function.
            is_code_template: Whether this is a code template.
            is_agent_skill: Whether this is an agent skill.
            code_execution_environment_id: ID of code execution environment.
            on_fn_call: Configuration for on function call (AWSJSON).
            hand_off_agent: ID of agent to hand off to.
            hand_off_to_original_agent_on_completion: Return to original agent on completion.
            hand_off_to_original_agent_on_failure: Return to original agent on failure.
            hand_off_agent_on_completion: ID of agent to hand off to on completion.
            hand_off_agent_on_failure: ID of agent to hand off to on failure.
            agent_skill_script: Script for agent skill execution.
            chat_script: Script for chat function.
            chat_action: Action for chat function (reply, silent, redirect).
            html_url: URL for HTML function.
            html_script: HTML script content.
            is_html_redirect_fn: Whether this is an HTML redirect function.
            html_form_on_successful_submit_message: Message on successful form submit.
            html_form_on_failed_submit_message: Message on failed form submit.
            db_schema_metadata: Database schema metadata (AWSJSON).
            pre_execution_script: Script to run before execution.
            post_execution_script: Script to run after execution.
            db_script: Database script content.
            db_connection_id: ID of database connection.
            pre_execution_fn_id: ID of pre-execution function.
            pre_execution_mapping: Mapping for pre-execution (AWSJSON).
            oauth_connection_id: ID of OAuth connection.
            handed_off_to_human_on_call: Whether to hand off to human on call.
            graphql_query: GraphQL query string.
            graphql_mutation: GraphQL mutation string.
            graphql_operation_name: GraphQL operation name.
            users_to_hand_off_to: List of user IDs to hand off to.
            is_global: Whether this function is global.

        Returns:
            The created AgentFunction object.
        """
        data: Dict[str, Any] = {
            "name": name,
            "description": description,
            "authorisationType": authorisation_type,
        }

        if url is not None:
            data["url"] = url
        if request_type is not None:
            data["requestType"] = request_type
        if authorisation_key is not None:
            data["authorisationKey"] = authorisation_key
        if authorisation_id is not None:
            data["authorisationID"] = authorisation_id
        if parameters is not None:
            data["parameters"] = parameters
        if model is not None:
            data["model"] = model
        if depends_on_before_execution is not None:
            data["dependsOnBeforeExecution"] = depends_on_before_execution
        if depends_on_after_execution is not None:
            data["dependsOnAfterExecution"] = depends_on_after_execution
        if params_as_path is not None:
            data["paramsAsPath"] = params_as_path
        if scope is not None:
            data["scope"] = scope
        if json_path_extractor is not None:
            data["jsonPathExtractor"] = json_path_extractor
        if headers is not None:
            data["headers"] = headers
        if static_args is not None:
            data["staticArgs"] = static_args
        if custom_execution_code is not None:
            data["customExecutionCode"] = custom_execution_code
        if is_mcp_server is not None:
            data["isMCPServer"] = is_mcp_server
        if is_callback_fn is not None:
            data["isCallbackFn"] = is_callback_fn
        if is_database_script is not None:
            data["isDatabaseScript"] = is_database_script
        if is_html_fn is not None:
            data["isHtmlFn"] = is_html_fn
        if is_chat_fn is not None:
            data["isChatFn"] = is_chat_fn
        if is_hand_off_fn is not None:
            data["isHandOffFn"] = is_hand_off_fn
        if is_code_template is not None:
            data["isCodeTemplate"] = is_code_template
        if is_agent_skill is not None:
            data["isAgentSkill"] = is_agent_skill
        if code_execution_environment_id is not None:
            data["codeExecutionEnvironmentID"] = code_execution_environment_id
        if on_fn_call is not None:
            data["onFnCall"] = on_fn_call
        if hand_off_agent is not None:
            data["handOffAgent"] = hand_off_agent
        if hand_off_to_original_agent_on_completion is not None:
            data["handOffToOriginalAgentOnCompletion"] = hand_off_to_original_agent_on_completion
        if hand_off_to_original_agent_on_failure is not None:
            data["handOffToOriginalAgentOnFailure"] = hand_off_to_original_agent_on_failure
        if hand_off_agent_on_completion is not None:
            data["handOffAgentOnCompletion"] = hand_off_agent_on_completion
        if hand_off_agent_on_failure is not None:
            data["handOffAgentOnFailure"] = hand_off_agent_on_failure
        if agent_skill_script is not None:
            data["agentSkillScript"] = agent_skill_script
        if chat_script is not None:
            data["chatScript"] = chat_script
        if chat_action is not None:
            data["chatAction"] = chat_action
        if html_url is not None:
            data["htmlUrl"] = html_url
        if html_script is not None:
            data["htmlScript"] = html_script
        if is_html_redirect_fn is not None:
            data["isHtmlRedirectFn"] = is_html_redirect_fn
        if html_form_on_successful_submit_message is not None:
            data["htmlFormOnSuccessfulSubmitMessage"] = html_form_on_successful_submit_message
        if html_form_on_failed_submit_message is not None:
            data["htmlFormOnFailedSubmitMessage"] = html_form_on_failed_submit_message
        if db_schema_metadata is not None:
            data["dbSchemaMetadata"] = db_schema_metadata
        if pre_execution_script is not None:
            data["preExecutionScript"] = pre_execution_script
        if post_execution_script is not None:
            data["postExecutionScript"] = post_execution_script
        if db_script is not None:
            data["dbScript"] = db_script
        if db_connection_id is not None:
            data["dbConnectionID"] = db_connection_id
        if pre_execution_fn_id is not None:
            data["preExecutionFnID"] = pre_execution_fn_id
        if pre_execution_mapping is not None:
            data["preExecutionMapping"] = pre_execution_mapping
        if oauth_connection_id is not None:
            data["OAuthConnectionID"] = oauth_connection_id
        if handed_off_to_human_on_call is not None:
            data["handedOffToHumanOnCall"] = handed_off_to_human_on_call
        if graphql_query is not None:
            data["graphqlQuery"] = graphql_query
        if graphql_mutation is not None:
            data["graphqlMutation"] = graphql_mutation
        if graphql_operation_name is not None:
            data["graphqlOperationName"] = graphql_operation_name
        if users_to_hand_off_to is not None:
            data["usersToHandOffTo"] = users_to_hand_off_to
        if is_global is not None:
            data["isGlobal"] = is_global

        response = self._client.request("POST", "/function/create", data=data)
        return AgentFunction.from_dict(response)

    def update(
        self,
        agent_function_id: str,
        **kwargs: Any,
    ) -> AgentFunction:
        """Update a agent_function.
        
        Fields can be passed using snake_case and will be converted to API format.
        You can also pass camelCase field names directly to bypass conversion.

        Args:
            agent_function_id: ID of the agent_function to update.
            **kwargs: Fields to update (snake_case or camelCase).

        Returns:
            The updated AgentFunction object.
        """
        data: Dict[str, Any] = {"id": agent_function_id}
        data.update(convert_kwargs_to_api(
            kwargs,
            AGENT_FUNCTION_FIELD_MAPPINGS,
            AGENT_FUNCTION_EXPLICIT_PARAMS
        ))
        response = self._client.request("POST", "/function/update", data=data)
        return AgentFunction.from_dict(response)

    def delete(self, agent_function_id: str) -> Dict[str, bool]:
        """Delete a agent_function.

        Args:
            agent_function_id: ID of the agent_function to delete.

        Returns:
            A dictionary with success status.
        """
        self._client.request("DELETE", f"/function/delete/{agent_function_id}")
        return {"success": True}

    def get(self, agent_function_id: str) -> AgentFunction:
        """Get a agent_function by ID.

        Args:
            agent_function_id: ID of the agent_function to retrieve.

        Returns:
            The AgentFunction object.
        """
        response = self._client.request("GET", f"/function/get/{agent_function_id}")
        return AgentFunction.from_dict(response)

    def list(
        self,
        limit: Optional[int] = None,
        offset: Optional[int] = None,
    ) -> ListResponse:
        """List all agent functions.

        Args:
            limit: Maximum number of agent functions to return.
            offset: Number of agent functions to skip.

        Returns:
            A ListResponse containing the agent functions.
        """
        params: Dict[str, Any] = {}
        if limit is not None:
            params["limit"] = limit
        if offset is not None:
            params["offset"] = offset

        response = self._client.request("GET", "/function/list", params=params)

        items = []
        if isinstance(response, list):
            items = [AgentFunction.from_dict(item) for item in response]
        elif isinstance(response, dict):
            items = [AgentFunction.from_dict(item) for item in response.get("items", [])]

        return ListResponse(items=items)

    def search(self, search_term: str) -> List[AgentFunction]:
        """Search agent functions by name.

        Args:
            search_term: Term to search for in function names.

        Returns:
            A list of matching AgentFunction objects.
        """
        all_functions = self.list()
        search_lower = search_term.lower()
        return [func for func in all_functions.items if search_lower in func.name.lower()]
