"""bind_current_generation_context — worker thread 명시 전파 (Codex BLOCKING-3).

GenerationContext 는 ContextVar 라 ThreadPoolExecutor worker 에 자동 전파되지
않는다(context.py docstring 계약). 병렬 롤 생성에서 capture 가
skipped_no_context 로 유실되지 않으려면 budget bind 와 함께 명시 전파가 필요.
"""
from __future__ import annotations

import threading
from concurrent.futures import ThreadPoolExecutor

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


def test_bind_captures_parent_context_into_worker_thread():
    with generation_context("p1", "e1", "still_recipe") as _q:
        parent_ctx = current_context()
        bound = bind_current_generation_context(lambda: current_context())

        seen: dict = {}

        def _body():
            seen["in_call"] = bound()
            seen["after"] = current_context()

        t = threading.Thread(target=_body)
        t.start()
        t.join()
    assert seen["in_call"] is parent_ctx
    assert seen["after"] is None  # worker 는 호출 후 clean


def test_bind_no_op_without_context():
    assert current_context() is None
    bound = bind_current_generation_context(lambda: current_context())
    seen: dict = {}

    def _body():
        seen["ctx"] = bound()

    t = threading.Thread(target=_body)
    t.start()
    t.join()
    assert seen["ctx"] is None


def test_bind_propagates_through_thread_pool():
    with generation_context("p1", "e1", "still_recipe"):
        parent_ctx = current_context()
        bound = bind_current_generation_context(lambda: current_context())
        with ThreadPoolExecutor(max_workers=2) as pool:
            results = [pool.submit(bound).result() for _ in range(4)]
    assert all(r is parent_ctx for r in results)


def test_capture_queue_concurrent_append_no_loss():
    """병렬 worker 가 같은 scope 큐에 append — 유실·중복 없이 전건 적재."""
    with generation_context("p1", "e1", "still_recipe") as q:
        def _append(i: int) -> None:
            q.append(f"/spool/{i}.png", {"i": i})

        with ThreadPoolExecutor(max_workers=8) as pool:
            list(pool.map(_append, range(200)))
        items = list(q._items)
    assert len(items) == 200
    assert {m["i"] for _, m in items} == set(range(200))
