"""Task A5 — sink (capture_generated_image / capture_artifact) 결정론 테스트.

default-capture-off 핵심: scope(generation_context) 밖에서 호출하면 저장 안 함
(skipped_no_context 증가). 이게 최종물 경로(scope 미사용)의 중복 0 을 보장한다.
"""

from pathlib import Path

from app.services.image_capture import sink as sink_mod
from app.services.image_capture.context import current_context, generation_context
from app.services.image_capture.queue import CAPTURE_DIAG
from app.services.image_capture.sink import capture_artifact, capture_generated_image


def test_capture_outside_scope_skips_and_counts():
    before = CAPTURE_DIAG["skipped_no_context"]
    assert current_context() is None
    # scope 밖 — 저장 안 함, raise 안 함, diag 증가
    capture_generated_image(b"x", role="pose_guide")
    capture_artifact(b"y", role="aerial_base")
    assert CAPTURE_DIAG["skipped_no_context"] == before + 2


def test_capture_inside_scope_spools_and_appends():
    with generation_context("p-sink", "e-sink", "pose_guide") as q:
        assert len(q._items) == 0
        capture_generated_image(
            b"PNG",
            role="pose_guide",
            input_image_ids=["bg-1", "base-2"],
            candidate_index=1,
            generation_call_id="call-9",
            prompt="a prompt",
            pipeline_metadata={"group_id": "g1"},
        )
        assert len(q._items) == 1
        spool_path, meta = q._items[0]
        # spool 파일 실재 + 바이트 일치
        assert Path(spool_path).exists()
        assert Path(spool_path).read_bytes() == b"PNG"
        # 메타 보존
        assert meta["pipeline_role"] == "pose_guide"
        assert meta["input_image_ids"] == ["bg-1", "base-2"]
        assert meta["candidate_index"] == 1
        assert meta["generation_call_id"] == "call-9"
        assert meta["prompt"] == "a prompt"
        assert meta["pipeline_metadata"] == {"group_id": "g1"}
    # scope 종료 후 flush(독립세션) — 큐 비워짐 + spool rename 됨
    assert not Path(spool_path).exists()


def test_capture_artifact_same_path():
    with generation_context("p-sink2", "e-sink2", "aerial_base") as q:
        capture_artifact(b"ABC", role="aerial_base", pipeline_metadata={"k": "v"})
        assert len(q._items) == 1
        spool_path, meta = q._items[0]
        assert Path(spool_path).read_bytes() == b"ABC"
        assert meta["pipeline_role"] == "aerial_base"


def test_capture_failure_is_non_fatal(monkeypatch):
    """write_spool 이 raise 해도 sink 는 삼킨다(생성 파이프라인 무영향)."""
    def _boom(*a, **k):
        raise RuntimeError("disk full")

    monkeypatch.setattr(sink_mod, "write_spool", _boom)
    with generation_context("p-sink3", "e-sink3", "pose_guide") as q:
        # raise 안 함
        capture_generated_image(b"x", role="pose_guide")
        # 적재 실패 — 큐 비어 있음
        assert len(q._items) == 0
