"""CP-6 Phase 2 — WorkflowStep + Workflow construction tests."""

import sys
import pathlib

sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent.parent.parent))
from recoil.core.paths import ensure_pipeline_importable  # noqa: E402
ensure_pipeline_importable()

import pytest  # noqa: E402

from recoil.pipeline.core.workflow import Workflow, WorkflowStep  # noqa: E402


def _step(step_id="s1", modality="image_t2i", **overrides) -> WorkflowStep:
    base = dict(
        step_id=step_id,
        modality=modality,
        payload={"shot_id": "X", "prompt": "p", "model": "nbp"},
    )
    base.update(overrides)
    return WorkflowStep(**base)


def test_workflow_step_minimal():
    s = _step()
    assert s.step_id == "s1"
    assert s.modality == "image_t2i"
    assert s.payload == {"shot_id": "X", "prompt": "p", "model": "nbp"}
    assert s.depends_on == []
    assert s.status == "pending"
    assert s.receipt is None
    assert s.error is None
    assert s.started_us is None
    assert s.finished_us is None


def test_workflow_step_full():
    s = _step(
        step_id="kf",
        modality="image_t2i",
        depends_on=["upstream"],
    )
    assert s.depends_on == ["upstream"]


def test_workflow_step_rejects_empty_step_id():
    with pytest.raises(ValueError):
        WorkflowStep(step_id="", modality="image_t2i", payload={})


def test_workflow_step_rejects_empty_modality():
    with pytest.raises(ValueError):
        WorkflowStep(step_id="s1", modality="", payload={})


def test_workflow_step_rejects_non_dict_payload():
    with pytest.raises(TypeError):
        WorkflowStep(step_id="s1", modality="image_t2i", payload=["not", "a", "dict"])  # type: ignore


def test_workflow_step_rejects_non_list_depends_on():
    with pytest.raises(TypeError):
        WorkflowStep(
            step_id="s1", modality="image_t2i", payload={}, depends_on="upstream"  # type: ignore
        )


def test_workflow_minimal():
    wf = Workflow(workflow_id="wf1", steps=[_step()])
    assert wf.workflow_id == "wf1"
    assert len(wf.steps) == 1
    assert wf.global_provenance == {}
    assert wf.created_at  # ISO 8601 string


def test_workflow_multi_step():
    s1 = _step(step_id="kf")
    s2 = _step(step_id="vid", modality="video_i2v", depends_on=["kf"])
    wf = Workflow(workflow_id="wf1", steps=[s1, s2])
    assert wf.get_step("kf") is s1
    assert wf.get_step("vid") is s2


def test_workflow_get_step_missing_raises():
    wf = Workflow(workflow_id="wf1", steps=[_step()])
    with pytest.raises(KeyError):
        wf.get_step("nonexistent")


def test_workflow_rejects_empty_id():
    with pytest.raises(ValueError):
        Workflow(workflow_id="", steps=[])


def test_workflow_rejects_non_list_steps():
    with pytest.raises(TypeError):
        Workflow(workflow_id="wf1", steps="not a list")  # type: ignore


def test_workflow_rejects_non_workflowstep_in_steps():
    with pytest.raises(TypeError):
        Workflow(workflow_id="wf1", steps=[{"step_id": "s1"}])  # type: ignore


def test_workflow_rejects_duplicate_step_id():
    s1 = _step(step_id="dup")
    s2 = _step(step_id="dup", modality="video_i2v")
    with pytest.raises(ValueError):
        Workflow(workflow_id="wf1", steps=[s1, s2])


def test_workflow_allows_empty_steps_for_round_trip():
    # Phase 3's executor will reject empty-steps at run() time; construction
    # allows it for serialization round-trip and edge-case tests.
    wf = Workflow(workflow_id="wf1", steps=[])
    assert wf.steps == []


def test_workflow_global_provenance():
    wf = Workflow(
        workflow_id="wf1",
        steps=[_step()],
        global_provenance={"project": "tartarus", "episode": 1},
    )
    assert wf.global_provenance == {"project": "tartarus", "episode": 1}
