#!/usr/bin/env python3
"""Bank batch-128: imperatives and directive force.

Rows are hand-authored from Patz's imperative sections and checked against the
living grammar/dictionary notes. The script only banks rows as under_review; it
does not generate sentence content.
"""

from __future__ import annotations

import datetime as dt
import json
import re
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")

DENY = {
    "nyawa", "jinbi", "jibin", "walba", "julmbu", "wanan", "ngampula",
    "yalama", "ngana-jinji", "jubuny", "wural", "bigan", "jiwuny",
    "warngkul", "wanaba", "jarrkunji", "munjal", "murku", "buni",
    "kunya", "milkan", "kulur-jambul", "janyji", "buburr", "jarrmba",
    "jinji", "daga", "wanka", "yumbul", "kuku-yumbul", "jubi",
    "yilbay", "jarrakay", "janka-yukil", "jukarr", "kunkul",
    "bubu-jandu", "bubu-warral", "jani-warra", "dungkarr", "jimba",
    "bujal", "wanyjarr", "ngudan", "jimirr", "jangan", "ganggal",
    "bukul", "jalany", "nyunun", "yinkar", "jiba-ngulkurr", "jiba-wawu",
    "nyawa-kari", "jinbi-kari", "buri", "bibiny", "jubaji", "nyalbu",
    "kuka", "kambar", "dalku", "milbin", "kindu",
}


def js(values: list[str]) -> str:
    return json.dumps(values, ensure_ascii=False)


