"""ND0501: Detect nodes missing required credentials."""

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("ND0501", Severity.WARNING)


@register("ND0501", Severity.WARNING, description="Missing required credentials", strict=False)
def check_missing_credentials(
    workflow: dict[str, Any],
    path: Path,
    source_map: SourceMap = None,
    suggestions: SuggestionLevel = SuggestionLevel.FULL,
) -> list[Diagnostic]:
    """Flag nodes that require credentials but have none configured."""
    diagnostics: list[Diagnostic] = []
    fname = str(path)

    for i, node in enumerate(workflow.get("nodes", [])):
        if node.get("disabled"):
            continue

        node_type = node.get("type", "")
        catalog_entry = NODE_CATALOG.get(node_type)
        if not catalog_entry:
            continue

        catalog_creds: list[str] = catalog_entry.get("credentials", [])
        if not catalog_creds:
            continue

        node_creds = node.get("credentials") or {}
        if not isinstance(node_creds, dict):
            continue

        # Pass if any declared credential type is present
        if node_creds.keys() & set(catalog_creds):
            continue

        pointer = f"/nodes/{i}/credentials"
        line = lookup_line(source_map, pointer)
        cred_list = ", ".join(sorted(catalog_creds))
        diagnostics.append(
            Diagnostic(
                file=fname,
                location=Location(node.get("name", f"node[{i}]"), "credentials"),
                code=_RULE_CODE,
                message=f"Credentials not configured; expected one of: {cred_list}",
                line=line,
            ),
        )

    return diagnostics
