"""ND0702: External/integration node without error handling."""

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

# Substrings in node type that indicate external/integration nodes.
_EXTERNAL_MARKERS = ("http", "api", "webhook", "ftp", "database", "email")


def _is_external_node(node_type: str) -> bool:
    """Return True if the node type looks like an external/integration node."""
    lower = node_type.lower()
    return any(marker in lower for marker in _EXTERNAL_MARKERS)


def _has_error_handling(node: dict[str, Any], connections: dict[str, Any]) -> bool:
    """Check whether a node has error handling configured.

    A node has error handling if any of:
    - onError is 'continueErrorOutput' or 'continueRegularOutput'
    - continueOnFail is True (legacy)
    - The node has an error-category output connection (main output index >= 1)
    """
    # Modern onError field
    on_error = node.get("onError", "")
    if on_error in ("continueErrorOutput", "continueRegularOutput"):
        return True

    # Legacy continueOnFail
    if node.get("continueOnFail") is True:
        return True

    # Check for error output connections (output index 1+ on "main")
    node_name = node.get("name", "")
    node_conns = connections.get(node_name)
    if isinstance(node_conns, dict):
        main_outputs = node_conns.get("main")
        if isinstance(main_outputs, list) and len(main_outputs) > 1:
            # Output index 1 is the error output when onError=continueErrorOutput
            error_output = main_outputs[1]
            if isinstance(error_output, list) and len(error_output) > 0:
                return True

    return False


@register(
    "ND0702",
    Severity.WARNING,
    description="External node without error handling",
    strict=False,
)
def check_external_node_error_handling(
    workflow: dict[str, Any],
    path: Path,
    source_map: SourceMap = None,
    suggestions: SuggestionLevel = SuggestionLevel.FULL,
) -> list[Diagnostic]:
    """Flag external/integration nodes that have no error handling configured."""
    diagnostics: list[Diagnostic] = []
    fname = str(path)
    connections = workflow.get("connections", {})

    for i, node in enumerate(workflow.get("nodes", [])):
        node_type = node.get("type", "")
        if not _is_external_node(node_type):
            continue
        if node.get("disabled"):
            continue
        if _has_error_handling(node, connections):
            continue

        node_name = node.get("name", f"node[{i}]")
        pointer = f"/nodes/{i}/name"
        line = lookup_line(source_map, pointer)
        diagnostics.append(
            Diagnostic(
                file=fname,
                location=Location(node_name, "onError"),
                code=_RULE_CODE,
                message=(
                    f'External node "{node_name}" ({node_type}) has no error handling configured'
                ),
                line=line,
            ),
        )

    return diagnostics
