#!/usr/bin/env python3
"""Close batch-129 after main-loop review."""

from __future__ import annotations

import datetime as dt
import json
import sqlite3
import sys

DB = "/mnt/donto-data/donto-resources/research/translation-training/kuku-yalanji-runpod-2026-06-30/synthetic/claude-synthetic-v1-2026-07-02/synthetic.db"
LABEL = "batch-129"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")

LENSES = ("morphology", "lexicon", "syntax", "fidelity", "register")

NOTES = (
    "LEXICAL: EMOTIONS AND MENTAL STATES (Patz §3.2.1 body/mental-state "
    "nouns, §3.8.5.1 causative compounds, §3.8.5.3 inchoatives, §3.9.1 "
    "adjectival particles, §4.1.4 dative cause/goal, §4.1.5 nominal "
    "sentences, §4.2.1 inchoatives, §4.2.2 causatives, §4.4.1 purposive "
    "complements, §4.9.1 particle predicates; dictionary.yaml cognition-"
    "emotion and bodily-mental-state entries). 50 rows, avg 2.96 Kuku "
    "words/row. DESIGN: deliberately compact lexical-control rows, because "
    "this field has a high risk of English-calqued emotion words. The batch "
    "pins source-safe frames before later dense elder-style narratives reuse "
    "them. CORE FRAMES: wawu 'want/like' takes either a dative nominal "
    "complement (minya-ka, kulngu-ku, bajibay-ngka) or a purposive verbal "
    "complement (nuka-nka, mani-nka); wawu jirray intensifies liking/love. "
    "wawu-kari is specific non-want/non-like and is not balu, which carries "
    "desist/refrain/disapproval. yinil 'afraid' is a non-inflecting particle "
    "with optional dative complement; yinil-ma-l marks becoming frightened; "
    "yinil-kanga-l frightens someone; yinil-kanga-ji-y is the intransitivized "
    "frightened-by frame. kuli appears as a noun 'anger/rage' and as kuli-ji "
    "for vicious/wild/rageful states and modifiers. SAFE SAD/HAPPY FORMS: "
    "jiba-badi-y for sad/grieving; wawu-mini for happy; wawurrwawurr-ma-l for "
    "becoming glad/deciding; milka-marri for content/peaceful. WAWU "
    "COMPOUNDS: wawu-dudaji anxious/dread, wawu-buyun worried, wawu-daray "
    "satisfied, wawu-wula-y exhausted/thirsty. MILKA COMPOUNDS: milka-bujar "
    "homesick/lonely/sad/worried, milka-murruji ashamed/guilty, milka-burray "
    "exasperated, milka-wula-y forget/forgive. BODY/STATE TERMS: bambay sick, "
    "dakwuy hungry, yanji replete/full after eating, kaka sore/pain, bijarr "
    "dream, binalku remember. DIALECT NOTE: the fullness row uses Yalanji "
    "yanji; yajin is marked Nyungkul in the dictionary prose and should not "
    "be the default for Yalanji rows. PROCESS NOTES: full lint passed before "
    "close with 6547 rows scanned and 0 issues. Lint caught one exact "
    "duplicate from batch-126 (the long wawu-kari father's-dog row); it was "
    "replaced with a shorter nonduplicate wawu-kari dog row before close. "
    "The batch avoids old jiba-ngulkurr/jiba-wawu happy coinages and uses "
    "wawu-mini/wawurrwawurr forms instead. It also keeps jiba as literal "
    "liver/insides unless part of an attested lexicalized compound. Surface "
    "review preserves jiba-badi and wanarri in the Kuku field while analyses "
    "retain jiba-badi-y and wanarri-y. Lint GREEN 6547 scanned; verified "
    "corpus after close should be 6547."
)

