#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
TemplateContextBuilder 的并发安全单元测试。

历史上 builder 持有 ``self._context`` 实例字段，``build()`` 内 ``clear()`` →
``_add_*`` → 推导式返回这一序列在 ``TRANSFER_THREADS > 1`` 下会被多线程相互
覆盖，导致同一 builder 实例并发调用产生互相串味的 rename_dict。本测试在多
线程下连续调用 ``build()``，校验每个线程拿到的字典只反映自己的入参。
"""
import threading

from app.helper.message import TemplateContextBuilder
from app.schemas.tmdb import TmdbEpisode


THREAD_COUNT = 8
ITERATIONS_PER_THREAD = 200


def _build_fake_meta():
    """
    构造模板上下文测试所需的最小元数据对象。
    """
    meta = type("FakeMeta", (), {})()
    meta.begin_episode = None
    meta.title = "Movie.2024.1080p.x265.10bit.mkv"
    meta.name = "Movie"
    meta.en_name = "Movie"
    meta.year = "2024"
    meta.season_seq = ""
    meta.season = ""
    meta.episode_seqs = ""
    meta.episode = ""
    meta.part = None
    meta.customization = None
    meta.fps = None
    meta.resource_type = None
    meta.resource_effect = None
    meta.edition = ""
    meta.resource_pix = "1080p"
    meta.resource_term = "1080p"
    meta.resource_team = None
    meta.video_encode = "x265 10bit"
    meta.video_bit = "10bit"
    meta.audio_encode = "AAC"
    meta.web_source = None
    return meta


def test_concurrent_build_no_cross_contamination() -> None:
    """
    使用 8 个线程并发调用同一 TemplateContextBuilder 实例的 build()，
    确保各自的 file_extension / 自定义 kwargs 不会被其它线程覆盖。
    """
    builder = TemplateContextBuilder()
    errors = []

    def worker(tag: int) -> None:
        try:
            for _ in range(ITERATIONS_PER_THREAD):
                ctx = builder.build(
                    file_extension=f".{tag}",
                    marker=tag,
                )
                assert ctx.get("fileExt") == f".{tag}"
                assert ctx.get("marker") == tag
        except AssertionError as exc:
            errors.append(exc)

    threads = [
        threading.Thread(target=worker, args=(i,), name=f"builder-{i}")
        for i in range(THREAD_COUNT)
    ]
    for thread in threads:
        thread.start()
    for thread in threads:
        thread.join()

    assert not errors, f"检测到并发串味，共 {len(errors)} 条；首个错误：{errors[0] if errors else ''}"


def test_build_returns_independent_dicts() -> None:
    """
    连续两次 build() 应返回相互独立的 dict 实例，避免调用方误用共享结果。
    """
    builder = TemplateContextBuilder()
    first = builder.build(file_extension=".a", marker=1)
    second = builder.build(file_extension=".b", marker=2)

    assert first is not second
    assert first.get("fileExt") == ".a"
    assert second.get("fileExt") == ".b"
    assert first.get("marker") == 1


def test_build_exposes_video_bit_from_meta() -> None:
    """
    模板上下文应提供独立 videoBit 字段，避免用户只能从 videoCodec 中手工拆位深。
    """
    context = TemplateContextBuilder().build(meta=_build_fake_meta())

    assert context.get("videoCodec") == "x265 10bit"
    assert context.get("videoBit") == "10bit"


def test_build_exposes_total_episodes_from_current_season() -> None:
    """
    模板上下文应提供当前季总集数，供入库通知模板直接引用。
    """
    context = TemplateContextBuilder().build(
        meta=_build_fake_meta(),
        episodes_info=[
            TmdbEpisode(episode_number=1, name="第一集"),
            TmdbEpisode(episode_number=2, name="第二集"),
            TmdbEpisode(episode_number=3, name="第三集"),
        ],
    )

    assert context.get("total_episodes") == 3
