"""ND0203: Detect nodes with parameters incompatible with their declared type."""

from pathlib import Path
from typing import Any

from valn8n_lib.rules._catalog import NODE_CATALOG
from valn8n_lib.rules.registry import SourceMap, register
from valn8n_lib.source_map import lookup_line
from valn8n_lib.types import Diagnostic, Location, RuleCode, Severity, SuggestionLevel

_RULE_CODE = RuleCode("ND0203", Severity.ERROR)

# Node types where the catalog shows empty properties due to extraction gaps,
# not because the node genuinely accepts zero parameters.
INCOMPLETE_CATALOG: frozenset[str] = frozenset(
    {
        "n8n-nodes-base.httpRequest",
        "n8n-nodes-base.mongoDb",
        "n8n-nodes-base.grist",
        "n8n-nodes-base.webflow",
        "@n8n/n8n-nodes-langchain.chainLlm",
        "@n8n/n8n-nodes-langchain.retrieverContextualCompression",
        "n8n-nodes-base.",  # empty-name artifact
    }
)


def _has_no_properties(catalog_entry: dict[str, Any]) -> bool:
    """Return True if catalog entry has zero properties across all versions."""
    if catalog_entry.get("properties"):
        return False
    props_by_ver = catalog_entry.get("properties_by_version", {})
    return not any(props_by_ver.values())


@register("ND0203", Severity.ERROR, description="Parameters on zero-property node")
def check_unexpected_params(
    workflow: dict[str, Any],
    path: Path,
    source_map: SourceMap = None,
    _suggestions: SuggestionLevel = SuggestionLevel.FULL,
) -> list[Diagnostic]:
    """Flag parameter keys on nodes whose type accepts no parameters."""
    diagnostics: list[Diagnostic] = []
    nodes = workflow.get("nodes", [])
    file = str(path)

    for node_index, node in enumerate(nodes):
        if not isinstance(node, dict):
            continue

        node_type = node.get("type", "")
        node_name = node.get("name", "<unnamed>")
        parameters = node.get("parameters")
        if not isinstance(parameters, dict) or not parameters:
            continue

        catalog_entry = NODE_CATALOG.get(node_type)
        if catalog_entry is None:
            continue

        if node_type in INCOMPLETE_CATALOG:
            continue

        if not _has_no_properties(catalog_entry):
            continue

        for key in parameters:
            pointer = f"/nodes/{node_index}/parameters/{key}"
            line = lookup_line(source_map, pointer)
            diagnostics.append(
                Diagnostic(
                    file=file,
                    location=Location(node_name, f"parameters/{key}"),
                    code=_RULE_CODE,
                    message=f'Unexpected parameter "{key}" for node type "{node_type}" '
                    f"(type accepts no parameters)",
                    line=line,
                )
            )

    return diagnostics