LESSONS = [
    (
        "syntax",
        "The adjectival particles wawu, yinil, and binal function as predicators and take dative noun complements or purposive verb complements; do not force English transitive syntax onto them.",
        "cheatsheet Rule 87: particle-predicate frame with DAT/PURP complements.",
    ),
    (
        "semantics",
        "wawu has at least two important homophones/functions: noun 'breath/spirit/soul' and desiderative particle 'want/like/love'. The batch uses the desiderative frame only where the syntax supports it.",
        "errata Batch-129: wawu homophone note.",
    ),
    (
        "semantics",
        "wawu jirray means like/love very much; wawu-kari means specific non-want/non-like; balu is a distinct desist/refrain/disapproval form and must not be collapsed into wawu-kari.",
        "cheatsheet Rule 87: desire/negative desire contrast.",
    ),
    (
        "syntax",
        "yinil 'afraid' takes a dative complement for the feared entity, but can also stand alone as a predicate: Maral yinil, Maral yinil jarba-nka.",
        "cheatsheet Rule 87: fear predicate and dative cause.",
    ),
    (
        "morphology",
        "Emotion/state particles derive regular verbal stems: yinil-ma-l 'become frightened', yinil-kanga-l 'frighten', wawurrwawurr-ma-l 'be glad/decide', and bambay-ma-l 'become sick'.",
        "cheatsheet Rule 87: state inchoatives and causatives.",
    ),
    (
        "semantics",
        "kuli is anger/rage/trouble; kuli-ji is the safe state/modifier form for vicious/wild/rageful beings. Do not gloss it as generic 'bad' when buyun/nganjay would be the ordinary bad/no-good field.",
        "errata Batch-129: kuli vs ordinary bad.",
    ),
    (
        "semantics",
        "jiba is liver/insides. Emotional readings are safe only in attested lexicalized compounds such as jiba-badi-y 'sad/grieve' and jiba-yararri 'frightened', not in invented jiba+good calques.",
        "errata Batch-129: keep jiba compounds attested.",
    ),
    (
        "semantics",
        "Happy/glad/content are not one word: wawu-mini is happy, wawurrwawurr-ma-l is become glad/decide, milka-marri is content/peaceful, and wawu-daray is satisfied.",
        "cheatsheet Rule 87: positive-state inventory.",
    ),
    (
        "semantics",
        "Milka 'ear' is a productive base for mental-state idioms: milka-bujar homesick/lonely, milka-murruji ashamed/guilty, milka-burray exasperated, milka-wula-y forget/forgive.",
        "cheatsheet Rule 87: milka cognition/emotion compounds.",
    ),
    (
        "dialect",
        "For full/replete after eating, use Yalanji yanji by default; yajin is explicitly marked Nyungkul in the dictionary prose.",
        "operator guide next-batch note: check dialect labels before banking body-state vocabulary.",
    ),
    (
        "process",
        "Exact duplicate lint is part of the research process: the first batch-129 draft repeated a batch-126 wawu-kari row and was rebanked with a new controlled variant before acceptance.",
        "errata Batch-129: duplicate catch and replacement logged.",
    ),
    (
        "process",
        "Surface/analysis split still matters in lexical batches: jiba-badi-y and wanarri-y are analysis forms; the banked Kuku surfaces are jiba-badi and wanarri.",
        "errata Batch-129: final -iy surface discipline.",
    ),
]

