#!/usr/bin/env python3
"""exp12 — реконструкция per-chunk инъекционных блоков глоссария из приёмочного store.

$0. Воспроизводит ровно то, что боевой редактор A1 (glm-5 mono) видел:
  - editorConstraintBlock (renderEditorConstraintBlock, memory.go): CONFIRMED dst-формы,
    строки «- «dst»», дедуп по dst. Заголовок editorConstraintHeader.
  - bilingualGlossaryBlock (renderGlossaryBlock, memory.go): «src → dst» (+ ⟨проверить⟩
    для не-CONFIRMED). Заголовок glossaryBlockHeader. Для билингв-армов (редактор видит src).

Активные ключи чанка = injected_ids(chunk) ∪ injected_ids(chunk0) для chunk>0 (аппрокс sticky
A5 внутри главы; выбранные главы — 2-чанковые, sticky несёт ровно активные chunk0).
Ключ injected_id = «src\x1fsense\x1fsince\x1funtil» → строка glossary по (src,sense,since,until).
CONFIRMED ⇔ status='approved' и окно since/until валидно для главы.

Выход: diag/blocks.json {(ch/chunk): {editor_constraint, bilingual_glossary, keys[]}}
"""
from __future__ import annotations
import json, sqlite3
from pathlib import Path

DIAG = Path("/home/ubuntu/books/gu-zhenren/diag")
DB = DIAG / "store-copy.db"
SEL = [5, 7, 14]  # пре-регистрированный выбор (exp12_extract.py)

EDITOR_HEADER = ("КАНОНИЧЕСКИЕ ПЕРЕВОДЫ имён и терминов (в черновике эти сущности должны быть "
                 "переданы ИМЕННО этими формами — приводи к ним любые расхождения, склоняя по "
                 "контексту; не вводи иных вариантов и не меняй ничего другого):")
GLOSS_HEADER = ("ГЛОССАРИЙ (используй эти утверждённые переводы имён и терминов последовательно; "
                "строки с пометкой ⟨проверить⟩ — неподтверждённые кандидаты):")


def main() -> None:
    con = sqlite3.connect(DB)
    con.row_factory = sqlite3.Row
    # glossary lookup by (src,sense,since,until)
    gl = {}
    for r in con.execute("SELECT src,sense,since_ch,until_ch,dst,status FROM glossary"):
        gl[(r["src"], r["sense"], r["since_ch"], r["until_ch"])] = (r["dst"], r["status"])

    # injected_ids per selected chunk
    inj = {}
    for r in con.execute(
        "SELECT chapter,chunk_idx,injected_ids FROM retrieval_state WHERE chapter IN (%s)"
        % ",".join(map(str, SEL))
    ):
        inj[(r["chapter"], r["chunk_idx"])] = json.loads(r["injected_ids"] or "[]")
    con.close()

    def keydec(k: str):
        src, sense, since, until = k.split("\x1f")
        return (src, sense, int(since), int(until))

    out = {}
    for ch in SEL:
        chunks = sorted(c for (cc, c) in inj if cc == ch)
        chunk0 = inj.get((ch, 0), [])
        for c in chunks:
            active = list(inj.get((ch, c), []))
            if c > 0:  # sticky: carry chunk0's exact matches (A5 scene inertia within chapter)
                for k in chunk0:
                    if k not in active:
                        active.append(k)
            # map to records
            recs = []
            for k in active:
                src, sense, since, until = keydec(k)
                dst, status = gl.get((src, sense, since, until), ("", "auto"))
                if not dst.strip():
                    continue
                confirmed = (status == "approved")  # windows all 0/0 here → spoiler-valid
                recs.append({"src": src, "dst": dst.strip(), "confirmed": confirmed})

            # editor constraint block: CONFIRMED only, dedup by dst
            seen, elines = set(), []
            for r in recs:
                if not r["confirmed"] or r["dst"] in seen:
                    continue
                seen.add(r["dst"]); elines.append(f"- «{r['dst']}»")
            editor_block = (EDITOR_HEADER + "\n" + "\n".join(elines)) if elines else ""

            # bilingual glossary block: src → dst (+ ⟨проверить⟩ if not confirmed)
            glines = []
            for r in recs:
                line = f"{r['src']} → {r['dst']}"
                if not r["confirmed"]:
                    line += "  ⟨проверить⟩"
                glines.append(line)
            gloss_block = (GLOSS_HEADER + "\n" + "\n".join(glines)) if glines else ""

            out[f"{ch}/{c}"] = {
                "editor_constraint": editor_block,
                "bilingual_glossary": gloss_block,
                "n_constraint_terms": len(elines),
                "n_gloss_terms": len(glines),
            }

    (DIAG / "blocks.json").write_text(json.dumps(out, ensure_ascii=False, indent=1), encoding="utf-8")
    for k, v in out.items():
        print(f"\n=== chunk {k}: {v['n_constraint_terms']} constraint / {v['n_gloss_terms']} gloss terms ===")
        print(v["editor_constraint"][:400])


if __name__ == "__main__":
    main()
