"""Tests for error handling rules ND0701 and ND0702."""

import json
from pathlib import Path

from valn8n_lib.rules.nd_0701 import check_missing_error_workflow
from valn8n_lib.rules.nd_0702 import check_external_node_error_handling
from valn8n_lib.source_map import build_source_map

# -- ND0701 tests --


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


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


def test_nd0701_error_workflow_set(tmp_path: Path) -> None:
    workflow: dict = {"nodes": [], "settings": {"errorWorkflow": "abc-123"}}
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_missing_error_workflow(workflow, p)
    assert diags == []


def test_nd0701_error_workflow_empty_string(tmp_path: Path) -> None:
    """Empty string is falsy — should still flag."""
    workflow: dict = {"nodes": [], "settings": {"errorWorkflow": ""}}
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_missing_error_workflow(workflow, p)
    assert len(diags) == 1


# -- ND0702 tests --


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


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


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


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


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


def test_nd0702_external_node_with_error_output_connection(tmp_path: Path) -> None:
    workflow: dict = {
        "nodes": [
            {"name": "HTTP Request", "type": "n8n-nodes-base.httpRequest", "parameters": {}},
            {"name": "Error Handler", "type": "n8n-nodes-base.set", "parameters": {}},
        ],
        "connections": {
            "HTTP Request": {
                "main": [
                    [{"node": "Success", "type": "main", "index": 0}],
                    [{"node": "Error Handler", "type": "main", "index": 0}],
                ],
            },
        },
    }
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_external_node_error_handling(workflow, p)
    assert diags == []


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


def test_nd0702_multiple_external_nodes(tmp_path: Path) -> None:
    workflow: dict = {
        "nodes": [
            {"name": "HTTP Request", "type": "n8n-nodes-base.httpRequest", "parameters": {}},
            {
                "name": "FTP Upload",
                "type": "n8n-nodes-base.ftp",
                "onError": "continueErrorOutput",
                "parameters": {},
            },
            {"name": "Send Email", "type": "n8n-nodes-base.emailSend", "parameters": {}},
        ],
        "connections": {},
    }
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_external_node_error_handling(workflow, p)
    assert len(diags) == 2
    names = {d.location.node_name for d in diags}  # type: ignore[union-attr]
    assert names == {"HTTP Request", "Send Email"}


def test_nd0702_stop_workflow_on_error_still_flags(tmp_path: Path) -> None:
    """onError=stopWorkflow is the default — should still flag."""
    workflow: dict = {
        "nodes": [
            {
                "name": "HTTP Request",
                "type": "n8n-nodes-base.httpRequest",
                "onError": "stopWorkflow",
                "parameters": {},
            },
        ],
        "connections": {},
    }
    p = tmp_path / "wf.json"
    p.write_text(json.dumps(workflow))
    diags = check_external_node_error_handling(workflow, p)
    assert len(diags) == 1


# -- Source-map fallback tests --


def test_nd0701_line_fallback_with_source_map(tmp_path: Path) -> None:
    """ND0701 points to /settings 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_error_workflow(workflow, p, source_map=sm)
    assert len(diags) == 1
    assert diags[0].line is not None
