"""생성 LLM 홀드 (pipeline.held_stages) — 컨슈머/워커 게이트 동작 테스트.

홀드 시멘틱: held 스테이지는 claim 자체를 하지 않는다 (attempts 미소모, DB 무접촉).
비-held 스테이지는 기존과 동일하게 처리된다.
"""

import pytest

from core.config import Settings, settings
from workers import digest_worker, queue_consumer


def _fake_consumer_env(monkeypatch, held):
    processed = []

    async def fake_process(stage, worker):
        processed.append(stage)

    async def fake_reset(stages, threshold):
        return None

    monkeypatch.setattr(queue_consumer, "_process_stage", fake_process)
    monkeypatch.setattr(queue_consumer, "reset_stale_items", fake_reset)
    monkeypatch.setattr(
        queue_consumer, "_load_workers",
        lambda: {s: object() for s in queue_consumer.MAIN_QUEUE_STAGES + ["markdown"]},
    )
    monkeypatch.setattr(queue_consumer, "_hold_logged", False)
    monkeypatch.setattr(settings, "pipeline_held_stages", held)
    return processed


def test_settings_default_empty():
    """미설정 시 빈 리스트 = 무동작 (기존 동작 무회귀)."""
    assert Settings().pipeline_held_stages == []


@pytest.mark.asyncio
async def test_consume_queue_skips_held_stages(monkeypatch):
    processed = _fake_consumer_env(
        monkeypatch, ["classify", "summarize", "deep_summary"]
    )

    await queue_consumer.consume_queue()

    assert "classify" not in processed
    assert "summarize" not in processed
    assert "deep_summary" not in processed
    # GPU/특화 스테이지는 계속 처리
    for stage in ("extract", "embed", "chunk", "stt", "fulltext"):
        assert stage in processed


@pytest.mark.asyncio
async def test_consume_queue_empty_hold_processes_all(monkeypatch):
    processed = _fake_consumer_env(monkeypatch, [])

    await queue_consumer.consume_queue()

    assert processed == list(queue_consumer.MAIN_QUEUE_STAGES)


@pytest.mark.asyncio
async def test_markdown_consumer_not_held(monkeypatch):
    """markdown 컨슈머는 홀드 비대상 (LLM 무관 — marker GPU 변환)."""
    processed = _fake_consumer_env(
        monkeypatch, ["classify", "summarize", "deep_summary", "digest"]
    )

    await queue_consumer.consume_markdown_queue()

    assert processed == ["markdown"]


@pytest.mark.asyncio
async def test_digest_worker_held_returns_before_pipeline(monkeypatch):
    called = {"pipeline": False}

    async def fake_pipeline():
        called["pipeline"] = True
        return {}

    monkeypatch.setattr(digest_worker, "run_digest_pipeline", fake_pipeline)
    monkeypatch.setattr(settings, "pipeline_held_stages", ["digest"])

    await digest_worker.run()

    assert called["pipeline"] is False


@pytest.mark.asyncio
async def test_digest_worker_unheld_runs_pipeline(monkeypatch):
    called = {"pipeline": False}

    async def fake_pipeline():
        called["pipeline"] = True
        return {"clusters": 0}

    monkeypatch.setattr(digest_worker, "run_digest_pipeline", fake_pipeline)
    monkeypatch.setattr(settings, "pipeline_held_stages", [])

    await digest_worker.run()

    assert called["pipeline"] is True


@pytest.mark.asyncio
async def test_briefing_worker_held_returns_before_pipeline(monkeypatch):
    from workers import briefing_worker

    called = {"pipeline": False}

    async def fake_pipeline(target_date):
        called["pipeline"] = True
        return {}

    monkeypatch.setattr(briefing_worker, "run_briefing_pipeline", fake_pipeline)
    monkeypatch.setattr(settings, "pipeline_held_stages", ["briefing"])

    assert await briefing_worker.run() is None
    assert called["pipeline"] is False


@pytest.mark.asyncio
async def test_study_explanation_consumer_held(monkeypatch):
    from workers import study_queue_consumer

    touched = []

    async def fake_reset():
        touched.append("reset")

    monkeypatch.setattr(study_queue_consumer, "reset_stale_study_jobs", fake_reset)
    monkeypatch.setattr(settings, "pipeline_held_stages", ["study_explanation"])

    await study_queue_consumer.consume_study_queue()

    assert touched == []


@pytest.mark.asyncio
async def test_study_consumers_held_no_db_touch(monkeypatch):
    """held 시 stale reset 포함 DB 접근 0 — claim 미발생 실증."""
    from workers import study_memo_card_jobs_consumer, study_session_queue_consumer

    touched = []

    async def fake_reset_session():
        touched.append("session_reset")

    async def fake_reset_card():
        touched.append("card_reset")

    monkeypatch.setattr(
        study_session_queue_consumer, "reset_stale_session_jobs", fake_reset_session
    )
    monkeypatch.setattr(
        study_memo_card_jobs_consumer, "reset_stale_card_jobs", fake_reset_card
    )
    monkeypatch.setattr(
        settings, "pipeline_held_stages",
        ["study_session_analysis", "study_memo_card"],
    )

    await study_session_queue_consumer.consume_study_session_queue()
    await study_memo_card_jobs_consumer.consume_study_memo_card_queue()

    assert touched == []