ROWS = [
    (1, "Put your pants on.",
     "Jarruja dida!",
     "jarruja dida | trousers put.on.IMP",
     "attested_l_conj_zero_imperative", 4, js(["jarruja", "didal"]),
     "Patz ex.150: disyllabic l-conj imperative has zero suffix; Jarruja dida!", "0.90"),
    (2, "Go back.",
     "Dunga-y-baja!",
     "dunga-y-baja | go-IMP-again",
     "attested_y_conj_imperative_again", 4, js(["dungay", "baja"]),
     "Patz ex.151: disyllabic y-conj imperative takes -y; dunga-y-baja.", "0.90"),
    (3, "Chase the dog.",
     "Kaya kanga!",
     "kaya kanga | dog chase.IMP",
     "l_conj_zero_transitive_imperative", 4, js(["kaya", "kangal"]),
     "Patz ex.149 gives kanga as zero imperative of kanga-l 'chase'; object-only imperative pattern.", "0.86"),
    (4, "Get water.",
     "Bana mana!",
     "bana mana | water get.IMP",
     "suppletive_mani_imperative", 4, js(["bana", "manil"]),
     "Patz §3.8.3.2 and §3.8.4.2: mani-l has suppletive imperative mana; ex.620 has bana mana.", "0.90"),
    (5, "Give meat to me.",
     "Minya ngaykunku daya!",
     "minya ngaykunku daya | meat 1sg.DAT give.IMP",
     "suppletive_daji_imperative_dative", 5, js(["minya", "ngaykunku", "dajil"]),
     "Patz §3.8.3.2: daji-l imperative daya; dative recipient pattern as in giving examples.", "0.86"),
    (6, "Learn language.",
     "Kuku-ku binal-ma-ka!",
     "kuku-ku binal-ma-ka | language-DAT know-INCHO-IMP",
     "attested_derived_ka_imperative", 5, js(["kuku", "binal", "binalmal"]),
     "Patz ex.155: roots/stems over two syllables take imperative -ka.", "0.90"),
    (7, "Put the baby on the blanket.",
     "Ngawa kararr-ba yijarri-ka!",
     "ngawa kararr-ba yijarri-ka | baby blanket-LOC put-IMP",
     "attested_trisyllabic_ka_imperative", 5, js(["ngawa", "kararr", "yijarril"]),
     "Patz ex.153: yijarri-l 'put/place' takes -ka in imperative; locative kararr-ba.", "0.90"),
    (8, "Wash your hands.",
     "Mara julurri-ji-ka!",
     "mara julurri-ji-ka | hand wash-REFL-IMP",
     "attested_reflexive_ka_imperative", 5, js(["mara", "julurril"]),
     "Patz ex.154: reflexive stem julurri-ji-ka 'wash yourself/hands'.", "0.90"),
    (9, "Heat it up.",
     "Wumbul-bunga!",
     "wumbul-bunga | hot-CAUS.IMP",
     "attested_causative_bunga_zero_imperative", 5, js(["wumbul", "bungal"]),
     "Patz ex.156: causative compounds with -bunga-l take zero imperative on the compound component.", "0.90"),
    (10, "Drop that.",
     "Yinya dara-y-mana!",
     "yinya dara-y-mana | that fall-y-CAUS.IMP",
     "attested_causative_mana_imperative", 5, js(["yinya", "daraymanil"]),
     "Patz ex.567: action causative with -mani-l uses suppletive imperative mana.", "0.90"),
    (11, "You all wash yourselves.",
     "Yurra julurri-ji-ka!",
     "yurra julurri-ji-ka | 2pl.NOM wash-REFL-IMP",
     "attested_explicit_subject_imperative", 5, js(["yurra", "julurril"]),
     "Patz ex.565: explicit second-person subject is possible and preferred initially when present.", "0.90"),
    (12, "You two keep sitting; I will come back.",
     "Yubal bundanda-ka, ngayu kada-y-baja!",
     "yubal bundanda-ka, ngayu kada-y-baja | 2du.NOM sit.REDUP-IMP, 1sg come-NONPAST-again",
     "attested_reduplicated_continuative_imperative", 5, js(["yubal", "bunday", "ngayu", "kaday", "baja"]),
     "Patz ex.568: reduplicated imperative prompts continued activity.", "0.90"),
    (13, "Come on, let us go now.",
     "Ngana kada-ri-ka-da!",
     "ngana kada-ri-ka-da | 1pl.inc.NOM come-PLS-IMP-TEMP1",
     "attested_inclusive_hortative_imperative", 5, js(["ngana", "kaday", "-ri", "-da"]),
     "Patz ex.569: first-person inclusive non-singular subject plus imperative gives hortative force.", "0.90"),
    (14, "Let him go.",
     "Balu dungay!",
     "balu dunga-y | desist go-IMP",
     "attested_balu_intransitive_imperative", 4, js(["balu", "dungay"]),
     "Patz ex.570: balu with intransitive imperative means leave alone / let go.", "0.90"),
    (15, "Let it burn.",
     "Balu wajujika!",
     "balu waju-ji-ka | desist burn-REFL-IMP",
     "attested_balu_burn_imperative", 4, js(["balu", "wajul"]),
     "Patz ex.571: Balu wajujika 'Let it burn!'.", "0.90"),
    (16, "Eat the food now.",
     "Mayi nuka-da!",
     "mayi nuka-da | food eat.IMP-TEMP1",
     "temporal_da_immediate_imperative", 4, js(["mayi", "nukal", "-da"]),
     "Patz §4.7: mayi nuka-da urges immediate eating and discontinuing other activity.", "0.90"),
    (17, "Eat straight away.",
     "Nuka-ngarr!",
     "nuka-ngarr | eat.IMP-TEMP3",
     "ngvrr_urgent_imperative", 4, js(["nukal", "-ngarr"]),
     "Patz ex.573: Nuka-ngarr expresses straight-away / first-of-all imperative urgency.", "0.90"),
    (18, "Eat the food first.",
     "Mayi-ngarr nuka!",
     "mayi-ngarr nuka | food-TEMP3 eat.IMP",
     "ngvrr_nominal_host_imperative", 4, js(["mayi", "nukal", "-ngarr"]),
     "Patz ex.573: -(ng)Vrr may attach to the object noun, Mayi-ngarr nuka.", "0.90"),
    (19, "Come straight away.",
     "Kadayarr!",
     "kada-y-arr | come-IMP-TEMP3",
     "urgent_y_conj_imperative", 4, js(["kaday", "-arr"]),
     "Patz §4.7 Louisa Smith example: kadayarr as stronger immediate command.", "0.88"),
    (20, "Hurry up, come.",
     "Ba, kada-y!",
     "ba, kada-y | hurry, come-IMP",
     "interjection_plus_y_imperative", 4, js(["ba", "kaday"]),
     "Patz §4.10: ba 'come on, hurry up'; y-conj imperative kada-y.", "0.86"),
    (21, "Go on, eat.",
     "Ma, nuka!",
     "ma, nuka | go.on eat.IMP",
     "interjection_plus_l_zero_imperative", 4, js(["ma", "nukal"]),
     "Patz §4.10: ma 'go on, go ahead'; l-conj zero imperative nuka.", "0.86"),
    (22, "Okay, let us go.",
     "Yala, ngana dunga-y!",
     "yala, ngana dunga-y | OK/let's 1pl.inc.NOM go-IMP",
     "interjection_hortative_imperative", 5, js(["yala", "ngana", "dungay"]),
     "Patz §4.10 yala 'OK, let's' plus §4.7 inclusive subject can form suggestions.", "0.84"),
    (23, "You two have a swim.",
     "Yubal janji!",
     "yubal janji | 2du.NOM swim.IMP",
     "dual_subject_l_zero_intransitive_imperative", 4, js(["yubal", "janjil"]),
     "Patz ex.615 has yubal plus janji 'have a swim'; janji-l is an intransitive l-conj zero imperative.", "0.88"),
    (24, "You children go and get water.",
     "Yurra karrkay dunga-y bana mana!",
     "yurra karrkay dunga-y bana mana | 2pl.NOM child go-IMP water get.IMP",
     "attested_coordinate_imperatives", 5, js(["yurra", "karrkay", "dungay", "bana", "manil"]),
     "Patz ex.620: plural addressee with coordinated imperatives dunga-y and mana.", "0.90"),
    (25, "Ask the dillybag expert about dillybags.",
     "Mala-balji-nda babaji-ka balji-ka.",
     "mala-balji-nda babaji-ka balji-ka | expert-dillybag-LOC ask.about-IMP dillybag-DAT",
     "attested_ask_about_ka_imperative", 5, js(["mala-balji", "babajil", "balji"]),
     "Patz ex.77: Mala-balji-nda babaji-ka balji-ka.", "0.90"),
    (26, "Put the eggs carefully on the plate.",
     "Diburr mumbar-abu-ku bulin-ba yijarri-ka!",
     "diburr mumbar-abu-ku bulin-ba yijarri-ka | egg careful-ERG/INST-ADV plate-LOC put-IMP",
     "attested_manner_ka_imperative", 5, js(["diburr", "mumbar", "bulin", "yijarril"]),
     "Patz ex.200: Diburr mumbar-abu-ku bulin-ba yijarri-ka.", "0.90"),
    (27, "Speak like this.",
     "Yala-ku balka!",
     "yala-ku balka | like.this-ADV speak.IMP",
     "attested_l_zero_speech_imperative", 4, js(["yala", "balkal"]),
     "Patz ex.201: Yala-ku balka 'Speak like this / make it like this'.", "0.90"),
    (28, "Sweep the whole house thoroughly.",
     "Bayan kida-rrku!",
     "bayan kida-rrku | house sweep.IMP-EMPH4",
     "attested_emphatic_l_zero_imperative", 4, js(["bayan", "kidal", "-rrku"]),
     "Patz ex.214/§3.10.2: Bayan kida-rrku 'Sweep the whole house thoroughly'.", "0.90"),
    (29, "You two get grass for the soup.",
     "Yubal wuju mana milkul-ku.",
     "yubal wuju mana milkul-ku | 2du.NOM grass get.IMP soup-DAT",
     "attested_dual_suppletive_mana_imperative", 5, js(["yubal", "wuju", "manil", "milkul"]),
     "Patz ex.403a: Yubal wuju mana milkul-ku.", "0.90"),
    (30, "If you happen to see mangoes, take them.",
     "Mayngku nyaji-ji, mana!",
     "mayngku nyaji-ji-y, mana | mango see-ITR-NONPAST take.IMP",
     "attested_suppletive_mana_followup_directive", 5, js(["mayngku", "nyajil", "manil"]),
     "Patz ex.362: mayngku nyaji-ji-y, mana; surface drops final y after i.", "0.88"),
    (31, "Burn ironwood to prevent the thunderstorm from coming.",
     "Jujubala waju jarramali kada-nyji!",
     "jujubala waju jarramali kada-nyji | ironwood burn.IMP thunderstorm come-PRECAUT",
     "attested_imperative_precautionary", 5, js(["jujubala", "wajul", "jarramali", "kaday"]),
     "Patz ex.164: imperative main clause plus precautionary subordinate clause.", "0.90"),
    (32, "Do not cook the food lest you spoil it.",
     "Mayi kari waju, buyun-dama-nyji!",
     "mayi kari waju, buyun-dama-nyji | food NEG cook.IMP bad-CAUS-PRECAUT",
     "attested_negative_imperative_precautionary", 5, js(["mayi", "kari", "wajul", "buyun", "damal"]),
     "Patz ex.436: Kari plus l-zero imperative waju in a precautionary construction.", "0.90"),
    (33, "Do not pick up the egg lest you drop it.",
     "Kari diburr kulba, dara-y-mani-nyji!",
     "kari diburr kulba, dara-y-mani-nyji | NEG egg lift.IMP fall-y-CAUS-PRECAUT",
     "attested_negative_kulba_precautionary", 5, js(["kari", "diburr", "kulba", "daraymanil"]),
     "Patz ex.441: Kari diburr kulba, dara-y-mani-nyji.", "0.90"),
    (34, "Keep a fire burning lest it get cold.",
     "Baya waju-l-waju, kiway kada-nyji!",
     "baya waju-l-waju, kiway kada-nyji | fire burn-REDUP-IMP cold come-PRECAUT",
     "attested_reduplicated_imperative_precautionary", 5, js(["baya", "wajul", "kiway", "kaday"]),
     "Patz ex.443: reduplicated imperative waju-l-waju prompts continued burning.", "0.90"),
    (35, "You lot, come here.",
     "Yurra kada-ri-ka!",
     "yurra kada-ri-ka | 2pl.NOM come-PLS-IMP",
     "attested_plural_subject_imperative", 5, js(["yurra", "kaday", "-ri"]),
     "Patz ex.189: plural-subject imperative kada-ri-ka.", "0.90"),
    (36, "Turn around.",
     "Yundu walu-wukurri-ka!",
     "yundu walu-wukurri-ka | 2sg.NOM face-follow-IMP",
     "attested_compound_ka_imperative", 5, js(["yundu", "walu", "wukurril"]),
     "Patz narrative line 99: Yundu walu-wukurri-ka 'Turn around!'.", "0.90"),
    (37, "Run, and look behind you.",
     "Yundu warri, yundu kurrka-waba-ji-ka!",
     "yundu warri-y, yundu kurrka-waba-ji-ka | 2sg.NOM run-IMP, 2sg.NOM behind-look.up-ITR-IMP",
     "attested_coordinate_y_and_ka_imperatives", 5, js(["yundu", "warri", "kurrka", "wabajiy"]),
     "Patz narrative line 101; surface warri avoids final -iy spelling leakage while analysis keeps -y.", "0.88"),
    (38, "Child, get up.",
     "Karrkay, wandi!",
     "karrkay, wandi | child get.up.IMP",
     "l_conj_zero_intransitive_imperative", 4, js(["karrkay", "wandil"]),
     "Patz §3.8.3.1 lists wandi-l as intransitive l-conj; disyllabic l-conj imperative has zero suffix.", "0.86"),
    (39, "Quick, look, look.",
     "Jinbal, nyaka nyaka!",
     "jinbal, nyaka nyaka | quick see.IMP see.IMP",
     "attested_suppletive_repeated_imperative", 4, js(["jinbal", "nyajil"]),
     "Patz narrative line 15 has jinbal plus repeated suppletive nyaka; Ah interjection omitted for orthographic lint.", "0.88"),
    (40, "Come, come.",
     "Kada-y kada-y!",
     "kada-y kada-y | come-IMP come-IMP",
     "attested_repeated_y_imperative", 4, js(["kaday"]),
     "Patz narrative line 26: repeated kada-y imperative after attention-getting interjection.", "0.88"),
    (41, "You two stay here.",
     "Yubal yalaymba bunda-y!",
     "yubal yalaymba bunda-y | 2du.NOM here sit/stay-IMP",
     "attested_dual_subject_y_imperative", 4, js(["yubal", "yalaymba", "bunday"]),
     "Patz narrative line 33: Yubal yalaymba bunda-y 'you two stay here'.", "0.90"),
    (42, "Ask the old man first.",
     "Yundu wulman-anda babaji-ka-rr!",
     "yundu wulman-anda babaji-ka-rr | 2sg.NOM old.man-LOC ask.about-IMP-TEMP3",
     "attested_rr_first_imperative", 5, js(["yundu", "wulman", "babajil", "-rr"]),
     "Patz narrative line 75 and §3.10.1: -rr with imperative means first/straight away.", "0.90"),
    (43, "Give it first.",
     "Daya-rr!",
     "daya-rr | give.IMP-TEMP3",
     "suppletive_daya_rr_imperative", 4, js(["dajil", "-rr"]),
     "Patz §3.10.1: with suppletive imperative forms, -(ng)Vrr may appear as -rr, e.g. daya-rr.", "0.90"),
    (44, "Wash your hands first.",
     "Mara julurri-ji-ka-rr!",
     "mara julurri-ji-ka-rr | hand wash-REFL-IMP-TEMP3",
     "ka_imperative_rr_first", 5, js(["mara", "julurril", "-rr"]),
     "Patz §3.10.1: trisyllabic/derived imperative forms can take -rr, e.g. julurri-ka-rr.", "0.88"),
    (45, "Keep looking.",
     "Nyaka-l-nyaka!",
     "nyaka-l-nyaka | see.IMP-l-REDUP",
     "suppletive_reduplicated_imperative", 5, js(["nyajil"]),
     "Patz §3.8.5.5: suppletive imperative roots are required in reduplicated imperatives; nyaka-l-nyaka.", "0.90"),
    (46, "Keep taking.",
     "Mana-l-mana!",
     "mana-l-mana | get.IMP-l-REDUP",
     "suppletive_reduplicated_mana_imperative", 5, js(["manil"]),
     "Patz §3.8.5.5: suppletive imperative roots are used in reduplicated imperatives; mana-l-mana.", "0.90"),
    (47, "Keep giving.",
     "Daya-l-daya!",
     "daya-l-daya | give.IMP-l-REDUP",
     "suppletive_reduplicated_daya_imperative", 5, js(["dajil"]),
     "Patz §3.8.5.5: suppletive imperative roots are used in reduplicated imperatives; daya-l-daya.", "0.90"),
    (48, "Keep going.",
     "Dunga-n-dunga-y!",
     "dunga-n-dunga-y | go-n-REDUP-IMP",
     "y_conj_reduplicated_imperative", 5, js(["dungay"]),
     "Patz §3.8.5.5: y-conj reduplication uses -n- link; dunga-n-dunga-y.", "0.90"),
    (49, "Keep shouting.",
     "Yirrka-n-yirrka-y!",
     "yirrka-n-yirrka-y | shout-n-REDUP-IMP",
     "y_conj_reduplicated_shout_imperative", 5, js(["yirrkay"]),
     "Patz §3.8.5.5: yirrka-n-yirrka-y is the y-conj reduplicated form.", "0.90"),
    (50, "Keep sitting.",
     "Bundanda-ka!",
     "bundanda-ka | sit.REDUP-IMP",
     "reduplicated_posture_ka_imperative", 5, js(["bunday"]),
     "Patz ex.568: bundanda-ka is reduplicated imperative 'keep sitting/staying'.", "0.90"),
]