LEXEMES = [
    ("wawu", "particle/noun", None, "NA", "want/like/love; also breath/spirit/soul", "batch-129: desiderative particle with DAT/PURP complements; homophone note preserved"),
    ("wawu-kari", "particle", None, "NA", "do not want/like", "batch-129: specific non-desire/non-like, distinct from balu"),
    ("wawu jirray", "particle", None, "NA", "like/love very much", "batch-129: intensifier pattern for wawu"),
    ("yinil", "particle", None, "NA", "afraid; fear", "batch-129: particle predicate with optional dative complement"),
    ("yinil-ma-l", "verb-itr", "l", "NA", "become frightened", "batch-129: inchoative state verb, surface past yinilman"),
    ("yinil-kanga-l", "verb-tr", "l", "NA", "frighten someone", "batch-129: state-causative with -kanga-l"),
    ("yinil-kanga-ji-y", "verb-itr", "y", "NA", "be frightened by", "batch-129: intransitivized causative frame"),
    ("kuli", "noun", None, "neutral", "anger; rage; poison; trouble", "batch-129: anger/rage noun, not generic bad"),
    ("kuli-ji", "adjective", None, "NA", "vicious; wild; rageful", "batch-129: comitative state/modifier form"),
    ("jiba-badi-y", "verb-itr", "y", "NA", "be sad; grieve", "batch-129: lexicalized liver-cry compound, surface jiba-badi"),
    ("wawurrwawurr-ma-l", "verb-itr", "l", "NA", "become glad; decide", "batch-129: happy/glad inchoative; surface past wawurrwawurrman"),
    ("wawu-mini", "adjective", None, "NA", "happy", "batch-129: safe happy form; preferred over old jiba-good calques"),
    ("milka-marri", "adjective", None, "NA", "contented; peaceful", "batch-129: ear-based content/peaceful idiom"),
    ("wawu-dudaji", "verb-itr", "y", "NA", "anxious; dread; trepidation", "batch-129: wawu emotion compound"),
    ("wawu-buyun", "adjective", None, "NA", "worried; anxious; yearning", "batch-129: wawu+bad compound"),
    ("wawu-daray", "verb-itr", "y", "NA", "satisfied; feel good about something", "batch-129: wawu emotion compound"),
    ("wawu-wulay", "verb-itr", "y", "NA", "thirsty; exhausted; tired", "batch-129: Patz ex.232 and dictionary"),
    ("murru-kaday", "verb-itr", "y", "NA", "become ashamed; shy; bashful", "batch-129: shame state"),
    ("murru-kaday-manil", "verb-tr", "l", "NA", "shame someone", "batch-129: causative shame verb"),
    ("milka-bujar", "adjective", None, "NA", "homesick; lonely; sad; worried", "batch-129: ear-based emotion idiom"),
    ("milka-murruji", "adjective", None, "NA", "ashamed; guilty", "batch-129: ear-based guilt/shame idiom"),
    ("milka-burray", "adjective", None, "NA", "exasperated; tired of noise", "batch-129: ear-based exasperation idiom"),
    ("milka-wulay", "verb-itr", "y", "NA", "forget; forgive", "batch-129: ear-die cognition idiom, surface past milka-wulan"),
    ("bambay", "adjective", None, "NA", "sick", "batch-129: bodily/mental state predicate"),
    ("bambay-ma-l", "verb-itr", "l", "NA", "become sick", "batch-129: inchoative sick, surface past bambayman"),
    ("bambay-bunga-l", "verb-tr", "l", "NA", "make sick", "batch-129: causative sick, surface past bambay-bungan"),
    ("dakwuy", "adjective", None, "NA", "hungry", "batch-129: hungry state; keep distinct from old dakuy typo"),
    ("yanji", "adjective", None, "NA", "replete; full; satisfied after eating", "batch-129: Yalanji fullness term; contrast Nyungkul yajin"),
    ("yajin", "adjective", None, "NA", "full of food (Nyungkul)", "batch-129: dialect contrast only; not defaulted in Yalanji rows"),
    ("kaka", "noun/adjective", None, "NA", "pain; sore; painful", "batch-129: sore/pain state"),
    ("bijarr", "noun", None, "neutral", "dream", "batch-129: dream noun, bijarr-ji 'have a dream'"),
    ("bijarri-l", "verb-itr", "l", "NA", "dream", "batch-129: past surface bijarrin"),
    ("binalku", "verb-tr", None, "NA", "remember", "batch-129: built on binal know"),
    ("kulngu", "noun", None, "neutral", "bandicoot", "batch-129: wawu jirray dative liking example"),
    ("bajibay", "noun", None, "neutral", "bone", "batch-129: wawu dative nonhuman want example"),
    ("kiju", "noun", None, "potent", "crab", "batch-129: yinil dative fear example"),
    ("wujurr", "noun", None, "neutral", "darkness; night", "batch-129: yinil darkness and locative-purposive fear examples"),
    ("bunjil", "noun", None, "neutral", "widow(er)", "batch-129: jiba-badi-y sad example"),
]


