"""Tests for naming & documentation rules ND0801, ND0802, ND0803."""

import json
from pathlib import Path

from valn8n_lib.rules.nd_0801 import check_default_node_name
from valn8n_lib.rules.nd_0802 import check_complex_node_missing_notes
from valn8n_lib.rules.nd_0803 import check_missing_workflow_description
from valn8n_lib.source_map import build_source_map

# -- ND0801 tests --


def test_nd0801_default_name_flagged(tmp_path: Path) -> None:
    workflow: dict = {
        "nodes": [
            {"name": "HTTP Request", "type": "n8n-nodes-base.httpRequest", "parameters": {}},
        ],
    }
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_default_node_name(workflow, p)
    assert len(diags) == 1
    assert diags[0].code.code == "ND0801"
    assert "HTTP Request" in diags[0].message


def test_nd0801_default_name_with_number_flagged(tmp_path: Path) -> None:
    workflow: dict = {
        "nodes": [
            {"name": "HTTP Request1", "type": "n8n-nodes-base.httpRequest", "parameters": {}},
        ],
    }
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_default_node_name(workflow, p)
    assert len(diags) == 1


def test_nd0801_custom_name_ok(tmp_path: Path) -> None:
    workflow: dict = {
        "nodes": [
            {"name": "Fetch User Data", "type": "n8n-nodes-base.httpRequest", "parameters": {}},
        ],
    }
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_default_node_name(workflow, p)
    assert diags == []


def test_nd0801_unknown_type_skipped(tmp_path: Path) -> None:
    workflow: dict = {
        "nodes": [
            {"name": "My Custom", "type": "n8n-nodes-custom.myNode", "parameters": {}},
        ],
    }
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_default_node_name(workflow, p)
    assert diags == []


def test_nd0801_sticky_note_skipped(tmp_path: Path) -> None:
    workflow: dict = {
        "nodes": [
            {"name": "Sticky Note", "type": "n8n-nodes-base.stickyNote", "parameters": {}},
        ],
    }
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_default_node_name(workflow, p)
    assert diags == []


def test_nd0801_disabled_skipped(tmp_path: Path) -> None:
    workflow: dict = {
        "nodes": [
            {
                "name": "HTTP Request",
                "type": "n8n-nodes-base.httpRequest",
                "disabled": True,
                "parameters": {},
            },
        ],
    }
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_default_node_name(workflow, p)
    assert diags == []


def test_nd0801_multiple_defaults(tmp_path: Path) -> None:
    workflow: dict = {
        "nodes": [
            {"name": "HTTP Request", "type": "n8n-nodes-base.httpRequest", "parameters": {}},
            {"name": "Fetch Data", "type": "n8n-nodes-base.httpRequest", "parameters": {}},
            {"name": "If", "type": "n8n-nodes-base.if", "parameters": {}},
        ],
    }
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_default_node_name(workflow, p)
    assert len(diags) == 2
    names = {d.location.node_name for d in diags}  # type: ignore[union-attr]
    assert names == {"HTTP Request", "If"}


# -- ND0802 tests --


def test_nd0802_code_node_no_notes(tmp_path: Path) -> None:
    workflow: dict = {
        "nodes": [
            {"name": "Code", "type": "n8n-nodes-base.code", "parameters": {}},
        ],
    }
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_complex_node_missing_notes(workflow, p)
    assert len(diags) == 1
    assert diags[0].code.code == "ND0802"


def test_nd0802_empty_notes(tmp_path: Path) -> None:
    workflow: dict = {
        "nodes": [
            {"name": "Code", "type": "n8n-nodes-base.code", "notes": "  ", "parameters": {}},
        ],
    }
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_complex_node_missing_notes(workflow, p)
    assert len(diags) == 1


def test_nd0802_with_notes_ok(tmp_path: Path) -> None:
    workflow: dict = {
        "nodes": [
            {
                "name": "Code",
                "type": "n8n-nodes-base.code",
                "notes": "Transforms user data",
                "parameters": {},
            },
        ],
    }
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_complex_node_missing_notes(workflow, p)
    assert diags == []


def test_nd0802_non_complex_skipped(tmp_path: Path) -> None:
    workflow: dict = {
        "nodes": [
            {"name": "Set", "type": "n8n-nodes-base.set", "parameters": {}},
        ],
    }
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_complex_node_missing_notes(workflow, p)
    assert diags == []


def test_nd0802_disabled_skipped(tmp_path: Path) -> None:
    workflow: dict = {
        "nodes": [
            {
                "name": "Code",
                "type": "n8n-nodes-base.code",
                "disabled": True,
                "parameters": {},
            },
        ],
    }
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_complex_node_missing_notes(workflow, p)
    assert diags == []


# -- ND0803 tests --


def test_nd0803_missing_description(tmp_path: Path) -> None:
    workflow: dict = {"nodes": []}
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_missing_workflow_description(workflow, p)
    assert len(diags) == 1
    assert diags[0].code.code == "ND0803"


def test_nd0803_empty_description(tmp_path: Path) -> None:
    workflow: dict = {"nodes": [], "description": ""}
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_missing_workflow_description(workflow, p)
    assert len(diags) == 1


def test_nd0803_whitespace_description(tmp_path: Path) -> None:
    workflow: dict = {"nodes": [], "description": "   \n  "}
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_missing_workflow_description(workflow, p)
    assert len(diags) == 1


def test_nd0803_valid_description(tmp_path: Path) -> None:
    workflow: dict = {"nodes": [], "description": "This workflow processes orders"}
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_missing_workflow_description(workflow, p)
    assert diags == []


# -- Source-map fallback tests --


def test_nd0802_line_fallback_with_source_map(tmp_path: Path) -> None:
    """ND0802 points to /nodes/0/notes which doesn't exist; should fall back to /nodes/0."""
    workflow: dict = {
        "nodes": [
            {"name": "Code", "type": "n8n-nodes-base.code", "parameters": {}},
        ],
    }
    raw = json.dumps(workflow, indent=2)
    p = tmp_path / "wf.json"
    p.write_text(raw)
    sm = build_source_map(raw)
    diags = check_complex_node_missing_notes(workflow, p, source_map=sm)
    assert len(diags) == 1
    assert diags[0].line is not None


def test_nd0803_line_fallback_with_source_map(tmp_path: Path) -> None:
    """ND0803 points to /description which doesn't exist; should fall back to root."""
    workflow: dict = {"nodes": []}
    raw = json.dumps(workflow, indent=2)
    p = tmp_path / "wf.json"
    p.write_text(raw)
    sm = build_source_map(raw)
    diags = check_missing_workflow_description(workflow, p, source_map=sm)
    assert len(diags) == 1
    assert diags[0].line is not None
