# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""
Node Type Registry for graphicalEditor - static node definitions (ai, email, sharepoint, trigger, flow, data, input).
Nodes are defined first; IO/method actions are used at execution time.
"""

import logging
from typing import Dict, List, Any

from modules.features.graphicalEditor.nodeDefinitions import STATIC_NODE_TYPES
from modules.features.graphicalEditor.portTypes import PORT_TYPE_CATALOG, SYSTEM_VARIABLES
from modules.shared.i18nRegistry import normalizePrimaryLanguageTag, resolveText

logger = logging.getLogger(__name__)


def getNodeTypes(
    services: Any = None,
    language: str = "de",
) -> List[Dict[str, Any]]:
    """
    Return static node types. No dynamic I/O derivation from methodDiscovery.
    services: Optional (kept for API compatibility, not used).
    """
    return list(STATIC_NODE_TYPES)


def _pickFromLangMap(d: Any, lang: str) -> Any:
    """Resolve multilingual dict: ``lang`` → ``xx`` → ``de`` → ``en`` → first non-empty value."""
    if not isinstance(d, dict) or not d:
        return None
    for k in (lang, "xx", "de", "en"):
        v = d.get(k)
        if v is not None and v != "":
            return v
    for v in d.values():
        if v is not None and v != "":
            return v
    return None


def _localizeNode(node: Dict[str, Any], language: str) -> Dict[str, Any]:
    """Apply request language via resolveText (t() keys + multilingual dicts)."""
    lang = normalizePrimaryLanguageTag(language, "en")
    out = dict(node)
    for key in list(out.keys()):
        if key.startswith("_"):
            del out[key]
    lbl = node.get("label")
    if lbl is not None:
        out["label"] = resolveText(lbl, lang) or node.get("id", "")
    desc = node.get("description")
    if desc is not None:
        out["description"] = resolveText(desc, lang)
    ol = node.get("outputLabels")
    if ol is not None:
        if isinstance(ol, list):
            out["outputLabels"] = [resolveText(x, lang) for x in ol]
        elif isinstance(ol, dict) and ol:
            first = next(iter(ol.values()), None)
            if isinstance(first, (list, tuple)):
                picked = _pickFromLangMap(ol, lang)
                raw = list(picked) if picked is not None else list(first)
                out["outputLabels"] = [resolveText(x, lang) for x in raw]
    params = []
    for p in node.get("parameters", []):
        pc = dict(p)
        pd = p.get("description")
        if pd is not None:
            pc["description"] = resolveText(pd, lang)
        params.append(pc)
    out["parameters"] = params
    return out


def getNodeTypesForApi(
    services: Any,
    language: str = "de",
) -> Dict[str, Any]:
    """
    API-ready response: nodeTypes with localized strings, plus categories, portTypeCatalog, systemVariables.
    """
    nodes = getNodeTypes(services, language)
    localized = [_localizeNode(n, language) for n in nodes]
    categories = [
        {"id": "trigger", "label": "Trigger"},
        {"id": "input", "label": "Eingabe/Mensch"},
        {"id": "flow", "label": "Ablauf"},
        {"id": "data", "label": "Daten"},
        {"id": "ai", "label": "KI"},
        {"id": "file", "label": "Datei"},
        {"id": "email", "label": "E-Mail"},
        {"id": "sharepoint", "label": "SharePoint"},
        {"id": "clickup", "label": "ClickUp"},
        {"id": "trustee", "label": "Treuhand"},
    ]

    catalogSerialized = {}
    for name, schema in PORT_TYPE_CATALOG.items():
        catalogSerialized[name] = {
            "name": schema.name,
            "fields": [f.model_dump() for f in schema.fields],
        }

    return {
        "nodeTypes": localized,
        "categories": categories,
        "portTypeCatalog": catalogSerialized,
        "systemVariables": SYSTEM_VARIABLES,
    }


def getNodeTypeToMethodAction() -> Dict[str, tuple]:
    """
    Mapping from node type id to (method, action) for execution.
    Used by ActionNodeExecutor.
    """
    mapping = {}
    for node in STATIC_NODE_TYPES:
        method = node.get("_method")
        action = node.get("_action")
        if method and action:
            mapping[node["id"]] = (method, action)
    return mapping
