"""ND0602: Detect connection references to non-existent nodes."""

from pathlib import Path
from typing import Any

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("ND0602", Severity.ERROR)


@register(
    "ND0602", Severity.ERROR, description="Connection references non-existent node", strict=True
)
def check_dangling_connection(
    workflow: dict[str, Any],
    path: Path,
    source_map: SourceMap = None,
    suggestions: SuggestionLevel = SuggestionLevel.FULL,
) -> list[Diagnostic]:
    """Flag connection sources and destinations that reference non-existent nodes."""
    diagnostics: list[Diagnostic] = []
    fname = str(path)
    node_names = {n.get("name") for n in workflow.get("nodes", []) if isinstance(n, dict)}
    connections = workflow.get("connections", {})

    for source_name, conn_types in connections.items():
        if source_name not in node_names:
            pointer = f"/connections/{source_name}"
            line = lookup_line(source_map, pointer)
            diagnostics.append(
                Diagnostic(
                    file=fname,
                    location=Location(source_name, "connections"),
                    code=_RULE_CODE,
                    message=f'Connection source "{source_name}" does not exist in nodes',
                    line=line,
                ),
            )

        if not isinstance(conn_types, dict):
            continue
        for conn_type, outputs in conn_types.items():
            if not isinstance(outputs, list):
                continue
            for output_idx, output_group in enumerate(outputs):
                if not isinstance(output_group, list):
                    continue
                for target_idx, target in enumerate(output_group):
                    if not isinstance(target, dict):
                        continue
                    target_node = target.get("node")
                    if target_node is not None and target_node not in node_names:
                        pointer = (
                            f"/connections/{source_name}/{conn_type}/{output_idx}/{target_idx}/node"
                        )
                        line = lookup_line(source_map, pointer)
                        diagnostics.append(
                            Diagnostic(
                                file=fname,
                                location=Location(target_node, "connections"),
                                code=_RULE_CODE,
                                message=f'Connection destination "{target_node}" not in nodes',
                                line=line,
                            ),
                        )

    return diagnostics
