# retoor <retoor@molodetz.nl>

from __future__ import annotations

from dataclasses import dataclass, field
from functools import lru_cache
from typing import Any, Literal

ParamLocation = Literal["path", "query", "body", "file"]


@dataclass(frozen=True)
class Param:
    name: str
    location: ParamLocation
    description: str
    required: bool = False
    type: str = "string"


@dataclass(frozen=True)
class Action:
    name: str
    method: str
    path: str
    summary: str
    description: str = ""
    params: tuple[Param, ...] = ()
    requires_auth: bool = True
    requires_admin: bool = False
    requires_primary_admin: bool = False
    handler: Literal[
        "http",
        "login",
        "logout",
        "status",
        "task",
        "agentic",
        "avatar",
        "client",
        "fetch",
        "docs",
        "cost",
        "chunks",
        "rsearch",
        "container",
        "workspace",
        "customization",
        "notification",
        "behavior",
        "virtual_tool",
        "email",
        "telegram",
        "interaction",
    ] = "http"
    freeform_body: bool = False
    ajax: bool = False
    read_only: bool = False

    @property
    def is_read_only(self) -> bool:
        return self.read_only or self.method.upper() == "GET"

    def tool_schema(self) -> dict[str, Any]:
        properties: dict[str, Any] = {}
        required: list[str] = []
        for param in self.params:
            if param.type == "array":
                properties[param.name] = {
                    "type": "array",
                    "items": {"type": "object"},
                    "description": param.description,
                }
            elif param.type == "object":
                properties[param.name] = {
                    "type": "object",
                    "description": param.description,
                    "additionalProperties": True,
                }
            else:
                properties[param.name] = {
                    "type": param.type,
                    "description": param.description,
                }
            if param.required:
                required.append(param.name)
        if self.freeform_body:
            properties["form_fields"] = {
                "type": "object",
                "description": "Additional form fields as key/value string pairs.",
                "additionalProperties": {"type": "string"},
            }
        text = (
            self.summary
            if not self.description
            else f"{self.summary}. {self.description}"
        )
        return {
            "type": "function",
            "function": {
                "name": self.name,
                "description": text,
                "parameters": {
                    "type": "object",
                    "properties": properties,
                    "required": required,
                    "additionalProperties": False,
                },
            },
        }


@dataclass(frozen=True)
class Catalog:
    actions: tuple[Action, ...] = field(default_factory=tuple)

    def by_name(self) -> dict[str, Action]:
        return {action.name: action for action in self.actions}

    def tool_schemas(self) -> list[dict[str, Any]]:
        return [action.tool_schema() for action in self.actions]

    @lru_cache(maxsize=16)
    def _schemas_for(
        self,
        authenticated: bool,
        is_admin: bool,
        is_primary_admin: bool,
    ) -> tuple[dict[str, Any], ...]:
        return tuple(
            action.tool_schema()
            for action in self.actions
            if (authenticated or not action.requires_auth)
            and (is_admin or not action.requires_admin)
            and (is_primary_admin or not action.requires_primary_admin)
        )

    def tool_schemas_for(
        self,
        authenticated: bool,
        is_admin: bool = False,
        is_primary_admin: bool = False,
    ) -> list[dict[str, Any]]:
        return list(
            self._schemas_for(bool(authenticated), bool(is_admin), bool(is_primary_admin))
        )
