"""ND0402: Warn on strings that look like expressions but lack the '=' prefix."""

from collections.abc import Iterator
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("ND0402", Severity.WARNING)


def _walk_strings(obj: Any, path_parts: list[str]) -> Iterator[tuple[str, str]]:
    """Recursively yield (field_path, string_value) for every string leaf."""
    if isinstance(obj, str):
        yield "/".join(path_parts), obj
    elif isinstance(obj, dict):
        for key, val in obj.items():
            yield from _walk_strings(val, [*path_parts, key])
    elif isinstance(obj, list):
        for idx, val in enumerate(obj):
            yield from _walk_strings(val, [*path_parts, str(idx)])


def _is_suspect_expression(value: str) -> bool:
    """Return True if value looks like an expression missing the '=' prefix.

    Matches strings that start with '{{' and end with '}}' but do NOT start with '={{'.
    """
    stripped = value.strip()
    return stripped.startswith("{{") and stripped.endswith("}}") and not stripped.startswith("={{")


@register("ND0402", Severity.WARNING, description="Suspect expression prefix")
def check_suspect_expressions(
    workflow: dict[str, Any],
    path: Path,
    source_map: SourceMap = None,
    suggestions: SuggestionLevel = SuggestionLevel.FULL,
) -> list[Diagnostic]:
    """Warn on parameter strings that look like expressions but lack '=' prefix."""
    diagnostics: list[Diagnostic] = []
    fname = str(path)

    for i, node in enumerate(workflow.get("nodes", [])):
        node_name = node.get("name", f"node[{i}]")
        parameters = node.get("parameters")
        if not isinstance(parameters, dict):
            continue

        for field_path, value in _walk_strings(parameters, []):
            if not _is_suspect_expression(value):
                continue

            pointer = f"/nodes/{i}/parameters/{field_path}"
            line = lookup_line(source_map, pointer)
            diagnostics.append(
                Diagnostic(
                    file=fname,
                    location=Location(node_name, f"parameters/{field_path}"),
                    code=_RULE_CODE,
                    message=(
                        f"Possible expression missing '=' prefix: value starts with "
                        f"'{{{{' but not '={{{{'. Did you mean '={{{{ ... }}}}'? "
                        f"Value: {value!r}"
                    ),
                    line=line,
                ),
            )

    return diagnostics