def scan() -> list[str]:
    issues: list[str] = []
    if len(ROWS) != 50:
        issues.append(f"expected 50 rows, got {len(ROWS)}")
    seqs = [r[0] for r in ROWS]
    if seqs != list(range(1, 51)):
        issues.append(f"seqs must be 1..50, got {seqs[:5]}...{seqs[-5:]}")
    seen_ku: set[str] = set()
    for row in ROWS:
        seq, english, kuku, analysis, frame, tier, words_used, evidence, confidence = row
        label = f"{LABEL}.{seq:02d}"
        if not english[:1].isupper() or english.rstrip("'\"").rstrip()[-1:] not in ".!?":
            issues.append(f"{label}: English must be capitalized and final-punctuated")
        if " | " not in analysis:
            issues.append(f"{label}: analysis missing gloss separator")
        if not kuku[:1].isupper():
            issues.append(f"{label}: Kuku surface must start uppercase")
        try:
            json.loads(words_used)
        except Exception as exc:
            issues.append(f"{label}: words_used is not JSON: {exc}")
        low = f" {kuku.lower()} {analysis.lower()} "
        for word in DENY:
            if re.search(rf"(?<![a-z]){re.escape(word)}(?![a-z])", low):
                issues.append(f"{label}: denied phantom/wrong-sense token {word!r}")
        if kuku.lower() in seen_ku:
            issues.append(f"{label}: duplicate Kuku surface within batch")
        seen_ku.add(kuku.lower())
    return issues


