#!/usr/bin/env python3
"""Bank batch-139: perception, attention, searching, and finding verbs."""

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-139"
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", "balbay-nja",
    "bunjurril", "buri", "baral", "ngara", "rrunyuji",
}


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


ROWS = [
    (1, "I saw the dog.",
     "Ngayu kaya nyajin.",
     "ngayu kaya nyaji-ny | I dog see/perceive-PAST",
     "perception_general_see", 4, js(["ngayu", "kaya", "nyajil"]), js([]),
     "Dictionary nyajil = perceive/see/hear; simple transitive perception row.", "0.86"),
    (2, "The man saw the child.",
     "Dingkar-angka karrkay nyajin.",
     "dingkar-angka karrkay nyaji-ny | man-ERG child see/perceive-PAST",
     "perception_general_see", 4, js(["dingkar", "karrkay", "nyajil"]), js([]),
     "Nyajil transitive with common-noun A marked ERG.", "0.86"),
    (3, "They looked up that way.",
     "Jana yarra wangkar nyajin.",
     "jana yarra wangkar nyaji-ny | they direction up see/perceive-PAST",
     "perception_directional", 5, js(["jana", "yarra", "wangkar", "nyajil"]), js([]),
     "Patz text 12 line 14 has Bula yarra wangkar nyaji-ny; subject varied to avoid corpus duplicate.", "0.88"),
    (4, "We saw the slippery ground.",
     "Nganjin bubu julbarr nyajin.",
     "nganjin bubu julbarr nyaji-ny | we.excl ground slippery see/perceive-PAST",
     "perception_general_see", 5, js(["nganjin", "bubu", "julbarr", "nyajil"]), js([]),
     "Patz text 3 line 12: Nganjin ... bubu nyaji-ny julbarr.", "0.90"),
    (5, "The child saw the animal.",
     "Karrkay-ngku minya nyajin.",
     "karrkay-ngku minya nyaji-ny | child-ERG animal see/perceive-PAST",
     "perception_general_see", 4, js(["karrkay", "minya", "nyajil"]), js([]),
     "Nyajil with ordinary animal object; ERG A retained.", "0.84"),
    (6, "You saw me.",
     "Yundu nganya nyajin.",
     "yundu nganya nyaji-ny | you me see/perceive-PAST",
     "perception_pronoun_object", 5, js(["yundu", "nganya", "nyajil"]), js([]),
     "Patz ex.383 has yundu ... nganya nyaji-nyaku; past contrast row.", "0.86"),
    (7, "They saw firewood.",
     "Jana baya nyajin.",
     "jana baya nyaji-ny | they firewood see/perceive-PAST",
     "perception_general_see", 4, js(["jana", "baya", "nyajil"]), js([]),
     "Nyajil with familiar object; no new lexical burden.", "0.82"),
    (8, "He saw the stone again.",
     "Nyulu kulji nyajin-baja.",
     "nyulu kulji nyaji-ny-baja | he stone see/perceive-PAST-again",
     "perception_again", 5, js(["nyulu", "kulji", "nyajil", "-baja"]), js([]),
     "Patz text 3 line 26 has nyaji-ny-baja; object varied.", "0.86"),
    (9, "We heard a grunt.",
     "Nganjin ngingkirr nyajin.",
     "nganjin ngingkirr nyaji-ny | we.excl grunt hear/perceive-PAST",
     "perception_hear_sound", 5, js(["nganjin", "ngingkirr", "nyajil"]), js([]),
     "Dictionary ngingkirr example: Nganjin dungan, ngingkirr nyajin.", "0.90"),
    (10, "The child heard the word.",
     "Karrkay-ngku milkabu kuku nyajin.",
     "karrkay-ngku milka-bu kuku nyaji-ny | child-ERG ear-INST word hear-PAST",
     "perception_explicit_hear", 5, js(["karrkay", "milka", "kuku", "nyajil"]), js([]),
     "Dictionary: milkabu nyajil explicitly disambiguates hearing.", "0.88"),
    (11, "I heard the barking.",
     "Ngayu milkabu ngangkurr nyajin.",
     "ngayu milka-bu ngangkurr nyaji-ny | I ear-INST bark.sound hear-PAST",
     "perception_explicit_hear", 5, js(["ngayu", "milka", "ngangkurr", "nyajil"]), js([]),
     "Rule 81 verified ngangkurr sound/bark; milkabu nyajil = hear.", "0.86"),
    (12, "I listened.",
     "Ngayu milka-janan.",
     "ngayu milka-janay-ny | I ear-stand-PAST",
     "perception_listen", 4, js(["ngayu", "milka-janay"]), js([]),
     "Dictionary milka-janay = listen/obey.", "0.86"),
    (13, "The child listened.",
     "Karrkay milka-janan.",
     "karrkay milka-janay-ny | child listen-PAST",
     "perception_listen", 4, js(["karrkay", "milka-janay"]), js([]),
     "Milka-janay is intransitive; noun subject remains ABS.", "0.84"),
    (14, "I cannot hear.",
     "Ngayu milka-kari.",
     "ngayu milka-kari | I ear-PRIV",
     "perception_deaf_privative", 4, js(["ngayu", "milka", "-kari"]), js([]),
     "Dictionary milka-kari = cannot hear/deaf; do not use ngara.", "0.88"),
    (15, "He is deaf.",
     "Nyulu milka dudu.",
     "nyulu milka dudu | he ear blunt",
     "perception_deaf_attribute", 4, js(["nyulu", "milka dudu"]), js([]),
     "Dictionary milka dudu = deaf/stubborn; source-backed perception attribute.", "0.84"),

    (16, "I looked.",
     "Ngayu miyil-janan.",
     "ngayu miyil-janay-ny | I eye-stand-PAST",
     "perception_look", 4, js(["ngayu", "miyil-janay"]), js([]),
     "Dictionary miyil-janay = look/watch.", "0.86"),
    (17, "The woman looked.",
     "Jalbu miyil-janan.",
     "jalbu miyil-janay-ny | woman look/watch-PAST",
     "perception_look", 4, js(["jalbu", "miyil-janay"]), js([]),
     "Miyil-janay intransitive look/watch row.", "0.84"),
    (18, "The man stared.",
     "Dingkar miyil-dandi.",
     "dingkar miyil-dandi | man eye-hard",
     "perception_stare_attribute", 4, js(["dingkar", "miyil-dandi"]), js([]),
     "Dictionary miyil-dandi = one who stares/looks hard/concentrates.", "0.86"),
    (19, "The child stared.",
     "Karrkay miyil-dandi.",
     "karrkay miyil-dandi | child eye-hard",
     "perception_stare_attribute", 4, js(["karrkay", "miyil-dandi"]), js([]),
     "Second miyil-dandi row with child subject.", "0.84"),
    (20, "The man watched the child.",
     "Dingkar-angka karrkay manubu-daman.",
     "dingkar-angka karrkay manubu-dama-ny | man-ERG child watch/guard-PAST",
     "perception_watch_guard", 5, js(["dingkar", "karrkay", "manubu-damal"]), js([]),
     "Dictionary manubu-damal = watch/guard.", "0.86"),
    (21, "Mother watched the baby.",
     "Ngamu-ngku ngawa manubu-daman.",
     "ngamu-ngku ngawa manubu-dama-ny | mother-ERG baby watch/guard-PAST",
     "perception_watch_guard", 5, js(["ngamu", "ngawa", "manubu-damal"]), js([]),
     "Manubu-damal with familiar kin/care frame.", "0.84"),
    (22, "The dog watched the man.",
     "Kaya-ngka dingkar warringan.",
     "kaya-ngka dingkar warringa-ny | dog-ERG man observe-PAST",
     "perception_observe", 5, js(["kaya", "dingkar", "warringal"]), js([]),
     "Dictionary warringal = watch/see where someone is going.", "0.84"),
    (23, "I looked around.",
     "Ngayu walu-wukurrin.",
     "ngayu walu-wukurri-ny | I face-follow-PAST",
     "perception_look_around", 5, js(["ngayu", "walu-wukurril"]), js([]),
     "Dictionary walu-wukurril = look around.", "0.88"),
    (24, "The child looked back.",
     "Karrkay kurrka-nyajijin.",
     "karrkay kurrka-nyaji-ji-ny | child back-see-ITR-PAST",
     "perception_look_back", 5, js(["karrkay", "kurrka-nyaji-ji"]), js([]),
     "Patz text 3 line 19; dictionary grammar-added kurrka-nyaji-ji.", "0.88"),

    (25, "You do not know.",
     "Yundu binal kari.",
     "yundu binal kari | you know NEG",
     "cognition_know_negated", 5, js(["yundu", "binal", "kari"]), js([]),
     "Dictionary binal example has ngayu binal kari; pronoun varied to avoid corpus duplicate.", "0.86"),
    (26, "I want to know.",
     "Ngayu binal-binalku bundanka.",
     "ngayu binal-binalku bunda-nka | I know-REDUP want-PURP",
     "cognition_want_to_know", 5, js(["ngayu", "binal", "bunday"]), js([]),
     "Dictionary binal example: Ngayu binal-binalku bundanka.", "0.88"),
    (27, "They know that.",
     "Jana binal yinya.",
     "jana binal yinya | they know that",
     "cognition_know", 4, js(["jana", "binal", "yinya"]), js([]),
     "Patz text 12 line 58 has jana binal ... yinya.", "0.86"),
    (28, "I know that.",
     "Ngayu yinya binal.",
     "ngayu yinya binal | I that know",
     "cognition_know", 4, js(["ngayu", "yinya", "binal"]), js([]),
     "Binal know with demonstrative object; short lexical row.", "0.84"),
    (29, "I remembered.",
     "Ngayu milka-dalkan.",
     "ngayu milka-dalkay-ny | I ear-split/remember-PAST",
     "cognition_remember", 5, js(["ngayu", "milka-dalkay"]), js([]),
     "Dictionary milka-dalkay = remember; past example has milka-dalkan.", "0.88"),
    (30, "The dead person remembered his wife.",
     "Nyulu yarkin manyarr-anka milka-dalkan.",
     "nyulu yarkin manyarr-anka milka-dalkay-ny | he dead.person wife-DAT:pt remember-PAST",
     "cognition_remember_about", 6, js(["nyulu", "yarkin", "manyarr", "milka-dalkay"]), js([]),
     "Dictionary milka-dalkay usage: Nyulu yarkin manyarranka milka-dalkan.", "0.90"),
    (31, "I forgot about the child.",
     "Ngayu karrkay-nka milka-wulan.",
     "ngayu karrkay-nka milka-wulay-ny | I child-DAT:pt forget-PAST",
     "cognition_forget", 5, js(["ngayu", "karrkay", "milka-wulay"]), js([]),
     "Dictionary milka-wulay usage has kayanka; animate DAT:pt object varied to avoid corpus duplicate.", "0.84"),
    (32, "I remembered the dog.",
     "Ngayu milkabu manin kaya.",
     "ngayu milka-bu mani-ny kaya | I ear-INST get/remember-PAST dog",
     "cognition_remember_transitive", 5, js(["ngayu", "milkabu manil", "kaya"]), js([]),
     "Dictionary milkabu manil = remember/recollect; object kept explicit.", "0.84"),

    (33, "I looked for food.",
     "Ngayu mayi nubin.",
     "ngayu mayi nubi-ny | I food seek-PAST",
     "search_seek", 4, js(["ngayu", "mayi", "nubil"]), js([]),
     "Dictionary nubil = seek/look for.", "0.86"),
    (34, "The woman looked for yam.",
     "Jalbu-ngku wukay nubin.",
     "jalbu-ngku wukay nubi-ny | woman-ERG yam seek-PAST",
     "search_seek", 5, js(["jalbu", "wukay", "nubil"]), js([]),
     "Patz antipassive discussion contrasts transitive wukay nubi-l.", "0.88"),
    (35, "They searched everywhere.",
     "Jana nubil-nubin.",
     "jana nubi-l-nubi-ny | they search-l-REDUP-PAST",
     "search_reduplicated", 5, js(["jana", "nubil"]), js([]),
     "Patz text 36: nubi-l-nubi-ny-da and nubi-l-nubi-nya for searching everywhere.", "0.88"),
    (36, "The child went into the scrub and got lost.",
     "Karrkay madja-nga dungan, nubijinkuda.",
     "karrkay madja-nga dunga-ny, nubi-ji-ny-kuda | child scrub-LOC go-PAST lose/be.lost-ITR-PAST-TEMP2",
     "search_lost", 6, js(["karrkay", "madja", "dungay", "nubiji", "-kuda"]), js([]),
     "Dictionary nubiji usage: child went into scrub and got lost.", "0.90"),
    (37, "I lost the spear.",
     "Ngayu kalka nubi-manin.",
     "ngayu kalka nubi-mani-ny | I spear lose-CAUS-PAST",
     "search_lose_transitive", 5, js(["ngayu", "kalka", "nubi-manil"]), js([]),
     "Dictionary nubi-manil = lose.", "0.86"),
    (38, "The child lost the toy.",
     "Karrkay-ngku jaba nubi-manin.",
     "karrkay-ngku jaba nubi-mani-ny | child-ERG toy lose-CAUS-PAST",
     "search_lose_transitive", 5, js(["karrkay", "jaba", "nubi-manil"]), js([]),
     "Nubi-manil lose with familiar object.", "0.84"),
    (39, "I found the spear.",
     "Ngayu kalka warrmba-bungan.",
     "ngayu kalka warrmba-bunga-ny | I spear find-CAUS-PAST",
     "search_find", 5, js(["ngayu", "kalka", "warrmba-bungal"]), js([]),
     "Dictionary warrmba-bungal = find (Yalanji).", "0.88"),
    (40, "The woman found food.",
     "Jalbu-ngku mayi warrmba-bungan.",
     "jalbu-ngku mayi warrmba-bunga-ny | woman-ERG food find-CAUS-PAST",
     "search_find", 5, js(["jalbu", "mayi", "warrmba-bungal"]), js([]),
     "Warrmba-bungal find with everyday object.", "0.86"),
    (41, "They found the dog.",
     "Jana kaya warrmba-bungan.",
     "jana kaya warrmba-bunga-ny | they dog find-CAUS-PAST",
     "search_find", 5, js(["jana", "kaya", "warrmba-bungal"]), js([]),
     "Warrmba-bungal find; pronoun A row.", "0.84"),
    (42, "The man found fish.",
     "Dingkar-angka kuyu warrmba-bungan.",
     "dingkar-angka kuyu warrmba-bunga-ny | man-ERG fish find-CAUS-PAST",
     "search_find", 5, js(["dingkar", "kuyu", "warrmba-bungal"]), js([]),
     "Warrmba-bungal find with familiar fish object.", "0.84"),

    (43, "I tasted the food.",
     "Ngayu mayi baban.",
     "ngayu mayi baba-ny | I food try/taste-PAST",
     "taste_try", 4, js(["ngayu", "mayi", "babal"]), js([]),
     "Dictionary babal = try/taste.", "0.86"),
    (44, "The child tasted the fish.",
     "Karrkay-ngku kuyu baban.",
     "karrkay-ngku kuyu baba-ny | child-ERG fish try/taste-PAST",
     "taste_try", 4, js(["karrkay", "kuyu", "babal"]), js([]),
     "Babal try/taste with familiar fish object.", "0.84"),
    (45, "The woman tried the place out.",
     "Jalbu-ngku bubu baban.",
     "jalbu-ngku bubu baba-ny | woman-ERG place try-PAST",
     "taste_try_place", 5, js(["jalbu", "bubu", "babal"]), js([]),
     "Dictionary babal example includes trying the place out.", "0.86"),
    (46, "I smelled the food.",
     "Ngayu mayi nyuman.",
     "ngayu mayi nyuma-ny | I food smell/taste-PAST",
     "smell_taste", 4, js(["ngayu", "mayi", "nyumal"]), js([]),
     "Dictionary nyumal = smell or taste.", "0.86"),
    (47, "The child smelled the smell.",
     "Karrkay-ngku jila nyuman.",
     "karrkay-ngku jila nyuma-ny | child-ERG smell smell/taste-PAST",
     "smell_taste", 5, js(["karrkay", "jila", "nyumal"]), js([]),
     "Patz text 36 line 19 has Nyuma-ny jila.", "0.88"),
    (48, "The smell was strong.",
     "Jila junkurr-ji.",
     "jila junkurr-ji | smell strength-COMIT",
     "smell_attribute", 4, js(["jila", "junkurr", "-ji"]), js([]),
     "Patz text 36 line 16: jila junkurr-ji.", "0.90"),
    (49, "The food smells bad; do not eat it.",
     "Mayi nganjay kari nuka.",
     "mayi nganjay kari nuka | food bad.smell NEG eat.IMP",
     "bad_smell_warning", 5, js(["mayi", "nganjay", "kari", "nukal"]), js([]),
     "Dictionary nganjay usage: Mayi nganjay kari nuka.", "0.90"),
    (50, "The food has a bad taste.",
     "Mayi nganja-burra.",
     "mayi nganja-burra | food taste-bad",
     "bad_taste_attribute", 4, js(["mayi", "nganja-burra"]), js([]),
     "Dictionary nganja-burra = bad taste/distasteful.", "0.86"),
]


