# apps/aroflo_connector_app/agent/tool_schema.py
from __future__ import annotations

from typing import Any, Dict, List, Tuple
import hashlib
import re

TYPE_MAP = {
    "string": "string",
    "integer": "integer",
    "boolean": "boolean",
    "object": "object",
    "number": "number",
    "array": "array",
}



MAX_TOOL_NAME = 64
SAFE_RE = re.compile(r"[^a-zA-Z0-9_\-]+")

# Mapa global: tool_name (corto) -> (zone_code, op_code real)
TOOL_NAME_MAP: Dict[str, Tuple[str, str]] = {}


def _make_safe(s: str) -> str:
    return SAFE_RE.sub("_", s).strip("_")


def _short_tool_name(zone_code: str, op_code: str) -> str:
    """
    Crea un nombre <=64, estable y único:
    - Prefijo fijo: aroflo__{zone}__
    - Si op_code es muy largo, lo comprime con hash, pero mantiene algo legible.
    """
    zone_code = _make_safe(zone_code)
    op_code_safe = _make_safe(op_code)

    base = f"aroflo__{zone_code}__{op_code_safe}"
    if len(base) <= MAX_TOOL_NAME:
        return base

    # Mantener prefijo parseable y truncar op con hash
    prefix = f"aroflo__{zone_code}__"
    h = hashlib.sha1(op_code_safe.encode("utf-8")).hexdigest()[:8]

    # dejamos algo legible del op + hash
    # Ej: aroflo__invoices__get_approved_unprocessed_invoices_7f3a91c2
    # asegurando max 64
    keep = MAX_TOOL_NAME - len(prefix) - 1 - len(h)  # 1 para "_"
    op_part = op_code_safe[: max(0, keep)]
    return f"{prefix}{op_part}_{h}"


def build_tools_from_manifest(manifest: Dict[str, Any]) -> List[Dict[str, Any]]:
    tools: List[Dict[str, Any]] = []

    # limpiar mapa para evitar residuos en ejecuciones repetidas
    TOOL_NAME_MAP.clear()

    for zone in manifest.get("zones", []):
        zone_code = zone["code"]

        for op in zone.get("operations", []):
            op_code = op["code"]
            tool_name = _short_tool_name(zone_code, op_code)

            # registrar mapping para ejecución real
            TOOL_NAME_MAP[tool_name] = (zone_code, op_code)

            properties: Dict[str, Any] = {}
            required: List[str] = []

            for p in op.get("params", []):
                p_type = TYPE_MAP.get(p.get("type", "string"), "string")

                prop: Dict[str, Any] = {
                    "type": p_type,
                    "description": p.get("description", "") or "",
                }

                # NUEVO: arrays con items
                if p_type == "array":
                    items_schema = p.get("items_schema")
                    if isinstance(items_schema, dict):
                        # usamos exactamente lo que la zona declaró
                        prop["items"] = items_schema
                    else:
                        # fallback seguro: lista sin imponer estructura
                        prop["items"] = {}

                # enum si aplica
                if p.get("enum"):
                    prop["enum"] = p["enum"]

                properties[p["name"]] = prop

                if p.get("required"):
                    required.append(p["name"])

            tools.append(
                {
                    "type": "function",
                    "name": tool_name,
                    "description": op.get("description", "") or "",
                    "parameters": {
                        "type": "object",
                        "properties": properties,
                        "required": required,  # siempre presente
                        "additionalProperties": False,
                    },
                }
            )

    return tools


