"""Fixture-driven and unit tests for ND0301 deep parameter key validation."""

import json
from pathlib import Path

import pytest

from fixtures import workflows_dir
from valn8n_lib.rules import get_rules
from valn8n_lib.rules.nd_0301 import check_deep_parameter_keys

FIXTURE_DIR = workflows_dir() / "test_deep_params"


def _discover_cases() -> list[str]:
    """Discover test cases from .lst companion files."""
    return sorted(p.stem for p in FIXTURE_DIR.glob("*.lst"))


@pytest.mark.parametrize("case", _discover_cases())
def test_deep_params_rule(case: str) -> None:
    from valn8n_lib.validator import validate_workflow

    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 == "ND0301")

    assert actual == expected, f"{case}: expected {expected}, got {actual}"


class TestExpressionSkipping:
    """Expression values should be skipped without emitting diagnostics."""

    def test_expression_value_is_skipped(self, tmp_path: Path) -> None:
        workflow = {
            "nodes": [
                {
                    "parameters": {
                        "conditions": "={{ $json.dynamic }}",
                    },
                    "id": "1",
                    "name": "ExprNode",
                    "type": "n8n-nodes-base.if",
                    "typeVersion": 2,
                    "position": [0, 0],
                }
            ]
        }
        path = tmp_path / "expr.json"
        path.write_text(json.dumps(workflow))
        diags = check_deep_parameter_keys(workflow, path)
        assert len(diags) == 0


class TestUnknownNodeType:
    """Unknown node types should be silently skipped."""

    def test_unknown_type_is_skipped(self, tmp_path: Path) -> None:
        workflow = {
            "nodes": [
                {
                    "parameters": {"foo": {"badKey": 1}},
                    "id": "1",
                    "name": "CustomNode",
                    "type": "n8n-nodes-community.customThing",
                    "typeVersion": 1,
                    "position": [0, 0],
                }
            ]
        }
        path = tmp_path / "unknown.json"
        path.write_text(json.dumps(workflow))
        diags = check_deep_parameter_keys(workflow, path)
        assert len(diags) == 0


class TestArrayElements:
    """Array elements in filter conditions should be individually validated."""

    def test_array_elements_validated(self, tmp_path: Path) -> None:
        workflow = {
            "nodes": [
                {
                    "parameters": {
                        "conditions": {
                            "options": {
                                "caseSensitive": True,
                                "leftValue": "",
                                "typeValidation": "strict",
                                "version": 2,
                            },
                            "conditions": [
                                {
                                    "id": "c1",
                                    "leftValue": "a",
                                    "rightValue": "b",
                                    "operator": {"type": "string", "operation": "equals"},
                                },
                                {
                                    "id": "c2",
                                    "leftValue": "x",
                                    "rightValue": "y",
                                    "badKey": "oops",
                                    "operator": {"type": "string", "operation": "equals"},
                                },
                            ],
                            "combinator": "and",
                        }
                    },
                    "id": "1",
                    "name": "ArrayTest",
                    "type": "n8n-nodes-base.if",
                    "typeVersion": 2,
                    "position": [0, 0],
                }
            ]
        }
        path = tmp_path / "array.json"
        path.write_text(json.dumps(workflow))
        diags = check_deep_parameter_keys(workflow, path)
        codes = [d.code.code for d in diags]
        assert codes == ["ND0301"]
        assert "badKey" in diags[0].message


class TestFixedCollectionMultipleValues:
    """fixedCollection with multipleValues (array of dicts)."""

    def test_fixed_collection_array_items(self, tmp_path: Path) -> None:
        # We need a node that has a fixedCollection property.
        # The Set node (v3.4) has an "assignments" fixedCollection.
        # Let's test with a custom workflow using the Set node.
        workflow = {
            "nodes": [
                {
                    "parameters": {
                        "assignments": {
                            "assignments": [
                                {"id": "a1", "name": "x", "value": "1", "type": "string"},
                            ]
                        }
                    },
                    "id": "1",
                    "name": "SetNode",
                    "type": "n8n-nodes-base.set",
                    "typeVersion": 3.4,
                    "position": [0, 0],
                }
            ]
        }
        path = tmp_path / "fc.json"
        path.write_text(json.dumps(workflow))
        diags = check_deep_parameter_keys(workflow, path)
        # The assignments parameter on Set node is type "assignmentCollection"
        # which maps to the schema, not fixedCollection. So this should be clean.
        assert len(diags) == 0
