"""
Module containing type aliases and enums
"""

import enum
from dataclasses import dataclass, field
from typing import Any, ClassVar, Literal, TypeAlias, TypedDict

from pydantic import BaseModel

FunctionLocation = tuple[str, str]
"""
Location of the function. First element specifies the Python module path, second element the
Python function name.
"""

FunctionArguments = dict[str, str]
"""Arguments to the function."""


class ContentType(enum.Enum):
    """
    HTTP content types which are used in the Content-Type headers
    """

    FORM = "application/x-www-form-urlencoded"
    HTML = "text/html"
    JSON = "application/json"


class RequestMethod(enum.Enum):
    """
    Enum with valid HTTP methods
    """

    GET = "GET"
    POST = "POST"
    PATCH = "PATCH"
    DELETE = "DELETE"
    PUT = "PUT"
    OPTIONS = "OPTIONS"


class ResponseCode(enum.Enum):
    """
    HTTP response codes to return
    """

    OK = 200
    NO_CONTENT = 204
    BAD_REQUEST = 400
    UNAUTHORIZED = 401
    METHOD_NOT_ALLOWED = 405
    INTERNAL_ERROR = 500
    NOT_AVAILABLE = 503

    def status_line(self) -> str:
        """Produces a HTTP status line: <status-code> <message>"""
        return f"{self.value} {self.name.replace('_', ' ')}"


ObjectJsonSchema: TypeAlias = dict[str, Any]
"""Type representing JSON schema of an object, e.g. where `"type": "object"`.

This type is used to define agents tools input and output schema.

With PEP-728 this should be a TypedDict with `type: Literal['object']`, and `extra_parts=Any`
"""


class ModuleLoadIssue(TypedDict, total=True):
    """Definition for a module load issue."""

    type: str
    """The type of the issue."""
    error: str
    """The error message."""
    module_name: str
    """The name of the module."""


class ToolIssue(TypedDict, total=True):
    """Definition for a tool issue."""

    type: str
    """The type of the issue."""
    error: str
    """The error message."""


@dataclass(kw_only=True)
class Schema:
    """Definition for input or output schema of a tool"""

    schema: ObjectJsonSchema | None = field(default=None)
    """The JSON Schema object defining the expected parameters for the tool."""
    model: type[BaseModel] | None = field(default=None)
    """The Pydantic model for the input or output schema."""
    issue: ToolIssue | None = field(default=None)
    """The issue on schema generation."""


class ToolDefinition(TypedDict):
    """API Tool Definition for the Agentic Tools API"""

    name: str
    """The name of the tool."""
    description: str
    """A human-readable description of the tool."""
    requires_human_permission: bool
    """Whether the tool requires human permission to be called."""
    inputSchema: ObjectJsonSchema | None
    """A JSON Schema object defining the expected parameters for the tool."""
    outputSchema: ObjectJsonSchema | None
    """An optional JSON Schema object defining the structure of the tool's output returned in
    the structuredContent field of a CallToolResult."""
    meta: dict[str, Any] | None
    """Metadata for the tool."""
    issues: list[ToolIssue] | None
    """The issues with the tool."""


@dataclass(kw_only=True)
class Tool:
    """Definition for a tool the client can call."""

    name: str
    """The name of the tool."""
    description: str | None = field(default=None)
    """A human-readable description of the tool."""
    requires_human_permission: bool = field(default=False)
    """Whether the tool requires human permission to be called."""
    examples: list[str] | None = field(default=None)
    """Examples of how to use the tool."""
    input_schema: ObjectJsonSchema | None = field(default=None)
    """A JSON Schema object defining the expected parameters for the tool."""
    output_schema: ObjectJsonSchema | None = field(default=None)
    """
    An optional JSON Schema object defining the structure of the tool's output
    returned in the structuredContent field of a CallToolResult.
    """
    meta: dict[str, Any] | None = field(default=None)
    """
    metadata
    """

    module_name: str | None = field(default=None)
    """The name of the module the tool is defined in."""

    issues: list[ToolIssue] | None = field(default=None)
    """The issues with the tool."""
    input_model: type[BaseModel] | None = field(default=None)
    """The Pydantic model class for the tool input."""
    output_model: type[BaseModel] | None = field(default=None)
    """The Pydantic model class for the tool output."""

    @property
    def is_ready(self) -> bool:
        """Check if the tool is ready for AI agent."""
        return self.description is not None and self.input_schema is not None

    def to_dict(self) -> ToolDefinition:
        """
        Convert the tool to a dictionary.
        """

        examples = "examples:\n" + "\n".join(self.examples) if self.examples else ""

        if self.examples is not None and self.description is not None:
            description = self.description + "\n" + examples
        elif self.examples is not None and self.description is None:
            description = examples
        else:
            description = self.description or ""

        return ToolDefinition(
            name=self.module_name + "." + self.name if self.module_name else self.name,
            description=description,
            requires_human_permission=self.requires_human_permission,
            inputSchema=self.input_schema,
            outputSchema=self.output_schema,
            meta=self.meta,
            issues=self.issues,
        )


class ListToolsResponse(TypedDict, total=True):
    """
    The expected type for a response for the
    List Agent Tools API
    """

    result: Literal["success"]
    data: list[ToolDefinition]


class ListToolsIssuesResponse(TypedDict, total=True):
    """
    The expected type for a response for the
    List Agent Tools Issues API
    """

    result: Literal["success"]
    data: list[ToolDefinition | ModuleLoadIssue]


class AppDescription:
    """App Description class"""

    _instance: "ClassVar[AppDescription | None]" = None

    description: str | None = None
    """A human-readable description of the app."""
    short_name: str | None = None
    """The short name of the app."""

    def __new__(
        cls, description: str | None = None, short_name: str | None = None
    ) -> "AppDescription":
        """Create the app description singleton."""
        if cls._instance is not None and (
            cls._instance.description != description or cls._instance.short_name != short_name
        ):
            raise ValueError("App description already created")
        cls._instance = super().__new__(cls)
        cls._instance.description = description
        cls._instance.short_name = short_name

        return cls._instance

    @classmethod
    def get(cls) -> "AppDescription":
        """Get the app description singleton."""
        if cls._instance is None:
            raise ValueError("App description not set")
        return cls._instance


class AppDescriptionData(TypedDict, total=True):
    """App Description"""

    description: str | None
    shortName: str | None


class AppDescriptionResponse(TypedDict, total=True):
    """App Description"""

    result: Literal["success"]
    data: AppDescriptionData