def main() -> int:
    issues = scan()
    if issues:
        print("pre-bank scan failed:")
        for issue in issues:
            print(f"  - {issue}")
        return 1

    db = sqlite3.connect(DB)
    db.execute("PRAGMA foreign_keys = ON")
    existing = db.execute("SELECT id FROM batches WHERE label=?", (LABEL,)).fetchone()
    if existing:
        batch_id = existing[0]
        accepted = db.execute(
            "SELECT COUNT(*) FROM sentences WHERE batch_id=? AND status IN ('accepted','revised')",
            (batch_id,),
        ).fetchone()[0]
        if accepted:
            raise SystemExit(f"{LABEL} already has accepted/revised rows; refusing to overwrite")
        db.execute("DELETE FROM reviews WHERE sentence_id IN (SELECT id FROM sentences WHERE batch_id=?)", (batch_id,))
        db.execute("DELETE FROM sentences WHERE batch_id=?", (batch_id,))
        db.execute("UPDATE batches SET notes=?, created_at=? WHERE id=?", ("rebanked under_review", NOW, batch_id))
    else:
        cur = db.execute(
            "INSERT INTO batches(label, cheatsheet_version, guide_version, notes, created_at) VALUES(?,?,?,?,?)",
            (LABEL, "Rule 86 draft: imperatives and directives", "operator-guide-2026-07-07", "under_review", NOW),
        )
        batch_id = cur.lastrowid

    db.executemany(
        """
        INSERT INTO sentences(
            batch_id, seq, english, kuku, analysis, frame, tier, words_used,
            loans_used, evidence, status, confidence, rights_status, author,
            created_at, updated_at
        ) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
        """,
        [
            (
                batch_id, seq, english, kuku, analysis, frame, tier, words_used,
                "[]", evidence, "under_review", confidence,
                "project_approved_synthetic_pending_elder_verification",
                "claude-main", NOW, NOW,
            )
            for seq, english, kuku, analysis, frame, tier, words_used, evidence, confidence in ROWS
        ],
    )
    db.commit()
    count = db.execute("SELECT COUNT(*) FROM sentences WHERE batch_id=?", (batch_id,)).fetchone()[0]
    avg_words = db.execute(
        "SELECT AVG(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1.0) FROM sentences WHERE batch_id=?",
        (batch_id,),
    ).fetchone()[0]
    print(f"banked {LABEL}: {count} rows under_review; avg Kuku words/row={avg_words:.2f}")
    return 0


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