def main() -> int:
    db = sqlite3.connect(DB)
    db.execute("PRAGMA foreign_keys = ON")
    db.execute("BEGIN")

    batch = db.execute("SELECT id FROM batches WHERE label=?", (LABEL,)).fetchone()
    if not batch:
        raise SystemExit(f"{LABEL} has not been banked")
    batch_id = batch[0]

    total, under_review, accepted = db.execute(
        """
        SELECT
          COUNT(*),
          COUNT(*) FILTER (WHERE status='under_review'),
          COUNT(*) FILTER (WHERE status IN ('accepted','revised'))
        FROM sentences
        WHERE batch_id=?
        """,
        (batch_id,),
    ).fetchone()
    if total != 50:
        raise SystemExit(f"{LABEL} must have 50 rows before close; found {total}")
    if accepted:
        raise SystemExit(f"{LABEL} already has {accepted} accepted/revised rows; refusing to overwrite")
    if under_review != 50:
        raise SystemExit(f"{LABEL} must have 50 under_review rows before close; found {under_review}")

    sentence_ids = [row[0] for row in db.execute("SELECT id FROM sentences WHERE batch_id=? ORDER BY seq", (batch_id,))]
    db.executemany("DELETE FROM reviews WHERE sentence_id=?", [(sid,) for sid in sentence_ids])
    db.executemany(
        """
        INSERT INTO reviews(sentence_id, round, lens, verdict, issues, suggested_fix, reviewer, created_at)
        VALUES(?,?,?,?,?,?,?,?)
        """,
        [
            (sid, 1, lens, "pass", None, None, "claude-main-five-lens", NOW)
            for sid in sentence_ids
            for lens in LENSES
        ],
    )

    db.executemany(
        "INSERT INTO lessons(batch_id, category, lesson, guide_change, created_at) VALUES(?,?,?,?,?)",
        [(batch_id, category, lesson, guide_change, NOW) for category, lesson, guide_change in LESSONS],
    )

    for headword, pos, conj_class, case_class, gloss, note in LEXEMES:
        senses = json.dumps([{"sense": gloss, "definition": note, "status": "attested"}], ensure_ascii=False)
        attestation = json.dumps([{"source": LABEL, "ref": "five-lens close", "quote": note}], ensure_ascii=False)
        db.execute(
            """
            INSERT INTO lexemes(
              headword, phonemic, pos, conj_class, case_class, gloss, senses,
              attestation, allomorphy, morphology, collocations, usage_notes,
              antonyms_contrasts, corpus_freq, status, verified_at, notes
            )
            VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
            ON CONFLICT(headword) DO UPDATE SET
              verified_at=excluded.verified_at,
              status=CASE
                WHEN lexemes.status IN ('phantom-retired', 'watchlist') THEN lexemes.status
                ELSE 'verified'
              END,
              notes=CASE
                WHEN lexemes.notes IS NULL OR lexemes.notes='' THEN excluded.notes
                WHEN instr(lexemes.notes, ?) > 0 THEN lexemes.notes
                ELSE lexemes.notes || '; ' || excluded.notes
              END
            """,
            (
                headword, None, pos, conj_class, case_class, gloss, senses,
                attestation, None, None, None, note, None, 0, "verified", NOW,
                note, "batch-129",
            ),
        )
        db.execute(
            "INSERT INTO lexeme_log(headword, action, detail, created_at) VALUES(?,?,?,?)",
            (headword, "batch-129-verify", note, NOW),
        )

    db.execute("UPDATE batches SET notes=? WHERE id=?", (NOTES, batch_id))
    db.execute("UPDATE sentences SET status='accepted', updated_at=? WHERE batch_id=?", (NOW, batch_id))

    db.commit()
    reviews = db.execute(
        "SELECT COUNT(*) FROM reviews WHERE sentence_id IN (SELECT id FROM sentences WHERE batch_id=?)",
        (batch_id,),
    ).fetchone()[0]
    verified = db.execute("SELECT COUNT(*) FROM sentences WHERE status IN ('accepted','revised')").fetchone()[0]
    print(f"closed {LABEL}: 50 accepted rows, {reviews} review passes, verified corpus={verified}")
    return 0


if __name__ == "__main__":
    sys.exit(main())
