"""기록 신원이 「돈이 나갔다」로 읽히면 안 된다.

_jit_tag_snapshot 은 샷 전후 record 묶음을 직렬화해 비교하고, 달라지면
지출로 읽어 재생성 제동을 건다. shot_run_uid 는 방문마다 바뀌므로 transient
로 걸러야 한다 — 안 그러면 아무것도 안 만든 재사용 방문이 완주 판의 래치를
거짓으로 올린다.

★반대로 너무 넓게 거르면 **일한 방문을 못 가려낸다** — 그쪽이 더 나쁘다.
그래서 shot_run_spend_attempt_count 는 transient 에 안 넣는다.
"""
from app.services.still_recipe_service import _jit_tag_snapshot


class _Rec:
    def __init__(self, data):
        self.data = data


def _snap(uid, produced):
    return _jit_tag_snapshot(
        _Rec({"S1sh1": {
            "prompt": "P", "selected": "B", "input_fingerprint": "fp1",
            "shot_run_uid": uid, "shot_run_produced": produced,
        }}),
        "S1sh1", {"S1sh1"},
    )


def test_visit_uid_alone_does_not_change_snapshot():
    """★두 방문이 uid 만 다르면 스냅숏은 같아야 한다."""
    assert _snap("0190-visit-1", False) == _snap("0190-visit-2", False)


def test_produced_flag_alone_does_not_change_snapshot():
    assert _snap("0190-a", False) == _snap("0190-a", True)


def test_real_spend_still_changes_snapshot():
    """★거르기가 너무 넓으면 일한 방문을 못 가려낸다 — 그것이 더 나쁘다."""
    a = _jit_tag_snapshot(
        _Rec({"S1sh1": {"prompt": "P", "selected": "B",
                        "shot_run_uid": "0190-a"}}), "S1sh1", {"S1sh1"})
    b = _jit_tag_snapshot(
        _Rec({"S1sh1": {"prompt": "P", "selected": "A",   # 선정이 바뀌었다
                        "shot_run_uid": "0190-a"}}), "S1sh1", {"S1sh1"})
    assert a != b


def test_critique_record_still_counts_as_spend():
    a = _jit_tag_snapshot(
        _Rec({"S1sh1": {"prompt": "P", "shot_run_uid": "0190-a"}}),
        "S1sh1", {"S1sh1"})
    b = _jit_tag_snapshot(
        _Rec({"S1sh1": {"prompt": "P", "shot_run_uid": "0190-b",
                        "critique": {"issues": [{"issue_ko": "x"}]}}}),
        "S1sh1", {"S1sh1"})
    assert a != b


def test_retroactive_critique_counts_as_spend_even_when_fix_loses():
    """★★소급 critique 는 유료인데 수리가 지면 저자는 이전 방문이다.

    produced 와 지출 표식을 하나로 쓰면 둘 중 하나가 틀린다:
    True 면 자산 계보를 가로채고, False 면 지출을 놓친다.
    """
    same = {"prompt": "P", "selected": "B", "critique": {"issues": []}}
    a = _jit_tag_snapshot(
        _Rec({"S1sh1": {**same, "shot_run_spend_attempt_count": 1,
                        "shot_run_produced": False}}), "S1sh1", {"S1sh1"})
    b = _jit_tag_snapshot(
        _Rec({"S1sh1": {**same, "shot_run_spend_attempt_count": 2,
                        "shot_run_produced": False}}), "S1sh1", {"S1sh1"})
    assert a != b, "수리가 진 소급 critique 의 지출이 안 잡힌다"


def test_spent_count_is_a_spend_signal():
    """★★유료 복구 갈래 — 판정만 다시 돌아 결과가 같아도 지출은 잡혀야 한다.

    「sel 존재 + 선정 기록 없음 → sel 폐기, 판정만 재수행」 갈래는 롤 생성은
    건너뛰지만 판정을 유료로 다시 돈다. 판정 결과가 전과 같으면 record 가
    안 움직인다 — 계수가 없으면 JIT 가 지출을 놓친다.

    ★이 구멍은 지금도 있다. 계수가 그것을 닫는다.
    """
    same = {"prompt": "P", "selected": "B", "verdicts": [{"label": "B"}]}
    a = _jit_tag_snapshot(
        _Rec({"S1sh1": {**same, "shot_run_uid": "0190-a",
                        "shot_run_spend_attempt_count": 1}}), "S1sh1", {"S1sh1"})
    b = _jit_tag_snapshot(
        _Rec({"S1sh1": {**same, "shot_run_uid": "0190-b",
                        "shot_run_spend_attempt_count": 2}}), "S1sh1", {"S1sh1"})
    assert a != b, "유료 재판정이 지출로 안 잡힌다"


def test_spent_count_unchanged_on_reuse():
    """재사용 방문은 계수가 안 오르므로 스냅숏도 같다."""
    same = {"prompt": "P", "selected": "B", "shot_run_spend_attempt_count": 3}
    a = _jit_tag_snapshot(
        _Rec({"S1sh1": {**same, "shot_run_uid": "0190-a"}}), "S1sh1", {"S1sh1"})
    b = _jit_tag_snapshot(
        _Rec({"S1sh1": {**same, "shot_run_uid": "0190-b"}}), "S1sh1", {"S1sh1"})
    assert a == b


def test_spent_count_is_not_transient():
    """계수를 실수로 _transient 에 넣으면 이 시험이 잡는다."""
    import inspect
    from app.services.still_recipe_service import _jit_tag_snapshot as f
    src = inspect.getsource(f)
    assert "shot_run_spend_attempt_count" not in \
        src.split("_transient = ")[1][:200], \
        "표식이 transient 에 들어갔다 — 유료 구간 진입을 놓친다"
