"""Integration tests for the `diamond` → conditional handler.

REQ-DOT-CONDITIONAL-SHAPE — a diamond node emits SUCCESS
unconditionally and lets the edge selector pick the next node via
the standard priority. Used together with edge `condition=` for
real routing decisions.
"""

from __future__ import annotations

from pathlib import Path

import pytest

from attractor.engine import Engine, EngineEvent, NodeStarted, RunStatus
from attractor.workflow import parse, validate
from attractor.workflow.validate import ValidationFailed

# ─── parser / validator ─────────────────────────────────────────────────


# [unit->REQ-DOT-CONDITIONAL-SHAPE]
def test_diamond_parses_as_conditional() -> None:
    """`shape=diamond` is recognized and maps to NodeKind.CONDITIONAL."""
    src = """\
digraph C {
    start [shape=Mdiamond, label="S"]
    exit  [shape=Msquare,  label="E"]
    route [shape=diamond,  label="Route"]
    start -> route -> exit
}
"""
    graph = validate(parse(src))
    route = next(n for n in graph.nodes if n.id == "route")
    assert route.kind.value == "conditional"


# [unit->REQ-DOT-CONDITIONAL-SHAPE]
def test_diamond_rejects_prompt() -> None:
    src = """\
digraph C {
    start [shape=Mdiamond]
    exit  [shape=Msquare]
    route [shape=diamond, prompt="this should be rejected"]
    start -> route -> exit
}
"""
    with pytest.raises(ValidationFailed) as ei:
        validate(parse(src))
    msgs = [e.message for e in ei.value.errors]
    assert any("prompt" in m and "routing-only" in m for m in msgs)


# [unit->REQ-DOT-CONDITIONAL-SHAPE]
def test_diamond_rejects_script() -> None:
    src = """\
digraph C {
    start [shape=Mdiamond]
    exit  [shape=Msquare]
    route [shape=diamond, script="echo nope"]
    start -> route -> exit
}
"""
    with pytest.raises(ValidationFailed) as ei:
        validate(parse(src))
    msgs = [e.message for e in ei.value.errors]
    assert any("script" in m and "routing-only" in m for m in msgs)


# ─── engine execution ───────────────────────────────────────────────────


@pytest.mark.asyncio
# [int->REQ-DOT-CONDITIONAL-SHAPE]
async def test_diamond_routes_via_condition(seeded_repo: Path) -> None:
    """A diamond node routes via edge condition=. No agent invoked."""
    src = """\
digraph CondRoute {
    start [shape=Mdiamond]
    exit  [shape=Msquare]
    route [shape=diamond, label="Route"]
    a     [shape=parallelogram, label="A", script="exit 0"]
    b     [shape=parallelogram, label="B", script="exit 0"]

    start -> route
    route -> a [condition="outcome=SUCCESS"]
    route -> b [condition="outcome=FAILURE"]
    a -> exit
    b -> exit
}
"""
    events: list[EngineEvent] = []
    engine = Engine(seeded_repo)
    graph = validate(parse(src))
    status = await engine.run(graph, events=events.append)
    # Diamond emits SUCCESS unconditionally → the outcome=SUCCESS
    # condition matches → route to `a`.
    assert status == RunStatus.COMPLETED
    started_node_ids = [e.node_id for e in events if isinstance(e, NodeStarted)]
    assert "a" in started_node_ids, (
        f"expected `a` to be started; got {started_node_ids}"
    )
    assert "b" not in started_node_ids


@pytest.mark.asyncio
# [int->REQ-DOT-CONDITIONAL-SHAPE]
async def test_diamond_falls_back_to_unlabeled(seeded_repo: Path) -> None:
    """When no condition matches, diamond falls back to the unlabeled
    edge (the SUCCESS-family unlabeled-default).
    """
    src = """\
digraph CondFallback {
    start [shape=Mdiamond]
    exit  [shape=Msquare]
    route [shape=diamond, label="Route"]
    a     [shape=parallelogram, label="A", script="exit 0"]

    start -> route
    route -> a
    a -> exit
}
"""
    engine = Engine(seeded_repo)
    graph = validate(parse(src))
    status = await engine.run(graph)
    assert status == RunStatus.COMPLETED
