"""Tests for PlanGraph builder."""

from __future__ import annotations

from dpe.core_bridge._plan_graph import build_plan_graph
from dpe.models.manifest import RunManifest


class TestBuildPlanGraph:
    def test_schema_version(self, completed_manifest: RunManifest) -> None:
        pg = build_plan_graph(completed_manifest)
        assert pg["schema_version"] == "plan-graph/1.0"

    def test_run_id_is_8_chars(self, completed_manifest: RunManifest) -> None:
        pg = build_plan_graph(completed_manifest)
        assert pg["run_id"] == "aabbccdd"
        assert len(pg["run_id"]) == 8

    def test_all_12_nodes_present(self, completed_manifest: RunManifest) -> None:
        pg = build_plan_graph(completed_manifest)
        assert len(pg["nodes"]) == 12

    def test_node_ids_are_unique(self, completed_manifest: RunManifest) -> None:
        pg = build_plan_graph(completed_manifest)
        ids = [n["node_id"] for n in pg["nodes"]]
        assert len(ids) == len(set(ids))

    def test_node_id_format(self, completed_manifest: RunManifest) -> None:
        pg = build_plan_graph(completed_manifest)
        for node in pg["nodes"]:
            assert node["node_id"].startswith("dpe-")

    def test_completed_stages_marked_completed(
        self, completed_manifest: RunManifest
    ) -> None:
        pg = build_plan_graph(completed_manifest)
        completed_nodes = [n for n in pg["nodes"] if n["status"] == "completed"]
        assert len(completed_nodes) == 8

    def test_skipped_stages_marked_skipped(
        self, completed_manifest: RunManifest
    ) -> None:
        pg = build_plan_graph(completed_manifest)
        skipped = [n for n in pg["nodes"] if n["status"] == "skipped"]
        assert len(skipped) == 4
        skipped_ids = {n["node_id"] for n in skipped}
        assert "dpe-cross_critique_a" in skipped_ids
        assert "dpe-cross_critique_b" in skipped_ids
        assert "dpe-prd_critique_r2" in skipped_ids
        assert "dpe-prd_revision_r2" in skipped_ids

    def test_dependency_graph_synthesis(
        self, completed_manifest: RunManifest
    ) -> None:
        pg = build_plan_graph(completed_manifest)
        synthesis = next(n for n in pg["nodes"] if n["name"] == "SYNTHESIS")
        assert set(synthesis["depends_on"]) == {
            "dpe-ideation_a",
            "dpe-ideation_b",
            "dpe-cross_critique_a",
            "dpe-cross_critique_b",
        }

    def test_dependency_graph_revision_r1(
        self, completed_manifest: RunManifest
    ) -> None:
        pg = build_plan_graph(completed_manifest)
        rev = next(n for n in pg["nodes"] if n["name"] == "PRD_REVISION_R1")
        assert set(rev["depends_on"]) == {"dpe-prd_v1", "dpe-prd_critique_r1"}

    def test_finalize_depends_on_last_revision(
        self, completed_manifest: RunManifest
    ) -> None:
        pg = build_plan_graph(completed_manifest)
        fin = next(n for n in pg["nodes"] if n["name"] == "FINALIZE")
        # R2 was skipped, so FINALIZE depends on R1
        assert fin["depends_on"] == ["dpe-prd_revision_r1"]

    def test_metadata_has_artifact_and_role(
        self, completed_manifest: RunManifest
    ) -> None:
        pg = build_plan_graph(completed_manifest)
        node = next(n for n in pg["nodes"] if n["name"] == "IDEATION_A")
        assert node["metadata"]["dpe_artifact"] == "10_ideation_a.md"
        assert node["metadata"]["dpe_role"] == "ideator_a"

    def test_agent_field_populated(self, completed_manifest: RunManifest) -> None:
        pg = build_plan_graph(completed_manifest)
        node = next(n for n in pg["nodes"] if n["name"] == "INPUT_NORMALIZE")
        assert node["agent"] == "simulation"


class TestBudgetExceeded:
    def test_pending_stages_after_budget_exceeded(
        self, budget_exceeded_manifest: RunManifest
    ) -> None:
        pg = build_plan_graph(budget_exceeded_manifest)
        pending = [n for n in pg["nodes"] if n["status"] == "pending"]
        pending_names = {n["name"] for n in pending}
        assert "PRD_CRITIQUE_R1" in pending_names
        assert "FINALIZE" in pending_names


class TestErrorRun:
    def test_error_stage_marked_failed(
        self, error_manifest: RunManifest
    ) -> None:
        pg = build_plan_graph(error_manifest)
        synthesis = next(n for n in pg["nodes"] if n["name"] == "SYNTHESIS")
        assert synthesis["status"] == "failed"

    def test_stages_after_error_are_pending(
        self, error_manifest: RunManifest
    ) -> None:
        pg = build_plan_graph(error_manifest)
        prd_v1 = next(n for n in pg["nodes"] if n["name"] == "PRD_V1")
        assert prd_v1["status"] == "pending"