def scan_static() -> 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, loans_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")
        for field_name, payload in (("words_used", words_used), ("loans_used", loans_used)):
            try:
                json.loads(payload)
            except Exception as exc:
                issues.append(f"{label}: {field_name} 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/blocked 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_static()
    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")
    db.execute("BEGIN")
    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 97 draft: perception/search lexicon", "operator-guide-2026-07-07", "under_review", NOW),
        )
        batch_id = cur.lastrowid

    dupes = []
    for seq, _english, kuku, *_rest in ROWS:
        hit = db.execute(
            """
            SELECT b.label, s.seq
            FROM sentences s JOIN batches b ON b.id=s.batch_id
            WHERE lower(trim(s.kuku))=lower(trim(?))
              AND s.status!='rejected'
              AND s.batch_id!=?
            """,
            (kuku, batch_id),
        ).fetchone()
        if hit:
            dupes.append(f"{LABEL}.{seq:02d} duplicates {hit[0]}.{hit[1]:02d}: {kuku}")
    if dupes:
        db.rollback()
        print("cross-corpus duplicate check failed:")
        for dupe in dupes:
            print(f"  - {dupe}")
        return 1

    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,
                loans_used, evidence, "under_review", confidence,
                "project_approved_synthetic_pending_elder_verification",
                "claude-main", NOW, NOW,
            )
            for seq, english, kuku, analysis, frame, tier, words_used, loans_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())
