"""Tests for ND0402 — suspect expression missing '=' prefix."""

from pathlib import Path

import pytest

from fixtures import workflows_dir
from valn8n_lib.rules import get_rules
from valn8n_lib.rules.nd_0402 import (
    _is_suspect_expression,
    check_suspect_expressions,
)
from valn8n_lib.validator import validate_workflow

FIXTURE_DIR = workflows_dir() / "test_suspect_expression"


# ---------------------------------------------------------------------------
# Unit tests for _is_suspect_expression
# ---------------------------------------------------------------------------


class TestIsSuspectExpression:
    def test_suspect_no_prefix(self) -> None:
        assert _is_suspect_expression("{{ $json.field }}") is True

    def test_suspect_multiple_templates(self) -> None:
        # Starts with {{ and ends with }} — suspect
        assert _is_suspect_expression("{{ a }} and {{ b }}") is True

    def test_valid_expression(self) -> None:
        assert _is_suspect_expression("={{ $json.field }}") is False

    def test_plain_string(self) -> None:
        assert _is_suspect_expression("hello world") is False

    def test_unclosed_not_suspect(self) -> None:
        # Doesn't end with }}
        assert _is_suspect_expression("{{ broken") is False

    def test_orphan_close_not_suspect(self) -> None:
        # Doesn't start with {{
        assert _is_suspect_expression("text }}") is False

    def test_empty_string(self) -> None:
        assert _is_suspect_expression("") is False

    def test_just_brackets(self) -> None:
        assert _is_suspect_expression("{{ }}") is True


# ---------------------------------------------------------------------------
# Integration tests with inline workflows
# ---------------------------------------------------------------------------


def _make_workflow(parameters: dict) -> dict:
    return {
        "nodes": [
            {
                "id": "test-1",
                "name": "Test Node",
                "type": "n8n-nodes-base.noOp",
                "position": [0, 0],
                "parameters": parameters,
                "typeVersion": 1,
            },
        ],
    }


class TestCheckSuspectExpressions:
    def test_suspect_detected(self) -> None:
        wf = _make_workflow({"field": "{{ $json.field }}"})
        diags = check_suspect_expressions(wf, Path("/fake.json"))
        assert len(diags) == 1
        assert diags[0].code.code == "ND0402"

    def test_valid_expression_no_diagnostic(self) -> None:
        wf = _make_workflow({"field": "={{ $json.field }}"})
        diags = check_suspect_expressions(wf, Path("/fake.json"))
        assert diags == []

    def test_plain_string_no_diagnostic(self) -> None:
        wf = _make_workflow({"field": "hello world"})
        diags = check_suspect_expressions(wf, Path("/fake.json"))
        assert diags == []

    def test_unclosed_no_diagnostic(self) -> None:
        wf = _make_workflow({"field": "{{ broken"})
        diags = check_suspect_expressions(wf, Path("/fake.json"))
        assert diags == []

    def test_nested_dict_suspect(self) -> None:
        wf = _make_workflow({"outer": {"inner": "{{ $json.x }}"}})
        diags = check_suspect_expressions(wf, Path("/fake.json"))
        assert len(diags) == 1
        assert "outer/inner" in (diags[0].location.field_path if diags[0].location else "")

    def test_list_element_suspect(self) -> None:
        wf = _make_workflow({"items": ["={{ ok }}", "{{ suspect }}"]})
        diags = check_suspect_expressions(wf, Path("/fake.json"))
        assert len(diags) == 1
        assert "items/1" in (diags[0].location.field_path if diags[0].location else "")


# ---------------------------------------------------------------------------
# Fixture-driven test
# ---------------------------------------------------------------------------


def _discover_cases() -> list[str]:
    return sorted(p.stem for p in FIXTURE_DIR.glob("*.lst"))


@pytest.mark.parametrize("case", _discover_cases())
def test_fixture(case: str) -> None:
    lst_path = FIXTURE_DIR / f"{case}.lst"
    json_path = FIXTURE_DIR / f"{case}.json"
    assert json_path.exists(), f"Missing fixture: {json_path}"

    expected = sorted(line.strip() for line in lst_path.read_text().splitlines() if line.strip())
    diagnostics = validate_workflow(json_path, rules=get_rules())
    actual = sorted(d.code.code for d in diagnostics if d.code.code == "ND0402")
    assert actual == expected, f"{case}: expected {expected}, got {actual}"
