"""The Task "navigate" node: POST /tasks/{id}/run reports where the caller
should go (TaskRunResult.navigation), with the node's inputs as query
parameters — only when the whole run succeeded (app/crud/tasks.py)."""

import uuid

PAGE_ID = str(uuid.uuid4())


def _start(to: str) -> dict:
    return {"key": "start", "type": "start", "label": "Start", "next_nodes": [{"to": to}]}


def _nav(**extra) -> dict:
    node = {
        "key": "go",
        "type": "navigate",
        "label": "Open receipt",
        "nav_target_type": "page",
        "nav_target_id": PAGE_ID,
        "input_params": [
            {"name": "applicationNo", "type": "string", "source_field": "seq"},
            {"name": "amount", "type": "number", "source_field": "amount"},
            {"name": "paid", "type": "boolean", "source_field": "paid", "required": False},
            {"name": "note", "type": "string", "source_field": "missing", "required": False},
        ],
    }
    node.update(extra)
    return node


async def _run(client, auth_headers, nodes, context):
    task = await client.post("/tasks", headers=auth_headers, json={"name": "Navigate test", "nodes": nodes})
    assert task.status_code == 201, task.text
    resp = await client.post(f"/tasks/{task.json()['id']}/run", headers=auth_headers, json={"context": context, "allow_draft": True})
    assert resp.status_code == 200, resp.text
    return resp.json()


async def test_navigate_returns_target_and_inputs_as_params(client, auth_headers):
    result = await _run(client, auth_headers, [_start("go"), _nav()], {"seq": "RTI-2026-0007", "amount": 12.5, "paid": True})
    assert result["ok"] is True
    assert result["navigation"] == {
        "target_type": "page",
        "target_id": PAGE_ID,
        "url": None,
        # An optional input with nothing to read is left out, not sent blank.
        "params": {"applicationNo": "RTI-2026-0007", "amount": "12.5", "paid": "true"},
    }
    step = next(t for t in result["trace"] if t["node_key"] == "go")
    assert step["ok"] and "applicationNo, amount, paid" in step["detail"]


async def test_navigate_url_is_rendered_and_walk_continues(client, auth_headers):
    nodes = [
        _start("go"),
        _nav(nav_target_type="url", nav_target_id=None, url="https://pay.example.com/{{applicationNo}}", next_nodes=[{"to": "go2"}]),
        # A later Navigate node wins.
        {"key": "go2", "type": "navigate", "label": "Then here", "nav_target_type": "form", "nav_target_id": PAGE_ID},
    ]
    result = await _run(client, auth_headers, nodes, {"seq": "A-1", "amount": 1})
    assert result["ok"] is True
    assert result["navigation"]["target_type"] == "form"
    assert result["navigation"]["params"] == {}
    first = next(t for t in result["trace"] if t["node_key"] == "go")
    assert first["ok"]

    only_url = await _run(client, auth_headers, nodes[:2], {"seq": "A-1", "amount": 1})
    assert only_url["navigation"]["url"] == "https://pay.example.com/A-1"
    assert only_url["navigation"]["target_id"] is None


async def test_no_navigation_when_the_run_fails(client, auth_headers):
    # A required input with no value fails the node itself.
    result = await _run(client, auth_headers, [_start("go"), _nav()], {"amount": 1})
    assert result["ok"] is False
    assert result["navigation"] is None

    # A node failing after the Navigate node also cancels the navigation.
    failing = {"key": "bad", "type": "loop", "label": "Bad loop", "loop_mode": "repeat", "input_params": [{"name": "count", "type": "int", "source_field": "nope"}]}
    result = await _run(client, auth_headers, [_start("go"), _nav(next_nodes=[{"to": "bad"}]), failing], {"seq": "A-1", "amount": 1})
    assert result["ok"] is False
    assert result["navigation"] is None


async def test_navigate_needs_a_target(client, auth_headers):
    result = await _run(client, auth_headers, [_start("go"), _nav(nav_target_id=None, input_params=[])], {})
    assert result["ok"] is False
    assert "pick the page" in result["trace"][-1]["detail"]

    result = await _run(client, auth_headers, [_start("go"), _nav(nav_target_type="url", url="  ", input_params=[])], {})
    assert result["ok"] is False
    assert result["trace"][-1]["detail"] == "set the URL to open"


async def test_runs_without_a_navigate_node_have_no_navigation(client, auth_headers):
    result = await _run(client, auth_headers, [{"key": "start", "type": "start", "label": "Start"}], {})
    assert result["ok"] is True
    assert result["navigation"] is None
