from __future__ import annotations

import importlib.util
import json
import sys
from pathlib import Path

import pytest

_RUNTIME_PATH = (
    Path(__file__).resolve().parents[1]
    / "experiments"
    / "perception"
    / "worker"
    / "e15_shadow_runtime.py"
)
_SPEC = importlib.util.spec_from_file_location("e15_shadow_runtime_test", _RUNTIME_PATH)
assert _SPEC is not None and _SPEC.loader is not None
_RUNTIME = importlib.util.module_from_spec(_SPEC)
sys.modules[_SPEC.name] = _RUNTIME
_SPEC.loader.exec_module(_RUNTIME)
CameraFragmentMetadata = _RUNTIME.CameraFragmentMetadata
IncrementalMediaBuffer = _RUNTIME.IncrementalMediaBuffer
PersistentFmp4Decoder = _RUNTIME.PersistentFmp4Decoder
ShadowRuntimeError = _RUNTIME.ShadowRuntimeError


def _metadata(sequence: int) -> CameraFragmentMetadata:
    return CameraFragmentMetadata(
        ingress_sequence=sequence,
        source_sequence=sequence,
        captured_at_epoch_ns=sequence * 100_000_000,
        worker_received_monotonic=0.0,
    )


def test_incremental_media_buffer_is_bounded_and_fail_closed() -> None:
    source = IncrementalMediaBuffer(1024)
    source.append(b"a" * 800)
    with pytest.raises(ShadowRuntimeError, match="hard bound"):
        source.append(b"b" * 300)
    snapshot = source.snapshot()
    assert snapshot["maximum_depth_bytes"] == 800
    assert snapshot["failed"] is True
    with pytest.raises(ShadowRuntimeError, match="source failed"):
        source.read(100)


def test_persistent_decoder_counts_latest_wins_camera_fragment_gaps() -> None:
    decoder = PersistentFmp4Decoder(on_frame=lambda _frame: None)
    decoder.start()
    decoder.feed_init(b"not-a-real-init")
    decoder.feed_segment(_metadata(1), b"first")
    decoder.feed_segment(_metadata(3), b"third")
    assert decoder.snapshot()["source_sequence_gaps"] == 1
    decoder.finish_input()
    with pytest.raises(ShadowRuntimeError, match="decoder failed"):
        decoder.join()


def test_persistent_decoder_rejects_non_increasing_camera_sequence() -> None:
    decoder = PersistentFmp4Decoder(on_frame=lambda _frame: None)
    decoder.start()
    decoder.feed_init(b"not-a-real-init")
    decoder.feed_segment(_metadata(2), b"second")
    with pytest.raises(ShadowRuntimeError, match="not increasing"):
        decoder.feed_segment(_metadata(2), b"duplicate")
    decoder.finish_input()
    with pytest.raises(ShadowRuntimeError, match="decoder failed"):
        decoder.join()


def test_persistent_decoder_streams_real_fmp4_epoch() -> None:
    pytest.importorskip("av")
    repository = Path(__file__).resolve().parents[1]
    epoch = (
        repository
        / ".runtime"
        / "mission-core"
        / "evidence"
        / "sessions"
        / "20260720T065719Z_viewer_live"
        / "media"
        / "sensor.camera.right"
        / "epoch-1"
    )
    if not epoch.is_dir():
        pytest.skip("canonical RAVNOVES00 camera epoch is unavailable")
    rows = [json.loads(line) for line in (epoch / "index.jsonl").read_text().splitlines()[:10]]
    decoded = []
    decoder = PersistentFmp4Decoder(on_frame=decoded.append)
    decoder.start()
    decoder.feed_init((epoch / "init.mp4").read_bytes())
    for row in rows:
        decoder.feed_segment(
            CameraFragmentMetadata(
                ingress_sequence=int(row["sequence"]),
                source_sequence=int(row["sequence"]),
                captured_at_epoch_ns=int(row["host_epoch_ns"]),
                worker_received_monotonic=0.0,
            ),
            (epoch / row["path"]).read_bytes(),
        )
    decoder.finish_input()
    decoder.join()

    assert len(decoded) == 10
    assert [frame.metadata.source_sequence for frame in decoded] == list(range(1, 11))
    assert all(frame.image.shape == (600, 800, 3) for frame in decoded)
    assert decoder.snapshot()["media"]["depth_bytes"] == 0
