"""Shared catalog loading and property resolution utilities."""

import json
from importlib.resources import files
from typing import Any

# Load node catalog (shared across ND0301, ND0302, etc.)
_catalog_text = files("valn8n_lib.data").joinpath("node_index.json").read_text()
_catalog_data = json.loads(_catalog_text)
NODE_CATALOG: dict[str, dict[str, Any]] = {}
for _node in _catalog_data.get("nodes", []):
    NODE_CATALOG[_node["type"]] = {
        "display_name": _node.get("display_name", ""),
        "properties": _node.get("properties") or [],
        "properties_by_version": _node.get("properties_by_version") or {},
        "default_version": _node.get("default_version"),
        "credentials": _node.get("credentials") or [],
        "has_trigger": _node.get("has_trigger", False),
        "inputs": _node.get("inputs") or [],
    }


def resolve_property_defs(
    catalog_entry: dict[str, Any], type_version: Any
) -> list[dict[str, Any]] | None:
    """Select version-specific properties from catalog, or None to skip."""
    props_by_ver = catalog_entry.get("properties_by_version", {})
    if type_version is not None and props_by_ver:
        major = str(int(type_version))
        if major in props_by_ver:
            result: list[dict[str, Any]] = props_by_ver[major]
            return result
        default_ver = catalog_entry.get("default_version")
        default_major = str(int(default_ver)) if default_ver is not None else None
        if default_major is not None and major != default_major:
            return None
    return catalog_entry["properties"] or None


def build_prop_map(property_defs: list[dict[str, Any]]) -> dict[str, dict[str, Any]]:
    """Build property type map, merging duplicate collection/fixedCollection/options."""
    prop_map: dict[str, dict[str, Any]] = {}
    for prop_def in property_defs:
        if not isinstance(prop_def, dict) or "name" not in prop_def:
            continue
        name = prop_def["name"]
        prop_type = prop_def.get("type")
        if name in prop_map and prop_type in (
            "collection",
            "fixedCollection",
            "options",
            "multiOptions",
        ):
            existing_opts = prop_map[name].get("options", [])
            new_opts = prop_def.get("options", [])
            if prop_type in ("options", "multiOptions"):
                seen = {o.get("value") for o in existing_opts if isinstance(o, dict)}
                merged = list(existing_opts)
                merged.extend(
                    opt
                    for opt in new_opts
                    if isinstance(opt, dict) and opt.get("value") not in seen
                )
                # Propagate dynamic typeOptions flags
                new_type_opts = prop_def.get("typeOptions") or {}
                if new_type_opts.get("loadOptionsMethod") or new_type_opts.get(
                    "loadOptionsDependsOn"
                ):
                    existing_type_opts = prop_map[name].get("typeOptions") or {}
                    prop_map[name] = {
                        **prop_map[name],
                        "typeOptions": {**existing_type_opts, **new_type_opts},
                    }
            else:
                seen = {o.get("name") for o in existing_opts if isinstance(o, dict)}
                merged = list(existing_opts)
                merged.extend(
                    opt for opt in new_opts if isinstance(opt, dict) and opt.get("name") not in seen
                )
            prop_map[name] = {**prop_map[name], "options": merged}
        else:
            prop_map[name] = prop_def
    return prop_map
