"""Task A2 — GenerationContext (contextvar + scope + worker bind) 결정론 테스트."""

import threading

from app.services.image_capture.context import (
    GenerationContext,
    bind_context,
    current_context,
    generation_context,
)


def test_default_context_is_none():
    assert current_context() is None


def test_scope_sets_and_resets_context():
    assert current_context() is None
    with generation_context("p", "e", "stage") as q:
        ctx = current_context()
        assert ctx is not None
        assert ctx.project_id == "p"
        assert ctx.episode_id == "e"
        assert ctx.stage == "stage"
        # yield 값은 이 scope 의 큐 — ctx.queue 와 동일 객체
        assert q is ctx.queue
    # 블록 밖에서 다시 None (reset)
    assert current_context() is None


def test_scope_resets_on_exception():
    try:
        with generation_context("p", "e", "stage"):
            raise ValueError("boom")
    except ValueError:
        pass
    assert current_context() is None


def test_scope_carries_optional_kwargs():
    with generation_context(
        "p", "e", "pose_guide", still_id="s1", scene_index=12, shot_index=8, entity_id="C04"
    ):
        ctx = current_context()
        assert ctx.still_id == "s1"
        assert ctx.scene_index == 12
        assert ctx.shot_index == 8
        assert ctx.entity_id == "C04"


def test_generation_context_is_frozen():
    with generation_context("p", "e", "stage"):
        ctx = current_context()
        try:
            ctx.stage = "mutated"  # type: ignore[misc]
            raised = False
        except Exception:
            raised = True
        assert raised, "GenerationContext 는 frozen 이어야 한다"


def test_bind_context_propagates_to_thread():
    """worker thread 는 contextvars 가 자동 전파 안 됨 → bind_context 로 명시 전파."""
    seen: dict[str, object] = {}

    with generation_context("p", "e", "stage") as q:
        captured_ctx = current_context()

        def worker():
            # thread 진입 시 자동 전파 안 됨 — 처음엔 None
            seen["before"] = current_context()
            token = bind_context(captured_ctx)
            try:
                inner = current_context()
                seen["after_stage"] = inner.stage if inner else None
                seen["after_queue_is_q"] = (inner.queue is q) if inner else False
            finally:
                # bind_context 반환값으로 reset 가능해야 함
                from app.services.image_capture.context import _gen_ctx

                _gen_ctx.reset(token)
            seen["after_reset"] = current_context()

        t = threading.Thread(target=worker)
        t.start()
        t.join()

    assert seen["before"] is None
    assert seen["after_stage"] == "stage"
    assert seen["after_queue_is_q"] is True
    assert seen["after_reset"] is None
