"""`short_id` → `(갈래, 번호)` **한 계약**. ★유료 0. ★sync 는 아직 안 건드린다.

## 왜 (2026-08-31 실측)

`EntitySyncService` 가 번호를 이렇게 센다 —

    prefix = short_id[0]                 # `LP01` → **"L"**
    int(short_id[1:])                    # int("P01") → ValueError
    except (ValueError, IndexError): 「형식이 이상하다」로 **건너뛴다**

★`LP01` 이 **죽지 않고 조용히 빠진다.** 그러면 `L` counter 가 실제보다
작아져 **이미 있는 `L##` 를 다시 발급**할 수 있다 — 오류도 안 난다.

여기서는 **고치기 전에** 두 가지를 잠근다 —

    ①기존 갈래(C/L/P/O)에서 새 계약이 옛 셈과 **글자까지 같은 답**을 낸다
    ②`LP##` 에서만 **달라진다** — 옛 것은 건너뛰고 새 것은 푼다

★sync 배선은 D 의 1-B 이고 아직 승인 전이다. 이 파일은 **계약과 등가**만 본다.
"""
from __future__ import annotations

import pytest

from app.modules.pipeline.grounding_entity_contract import (OWNER_PREFIX,
                                                            split_final_id)


def old_way(sid: str):
    """★`entity_sync_service.py:71-84` 를 **그대로** 옮긴 것.

    고치기 전 셈을 견주려면 옛 셈이 여기 있어야 한다 — 「기억나는 대로」가
    아니라 **그 자리 코드 그대로**.
    """
    if not (sid and len(sid) > 1):
        return None
    try:
        return sid[0], int(sid[1:])
    except (ValueError, IndexError):
        return None


class TestTheNewContractAgreesOnEverythingThatWorkedBefore:
    """★① 기존 갈래에서 **답이 같아야** 한다 — 안 그러면 번호가 흔들린다."""

    @pytest.mark.parametrize("sid", [
        "C01", "C99", "C100", "L18", "L7", "L103",
        "P01", "P250", "O42", "O9",
    ])
    def test_same_prefix_and_number(self, sid):
        old = old_way(sid)
        new = split_final_id(sid)
        assert old is not None and new is not None, f"★{sid} 를 못 푼다"
        owner, n = new
        assert OWNER_PREFIX[owner] == old[0], \
            f"★{sid}: 접두가 다르다 {OWNER_PREFIX[owner]!r} vs {old[0]!r}"
        assert n == old[1], f"★{sid}: 번호가 다르다 {n} vs {old[1]}"

    @pytest.mark.parametrize("sid", ["", "C", "Pfoo", "L", "xyz", "C08O09"])
    def test_both_refuse_the_same_junk(self, sid):
        """★못 푸는 것도 **같이** 못 풀어야 한다 — 새로 삼키면 안 된다."""
        assert (old_way(sid) is None) == (split_final_id(sid) is None), \
            f"★{sid!r} 에서 갈린다: 옛 {old_way(sid)} · 새 {split_final_id(sid)}"


class TestOnlyLocationPartChanges:
    """★② `LP##` 에서만 달라진다 — 그것이 고치는 이유다."""

    @pytest.mark.parametrize("sid", ["LP01", "LP7", "LP103"])
    def test_the_old_way_silently_drops_it(self, sid):
        assert old_way(sid) is None, \
            f"★{sid} 를 옛 셈이 푼다 — 그럼 고칠 이유가 없다"

    @pytest.mark.parametrize("sid,want", [
        ("LP01", 1), ("LP7", 7), ("LP103", 103)])
    def test_the_new_contract_resolves_it(self, sid, want):
        got = split_final_id(sid)
        assert got == ("location_part", want), f"★{sid} → {got}"

    def test_the_old_way_would_miscount_the_location_counter(self):
        """★★이것이 **조용한 결함**이다.

        `LP20` 이 있는데 옛 셈은 그것을 건너뛴다. 그러면 `L` counter 가
        `LP` 를 못 보고, 새 `location` 을 발급할 때 **이미 쓰인 번호**를
        다시 줄 수 있다. 오류가 안 나서 더 나쁘다.
        """
        ids = ["L05", "LP20"]
        old_max = max((old_way(s) or ("", 0))[1]
                      for s in ids if (old_way(s) or ("",))[0] == "L")
        assert old_max == 5, "★옛 셈이 LP20 을 봤다"
        new = [split_final_id(s) for s in ids]
        assert new[1] == ("location_part", 20), "★새 계약이 LP20 을 못 푼다"
        # ★갈래가 갈리므로 `L` counter 는 5 가 맞고 `LP` counter 가 따로 20 이다
        assert new[0] == ("location", 5)


class TestOneParserNotTwo:
    """★파서를 두 벌로 만들면 한쪽만 고쳐진다."""

    def test_it_uses_the_same_rule_as_owner_of_final_id(self):
        from app.modules.pipeline.grounding_entity_contract import (
            owner_of_final_id)

        for sid in ("C01", "L18", "P03", "O42", "LP01", "Pfoo", ""):
            a = owner_of_final_id(sid)
            b = split_final_id(sid)
            assert (a is None) == (b is None), f"★{sid!r} 에서 갈린다"
            if b is not None:
                assert b[0] == a

    def test_every_owner_prefix_round_trips(self):
        """★갈래를 늘려도 이 시험이 따라간다 — 목록을 손으로 안 적는다."""
        for owner, pre in OWNER_PREFIX.items():
            sid = f"{pre}07"
            assert split_final_id(sid) == (owner, 7), f"★{owner}/{sid}"
