"""CP-6 Phase 6 — end-to-end Workflow scenarios mirroring production shapes."""

import sys
import pathlib
from types import SimpleNamespace

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.dispatch import _reset_bootstrap_for_tests  # noqa: E402
from recoil.pipeline.core.dispatch_context import DispatchContext  # noqa: E402
from recoil.pipeline.core.registry import _reset_for_tests  # noqa: E402
from recoil.pipeline.core.workflow import Workflow, WorkflowStep  # noqa: E402


def _step_result(success=True, **overrides):
    base = dict(
        shot_id="X", success=success,
        final_state="keyframe_generated" if success else "failed",
        output_path="/tmp/x.png" if success else None,
        cost_usd=0.04, error=None if success else "boom",
        take_index=0, gate_verdict=None, model="nbp", pipeline="still",
    )
    base.update(overrides)
    return SimpleNamespace(**base)


class _StubStepRunner:
    """Configurable stub. Fail flag controls keyframe outcome."""
    def __init__(self, *, kf_succeeds=True, vid_succeeds=True):
        self._dispatch_path = "unknown"
        self.kf_succeeds = kf_succeeds
        self.vid_succeeds = vid_succeeds
        self.calls = []

    def execute_keyframe(self, **kw):
        self.calls.append(("keyframe", dict(kw)))
        return _step_result(success=self.kf_succeeds)

    def execute_video(self, **kw):
        self.calls.append(("video", dict(kw)))
        return _step_result(
            success=self.vid_succeeds,
            output_path="/tmp/v.mp4" if self.vid_succeeds else None,
            final_state="video_complete" if self.vid_succeeds else "failed",
            cost_usd=0.20, model="seeddance-2.0", pipeline="i2v",
            error=None if self.vid_succeeds else "video gen failed",
        )


@pytest.fixture(autouse=True)
def reset():
    _reset_for_tests()
    _reset_bootstrap_for_tests()
    yield
    _reset_for_tests()
    _reset_bootstrap_for_tests()


def test_kf_then_video_happy_path(tmp_path):
    sr = _StubStepRunner()
    ctx = DispatchContext(caller_id="prod", step_runner=sr, receipts_log_path="DISABLED")
    wf = Workflow(workflow_id="wf_h", steps=[
        WorkflowStep(step_id="kf", modality="image_t2i",
                     payload={"shot_id": "X", "prompt": "p", "model": "nbp", "aspect_ratio": "9_16"}),
        WorkflowStep(step_id="vid", modality="video_i2v",
                     payload={"shot_id": "X", "prompt": "p", "model": "seeddance-2.0", "aspect_ratio": "9_16"},
                     depends_on=["kf"]),
    ])
    wf.run(context=ctx)
    assert [s.status for s in wf.steps] == ["succeeded", "succeeded"]


def test_kf_fail_skips_video(tmp_path):
    sr = _StubStepRunner(kf_succeeds=False)
    ctx = DispatchContext(caller_id="prod", step_runner=sr, receipts_log_path="DISABLED")
    wf = Workflow(workflow_id="wf_f", steps=[
        WorkflowStep(step_id="kf", modality="image_t2i",
                     payload={"shot_id": "X", "prompt": "p", "model": "nbp", "aspect_ratio": "9_16"}),
        WorkflowStep(step_id="vid", modality="video_i2v",
                     payload={"shot_id": "X", "prompt": "p", "model": "seeddance-2.0", "aspect_ratio": "9_16"},
                     depends_on=["kf"]),
    ])
    wf.run(context=ctx)
    assert wf.steps[0].status == "failed"
    assert wf.steps[1].status == "skipped"
    # StepRunner only saw the keyframe call — video was never invoked
    methods = [c[0] for c in sr.calls]
    assert methods == ["keyframe"]


def test_kf_succeed_video_fail(tmp_path):
    sr = _StubStepRunner(vid_succeeds=False)
    ctx = DispatchContext(caller_id="prod", step_runner=sr, receipts_log_path="DISABLED")
    wf = Workflow(workflow_id="wf_v", steps=[
        WorkflowStep(step_id="kf", modality="image_t2i",
                     payload={"shot_id": "X", "prompt": "p", "model": "nbp", "aspect_ratio": "9_16"}),
        WorkflowStep(step_id="vid", modality="video_i2v",
                     payload={"shot_id": "X", "prompt": "p", "model": "seeddance-2.0", "aspect_ratio": "9_16"},
                     depends_on=["kf"]),
    ])
    wf.run(context=ctx)
    assert wf.steps[0].status == "succeeded"
    assert wf.steps[1].status == "failed"
    assert "video gen failed" in (wf.steps[1].receipt.run_result.error or "")


