"""상한은 **자리 수**로 센다 — 같은 자리를 새 신원으로 다시 사는 것은 상한을 안 늘린다.

★실측 2026-09-02 밤 (attempt b63fd8b6, 유료 0): 처리 계약 4 로 행 집합이 바뀌자 merge 의
payload 신원이 달라졌고, 상한 2(구간 1 + merge 1)에 「이미 2회」로 막혀 force 재실행이
통째로 죽었다. 옛 merge 줄은 감사용으로 남고, 자리 수만 상한에 댄다.
"""
from __future__ import annotations

import pytest

from app.modules.pipeline import grounding_chunk_journal as cj


def _j(tmp_path):
    return cj.ChunkJournal(tmp_path / "j.json", contract={"acquisition": {"pack": "x"}, "chunks": 1})


def test_a_superseded_merge_slot_can_be_rebought_within_the_cap(tmp_path):
    j = _j(tmp_path)
    cj.buy_or_reuse(j, "read-c0", cap=2, send=lambda: {"rows": []}, slot="chunk:c0")
    cj.buy_or_reuse(j, "merge-v1", cap=2, send=lambda: {"decisions": []}, slot="merge")
    got = cj.buy_or_reuse(j, "merge-v2", cap=2, send=lambda: {"decisions": [1]}, slot="merge")
    assert got == {"decisions": [1]}
    assert j.bought() == 3 and j.bought_slots() == 2, "★보낸 횟수는 셋 · 자리는 둘"
    # 옛 줄은 지워지지 않는다 — 감사가 본다
    assert j.entries["merge-v1"]["status"] == cj.STATUS_OK


def test_a_new_slot_beyond_the_cap_still_stops(tmp_path):
    j = _j(tmp_path)
    cj.buy_or_reuse(j, "read-c0", cap=2, send=lambda: {}, slot="chunk:c0")
    cj.buy_or_reuse(j, "merge-v1", cap=2, send=lambda: {}, slot="merge")
    with pytest.raises(cj.BudgetExceeded):
        cj.buy_or_reuse(j, "read-c1", cap=2, send=lambda: {}, slot="chunk:c1")


def test_without_slots_the_old_meaning_is_unchanged(tmp_path):
    j = _j(tmp_path)
    cj.buy_or_reuse(j, "a", cap=2, send=lambda: {})
    cj.buy_or_reuse(j, "b", cap=2, send=lambda: {})
    with pytest.raises(cj.BudgetExceeded):
        cj.buy_or_reuse(j, "c", cap=2, send=lambda: {})
    assert j.bought_slots() == j.bought() == 2


def test_reuse_of_a_bought_identity_keeps_its_slot(tmp_path):
    j = _j(tmp_path)
    cj.buy_or_reuse(j, "merge-v1", cap=1, send=lambda: {"d": 1}, slot="merge")
    again = cj.ChunkJournal(tmp_path / "j.json", contract={"acquisition": {"pack": "x"}, "chunks": 1})
    assert again.entries["merge-v1"]["slot"] == "merge"
    assert cj.buy_or_reuse(again, "merge-v1", cap=1, send=lambda: {"d": 2}, slot="merge") == {"d": 1}
    assert again.bought_slots() == 1


def test_the_chunk_step_names_its_slots():
    """★production 이 자리 이름을 실제로 넘긴다 — 읽기는 구간마다, merge 는 하나."""
    import inspect
    from app.core.steps import grounding_chunk_step as gcs
    src = inspect.getsource(gcs.GroundingChunkStep)
    assert 'slot=f"chunk:{chunk.get(\'chunk_id\')}"' in src and 'slot="merge"' in src
