#!/usr/bin/env python3
"""Close batch-128 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-128"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")

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

NOTES = (
    "CONSTRUCTION: IMPERATIVES AND DIRECTIVES (Patz §3.8.3.2 suppletive "
    "imperatives; §3.8.4.1-2 verbal inflection paradigm and imperative "
    "allomorphs; §3.8.5.1-2 causative compounds; §3.8.5.5 reduplication; "
    "§3.10.1 temporal clitics; §4.7 imperative clauses; §4.10 interjections; "
    "narrative examples 615, 620, and text-line imperatives). 50 rows, avg "
    "2.32 Kuku words/row. DESIGN: this batch is intentionally command-heavy "
    "and short. Its job is to stabilize directive morphology before the corpus "
    "returns to longer prose: zero imperative on disyllabic l-conjugation roots "
    "(dida, kanga, nuka, janji, balka, kida, wandi), -y on disyllabic "
    "y-conjugation roots (dunga-y, kada-y, bunda-y, yirrka-y), -ka on roots or "
    "stems over two syllables (yijarri-ka, julurri-ji-ka, binal-ma-ka, "
    "babaji-ka, walu-wukurri-ka), and the suppletive imperatives nyaka, mana, "
    "daya. CONTRASTIVE MORPHOLOGY: rows 9-10 keep the important exception that "
    "causative compounds with -bunga-l and -mani-l do not simply take long-stem "
    "-ka; -bunga-l compounds have zero imperative on the second component and "
    "action-causative -mani-l uses suppletive mana, e.g. dara-y-mana. DIRECTIVE "
    "FORCE: rows 16-19 contrast plain imperative with -da immediate action and "
    "-(ng)Vrr/-rr 'straight away/first of all'; rows 14-15 record balu as "
    "desist/let, using the two attested intransitive imperative examples and "
    "not extrapolating the tentative transitive example. SUBJECTS: rows 11-13, "
    "23-24, 35, and 41 preserve explicit second-person and first-person "
    "inclusive subjects, with initial subject position where Patz says it is "
    "preferred. CONTINUATIVE COMMANDS: rows 12, 34, and 45-50 record "
    "reduplicated imperative force, including the suppletive reduplicated forms "
    "nyaka-l-nyaka, mana-l-mana, daya-l-daya and y-conjugation link -n-. "
    "PROCESS NOTES: full lint passed before close with 6497 rows scanned and 0 "
    "issues. The batch deliberately avoided treating nyaka as generic English "
    "'listen!' because prior errata warns that nyaji-l covers see/hear/perceive "
    "and the imperative evidence is safer as see/look unless a hearing context "
    "is explicit. It also avoided new ngara='listen' rows; ngara-l is verified "
    "as set up/build camp, not hear. Exact narrative interjections Ah/Yah were "
    "not banked as surface tokens because the current orthographic lint does "
    "not yet whitelist h-final attention sounds; the rows keep the imperative "
    "content (kada-y kada-y, jinbal nyaka nyaka) and record the source. Exact "
    "walnga 'wake up!' was likewise left for a later whitelist/lexeme pass; the "
    "safe intransitive l-conj wandi-l row covers get up/wake by the regular "
    "zero imperative. Surface-form review kept warri as the elder surface while "
    "the analysis records warri-y, and kept nyaji-ji as the surface while the "
    "analysis records nyaji-ji-y, matching the word-final -iy drop rule. Lint "
    "GREEN 6497 scanned; verified corpus after close should be 6497."
)

LESSONS = [
    (
        "morphology",
        "Disyllabic l-conjugation imperatives have zero suffix: dida, kanga, nuka, janji, balka, kida, and wandi are imperative surfaces without an added ending.",
        "cheatsheet Rule 86: l-conj zero imperative list and warning against adding -ka to short l roots.",
    ),
    (
        "morphology",
        "Disyllabic y-conjugation imperatives take -y: dunga-y, kada-y, bunda-y, and yirrka-y. Because this matches nonpast marking, imperative force may depend on intonation and context.",
        "cheatsheet Rule 86: y-conj imperative/nonpast homophony note.",
    ),
    (
        "morphology",
        "Roots and stems over two syllables take imperative -ka: yijarri-ka, julurri-ji-ka, binal-ma-ka, babaji-ka, and walu-wukurri-ka.",
        "cheatsheet Rule 86: long-root/derived-stem -ka imperative.",
    ),
    (
        "morphology",
        "The three suppletive imperatives must be memorized: nyaji-l -> nyaka, mani-l -> mana, daji-l -> daya. Do not use *nyaji, *mani, or *daji as command forms.",
        "operator guide imperative gate: suppletive command forms.",
    ),
    (
        "morphology",
        "Causative compounds are the main trap: wumbul-bunga is zero imperative despite the whole compound being long, and action-causative -mani-l uses suppletive mana as in dara-y-mana.",
        "cheatsheet Rule 86: causative-compound exception to the -ka generalization.",
    ),
    (
        "syntax",
        "Explicit imperative subjects are allowed and, when present, strongly prefer initial position: Yurra..., Yubal..., Yundu..., Ngana....",
        "cheatsheet Rule 86: imperative subject-position note.",
    ),
    (
        "syntax",
        "First-person inclusive non-singular subjects plus imperative give hortative force, as in Ngana kada-ri-ka-da 'Come on, let us go now'.",
        "cheatsheet Rule 86: inclusive hortative row and caution that this is not ordinary future marking.",
    ),
    (
        "semantics",
        "Balu in imperative clauses is not plain kari negation: in the attested directive examples it means let/desist/leave alone and is safely attested with intransitive imperatives only.",
        "errata Batch-128: do not overextend the tentative transitive balu example.",
    ),
    (
        "morphology",
        "The temporal clitic -da on an imperative urges immediate action now; -(ng)Vrr/-rr is stronger 'straight away/first of all' force and has no politeness contrast in Patz's analysis.",
        "cheatsheet Rule 86: urgency clitic contrast.",
    ),
    (
        "morphology",
        "-(ng)Vrr has allomorphs: -ngarr after nuka/mayi in the cited examples, fused -arr in kadayarr, and -rr after suppletive or -ka imperative forms such as daya-rr and babaji-ka-rr.",
        "cheatsheet Rule 86: urgency-clitic allomorphy.",
    ),
    (
        "morphology",
        "Reduplicated imperatives prompt continuing activity: bundanda-ka, waju-l-waju, dunga-n-dunga-y, yirrka-n-yirrka-y, and the suppletive forms nyaka-l-nyaka / mana-l-mana / daya-l-daya.",
        "cheatsheet Rule 86: continuative imperative patterns.",
    ),
    (
        "syntax",
        "Imperatives can coordinate with precautionary subordinate clauses: waju ... kada-nyji, kari waju ... buyun-dama-nyji, kari kulba ... dara-y-mani-nyji.",
        "operator guide: keep precautionary -nyji distinct from imperative morphology.",
    ),
    (
        "process",
        "Surface-form review remains mandatory: warri-y and nyaji-ji-y are analysis forms, but the banked elder surfaces are warri and nyaji-ji because word-final -iy drops.",
        "errata Batch-128: surface/analysis split for final -iy.",
    ),
    (
        "process",
        "Exact source examples with Ah/Yah and walnga were not banked until the lexeme/lint status is explicitly updated; the batch used source-safe imperative content and recorded the omission.",
        "operator guide: do not add orthographic lint whitelists casually during a production batch.",
    ),
]

LEXEMES = [
    ("imp-zero-l", "suffix", None, "NA", "zero imperative on disyllabic l-conjugation roots", "batch-128: dida, kanga, nuka, janji, balka, kida, wandi"),
    ("imp-y", "suffix", None, "NA", "imperative -y on disyllabic y-conjugation roots", "batch-128: dunga-y, kada-y, bunda-y, yirrka-y"),
    ("imp-ka", "suffix", None, "NA", "imperative -ka on roots/stems over two syllables", "batch-128: yijarri-ka, julurri-ji-ka, binal-ma-ka, babaji-ka"),
    ("nyaka", "verb-imp", "l", "NA", "suppletive imperative of nyaji-l, see/look/hear/perceive", "batch-128: use as see/look unless a hearing context is explicit"),
    ("mana", "verb-imp", "l", "NA", "suppletive imperative of mani-l, get/take", "batch-128: also used by -mani-l action-causative imperatives"),
    ("daya", "verb-imp", "l", "NA", "suppletive imperative of daji-l, give", "batch-128: daya and daya-rr"),
    ("balu", "particle", None, "NA", "desist; let; leave alone", "batch-128: attested with intransitive imperative Balu dungay / Balu wajujika; do not flatten to kari"),
    ("ba", "particle", None, "NA", "come on; hurry up", "batch-128: interjection before imperative"),
    ("ma", "particle", None, "NA", "go on; go ahead", "batch-128: interjection before imperative"),
    ("yala", "particle", None, "NA", "OK; let's; like this in yala-ku", "batch-128: interjection/suggestion plus yala-ku balka"),
    ("-da", "clitic", None, "NA", "TEMP1 immediate/following clitic; with imperative means do it now", "batch-128: mayi nuka-da and kada-ri-ka-da"),
    ("-(ng)Vrr", "clitic", None, "NA", "TEMP3 first-of-all/straight-away clitic", "batch-128: no politeness contrast; allomorphs -ngarr, -arr, -rr"),
    ("-ngarr", "clitic", None, "NA", "allomorph of -(ng)Vrr after hosts such as nuka and mayi", "batch-128: Nuka-ngarr and Mayi-ngarr nuka"),
    ("-arr", "clitic", None, "NA", "allomorph of -(ng)Vrr in kadayarr", "batch-128: Kadayarr urgent come imperative"),
    ("-rr", "clitic", None, "NA", "allomorph of -(ng)Vrr after suppletive and -ka imperative forms", "batch-128: daya-rr, babaji-ka-rr, julurri-ji-ka-rr"),
    ("-ri", "suffix", None, "NA", "plural subject agreement suffix", "batch-128: imperative kada-ri-ka and hortative kada-ri-ka-da"),
    ("wumbul-bunga-l", "compound", "l", "NA", "make hot; heat up", "batch-128: causative compound zero imperative wumbul-bunga"),
    ("dara-y-mani-l", "compound", "l", "NA", "cause to fall; drop", "batch-128: action-causative imperative dara-y-mana"),
    ("bundanda-ka", "verb-imp", "y", "NA", "keep sitting/staying", "batch-128: reduplicated posture imperative"),
    ("nyaka-l-nyaka", "verb-imp", "l", "NA", "keep looking", "batch-128: suppletive reduplicated imperative"),
    ("mana-l-mana", "verb-imp", "l", "NA", "keep taking", "batch-128: suppletive reduplicated imperative"),
    ("daya-l-daya", "verb-imp", "l", "NA", "keep giving", "batch-128: suppletive reduplicated imperative"),
    ("dunga-n-dunga-y", "verb-imp", "y", "NA", "keep going", "batch-128: y-conj reduplicated imperative with -n- link"),
    ("yirrka-n-yirrka-y", "verb-imp", "y", "NA", "keep shouting", "batch-128: y-conj reduplicated imperative with -n- link"),
    ("babaji-l", "verb-tr", "l", "NA", "ask; ask about", "batch-128: babaji-ka and babaji-ka-rr imperatives"),
    ("walu-wukurri-l", "compound", "l", "NA", "turn around", "batch-128: walu-wukurri-ka imperative"),
    ("kurrka-waba-ji-y", "compound", "y", "NA", "look behind", "batch-128: kurrka-waba-ji-ka imperative"),
    ("didal", "verb-tr", "l", "NA", "put on clothing", "batch-128: exact Patz imperative Jarruja dida"),
    ("jujubala", "noun", None, "neutral", "ironwood", "batch-128: exact Patz precautionary imperative Jujubala waju..."),
    ("jarramali", "noun", None, "neutral", "thunderstorm", "batch-128: exact Patz precautionary example with kada-nyji"),
]


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-128",
            ),
        )
        db.execute(
            "INSERT INTO lexeme_log(headword, action, detail, created_at) VALUES(?,?,?,?)",
            (headword, "batch-128-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())