def test_hooks_fire_in_real_dispatch_chain(tmp_path):
    sr = _StubStepRunner()
    ctx = DispatchContext(caller_id="prod", step_runner=sr, receipts_log_path="DISABLED")
    wf = Workflow(workflow_id="wf_h", steps=[
        WorkflowStep(step_id="kf", modality="image_t2i",
                     payload={"shot_id": "X", "prompt": "p", "model": "nbp", "aspect_ratio": "9_16"}),
        WorkflowStep(step_id="vid", modality="video_i2v",
                     payload={"shot_id": "X", "prompt": "p", "model": "seeddance-2.0", "aspect_ratio": "9_16"},
                     depends_on=["kf"]),
    ])
    log = []
    wf.run(
        context=ctx,
        pre_step=lambda s, w: log.append(("pre", s.step_id)),
        post_step=lambda s, w: log.append(("post", s.step_id, s.status)),
    )
    assert log == [
        ("pre", "kf"), ("post", "kf", "succeeded"),
        ("pre", "vid"), ("post", "vid", "succeeded"),
    ]


def test_three_step_chain(tmp_path):
    """A 3-step chain (kf → vid → vid_post-stub) — proves the executor
    handles depth > 2."""
    sr = _StubStepRunner()
    ctx = DispatchContext(caller_id="prod", step_runner=sr, receipts_log_path="DISABLED")
    wf = Workflow(workflow_id="wf_3", steps=[
        WorkflowStep(step_id="kf", modality="image_t2i",
                     payload={"shot_id": "X", "prompt": "p", "model": "nbp", "aspect_ratio": "9_16"}),
        WorkflowStep(step_id="vid", modality="video_i2v",
                     payload={"shot_id": "X", "prompt": "p", "model": "seeddance-2.0", "aspect_ratio": "9_16"},
                     depends_on=["kf"]),
        # Stub modality fails-by-design — this proves an in-flight failure
        # in step 3 doesn't roll back the earlier successes.
        WorkflowStep(step_id="lipsync", modality="lipsync_post",
                     payload={"video_path": "/tmp/v.mp4"},
                     depends_on=["vid"]),
    ])
    wf.run(context=ctx)
    assert wf.steps[0].status == "succeeded"
    assert wf.steps[1].status == "succeeded"
    assert wf.steps[2].status == "failed"  # NotImplementedError stub


def test_parallel_branches_one_fails_other_succeeds(tmp_path):
    """Two independent branches (a ↛ b). a fails, b should run normally."""
    sr = _StubStepRunner()  # all succeed by default

    # Workaround: use a single StubStepRunner; route both branches through
    # different modalities to simulate independent failure modes. Branch A
    # uses an unknown modality (KeyError → step failed); Branch B uses a
    # real modality (succeeds).
    ctx = DispatchContext(caller_id="prod", step_runner=sr, receipts_log_path="DISABLED")
    wf = Workflow(workflow_id="wf_b", steps=[
        WorkflowStep(step_id="a", modality="totally_bogus", payload={}),
        WorkflowStep(step_id="b", modality="image_t2i",
                     payload={"shot_id": "Y", "prompt": "p", "model": "nbp", "aspect_ratio": "9_16"}),
        WorkflowStep(step_id="b2", modality="video_i2v",
                     payload={"shot_id": "Y", "prompt": "p", "model": "seeddance-2.0", "aspect_ratio": "9_16"},
                     depends_on=["b"]),
        WorkflowStep(step_id="a2", modality="image_t2i",
                     payload={"shot_id": "Z", "prompt": "p", "model": "nbp", "aspect_ratio": "9_16"},
                     depends_on=["a"]),
    ])
    wf.run(context=ctx)
    assert wf.steps[0].status == "failed"      # a — unknown modality
    assert wf.steps[1].status == "succeeded"   # b
    assert wf.steps[2].status == "succeeded"   # b2 (depends on b)
    assert wf.steps[3].status == "skipped"     # a2 (depends on a)
