"""병렬 worker 도 같은 부모 trace 를 봐야 한다.

★ContextVar 는 thread 를 안 넘는다. Task 3 의 bind_trace 시험은 같은
thread 안에서만 봤다 — 그것으로는 이 결함을 못 잡는다. 여기서는 진짜
ThreadPoolExecutor 를 돌린다.
"""
from concurrent.futures import ThreadPoolExecutor

import pytest

from app.modules.llm.opik_trace import (
    TraceHandle, bind_current_trace, bind_trace, current_trace, reset_trace)


def test_contextvar_does_not_cross_threads_by_itself():
    """전제를 못박는다 — 이것이 참이라 전파기가 필요하다."""
    token = bind_trace(TraceHandle(uid="0190-parent", name="still:x"))
    try:
        with ThreadPoolExecutor(max_workers=1) as pool:
            assert pool.submit(current_trace).result() is None
    finally:
        reset_trace(token)


def test_bind_current_trace_carries_parent_into_worker():
    token = bind_trace(TraceHandle(uid="0190-parent", name="still:x"))
    try:
        bound = bind_current_trace(lambda: current_trace())
        with ThreadPoolExecutor(max_workers=2) as pool:
            uids = [f.result().uid for f in
                    [pool.submit(bound) for _ in range(4)]]
        assert uids == ["0190-parent"] * 4
    finally:
        reset_trace(token)


def test_worker_restores_after_run():
    """worker 안에서 세운 값이 그 thread 에 남으면 다음 작업이 물려받는다."""
    token = bind_trace(TraceHandle(uid="0190-a", name="a"))
    try:
        bound = bind_current_trace(lambda: current_trace().uid)
        with ThreadPoolExecutor(max_workers=1) as pool:
            assert pool.submit(bound).result() == "0190-a"
            # 같은 thread 를 재사용하는데, 전파기 없이 부르면 비어 있어야 한다
            assert pool.submit(current_trace).result() is None
    finally:
        reset_trace(token)


def test_no_parent_is_not_an_error():
    bound = bind_current_trace(lambda: current_trace())
    with ThreadPoolExecutor(max_workers=1) as pool:
        assert pool.submit(bound).result() is None


def test_wrapper_is_transparent_to_args_and_exceptions():
    token = bind_trace(TraceHandle(uid="0190-a", name="a"))
    try:
        def _fn(a, b, *, c):
            if a == "boom":
                raise ValueError("터짐")
            return (a, b, c, current_trace().uid)

        bound = bind_current_trace(_fn)
        with ThreadPoolExecutor(max_workers=1) as pool:
            assert pool.submit(bound, 1, 2, c=3).result() == (1, 2, 3, "0190-a")
            with pytest.raises(ValueError):
                pool.submit(bound, "boom", 2, c=3).result()
    finally:
        reset_trace(token)
