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

from __future__ import annotations

import datetime as dt
import json
import sqlite3

from process_ledger import ensure_schema, record_event

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-372"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-372-low-coverage-fish-nonsingular-past-perception-after-rule329-process.md"
LENSES = ("morphology", "lexicon", "syntax", "fidelity", "register")

NOTES = (
    "LOW-COVERAGE FISH NON-SINGULAR PAST PERCEPTION AFTER RULE 329 (Rule 330). 50 rows. TARGETS: bububay "
    "blacktip shark, baban angelfish, kundurr-kundurr crescent perch, kurruba freshwater fish, murrabal "
    "barramundi, dukul-barangkal flathead fish, jalil-jalil mudskipper, kurrubal freshwater cod, murraja "
    "stonefish, kubu-kubu tadpole. SUPPORT: bare nominative ngali, ngaliny, yubal, yurra, ngana; Yalanji "
    "yaluy/yinyay; dictionary-attested past nyajin. Agreeing duplicate and placeholder/complete records reconciled; "
    "final-m, rhotic-palatal, unhyphenated-reduplication, dialect-only, and conflicting candidates excluded. No decomposition, "
    "productive morphology, dialect substitution, habitat, catching, food, danger, source, possession, target "
    "case, nonpast/purposive/reduplicated perception, support inflection, or event proposition was opened."
)

LESSONS = [
    ("source", "Rule 330 selected ten direct non-loan fish heads at accepted/revised coverage five to seven: bububay, baban, kundurr-kundurr, kurruba, murrabal, dukul-barangkal, jalil-jalil, kurrubal, murraja, and kubu-kubu.", "Rule 330: fish inventory."),
    ("source", "Dictionary lines reread: baban 91-108, bububay 3470-3482, dukul-barangkal 6312-6322, jalil-jalil 7406-7419, kubu-kubu 14716-14729, kundurr-kundurr 15722-15735, kurruba/kurrubal 16537-16556, murrabal 20970-20980, and murraja 20981-20998.", "Rule 330: dictionary source lines."),
    ("source", "Exact Patz searches found only murraja, at lines 4279, 4340, 6505, and 6507 in inflected stonefish/poking clauses; the other nine selected heads were absent.", "Rule 330: Patz target occurrence and sense audit."),
    ("source", "Patz line 1476 includes fauna among noun referents, licensing bare visible fish reference without importing habitat, catching, food, danger, source, property, or event content.", "Rule 330: fauna noun source."),
    ("source", "Patz lines 2577-2584 source selected non-singular nominatives, 2834-2847 Yalanji yaluy/yinyay, 2021-2024 and 2568-2571 past nyaji-ny, 3160-3162 past -ny, and 4256-4265 perception A/O; dictionary examples 23876-23883 and 23968-23976 contain running nyajin.", "Rule 330: construction source."),
    ("source", "The fish audit excluded final-m kijukum, rhotic-palatal walarrji, linter-invalid unhyphenated bayilbayil, Nyungkul-only yidilnyul/yidi/kanyaji/dalku, and conflicting danbal/ngiwa/wara/walkan. Agreeing duplicates were reconciled; bububay relies only on its complete fish record, not its empty placeholder.", "Rule 330: candidate exclusions and record reconciliation."),
    ("process", "Rule 330 was scoped as bare fish reference in non-singular past visual perception, not duplicate-record identity, decomposition, productive morphology, dialect substitution, habitat, catching, food, danger, source, possession, property, or event content.", "Rule 330: bounded lexical-debt scope."),
    ("process", "All 50 rows were explicit authored calls; exact static contracts, row-level linter probes, internal uniqueness, and corpus exact-collision preflight all passed before banking.", "Rule 330: authorship and preflight."),
    ("process", "Target coverage before banking was bububay 5, baban 5, kundurr-kundurr 5, kurruba 5, murrabal 5, dukul-barangkal 6, jalil-jalil 6, kurrubal 6, murraja 7, and kubu-kubu 7.", "Rule 330: target coverage audit."),
    ("process", "Support coverage before banking was ngali 573, ngaliny 515, yubal 525, yurra 510, ngana 1085, yaluy 4092, yinyay 4344, and nyajin 1550.", "Rule 330: support coverage audit."),
    ("lexicon", "Bububay was retained as blacktip shark using only its complete fish record; placeholder reconciliation, spelling normalization, body/fin parts, colour/pattern, habitat, movement, catching, danger, food, possession, source, and events stayed closed.", "Rule 330: bububay."),
    ("lexicon", "Baban was retained as angelfish only; the source typo was reconciled by the agreeing duplicate, while spelling-variant propositions, body parts, colour/pattern, habitat, catching, food, possession, source, and events stayed closed.", "Rule 330: baban."),
    ("lexicon", "Kundurr-kundurr was retained as the exact whole crescent-perch head; free-base use, productive reduplication, crescent-shape property, habitat, catching, food, possession, source, and events stayed closed.", "Rule 330: kundurr-kundurr."),
    ("lexicon", "Kurruba was retained as freshwater fish only; species identity, habitat proposition, size/appearance, movement, catching, food, possession, source, equivalence, and events stayed closed.", "Rule 330: kurruba."),
    ("lexicon", "Murrabal was retained as the Yalanji barramundi head; Nyungkul kudi equivalence/substitution, habitat, size, catching, spearing, cooking, eating, possession, source, and events stayed closed.", "Rule 330: murrabal."),
    ("lexicon", "Dukul-barangkal was retained as the exact whole flathead-fish head; decomposition, productive compounding, dalmbal substitution/equivalence, shape, habitat, catching, food, possession, source, and events stayed closed.", "Rule 330: dukul-barangkal."),
    ("lexicon", "Jalil-jalil was retained as the exact whole mudskipper head; free-base use, productive reduplication, mud/water habitat, skipping/movement, amphibious property, catching, food, possession, source, and events stayed closed.", "Rule 330: jalil-jalil."),
    ("lexicon", "Kurrubal was retained as freshwater cod only; species identity beyond the label, habitat proposition, size/appearance, movement, catching, food, possession, source, and events stayed closed.", "Rule 330: kurrubal."),
    ("lexicon", "Murraja was retained as bare stonefish only; potent/ergative/instrumental/locative morphology, poking, injury, foot/body contact, danger, story context, habitat, catching, food, possession, source, and events stayed closed.", "Rule 330: murraja."),
    ("lexicon", "Kubu-kubu was retained as the exact whole tadpole head; free-base use, productive reduplication, frog identity/transformation, life stage, habitat, movement, catching, food, possession, source, and events stayed closed.", "Rule 330: kubu-kubu."),
    ("morphology", "All Rule 330 fish heads stayed bare absolutive objects, with no case, number, possession, derivation, dialect, habitat, event, property, source, or use morphology added.", "Rule 330: bare fish O."),
    ("morphology", "Kundurr-kundurr, jalil-jalil, and kubu-kubu remained exact whole reduplicated heads; dukul-barangkal remained an exact whole compound. No free base, decomposition, productive process, synonym substitution, number reading, or equivalence was generated.", "Rule 330: lexicalized-form restraint."),
    ("morphology", "All subject forms stayed bare nominatives: ngali, ngaliny, yubal, yurra, and ngana. No accusative, dative, locative, ablative, comitative, possessive, alternative, expanded, or non-selected pronoun was admitted.", "Rule 330: bare pronoun A forms."),
    ("morphology", "Yaluy, yinyay, and nyajin stayed fixed support; no demonstrative case, nonpast nyajil, purposive nyaji-nka, reduplicated watching, hearing/smell compound, or support inflection was admitted.", "Rule 330: fixed support morphology."),
    ("syntax", "Rows 1-10 used Ngali TARGET yaluy nyajin for first-dual-exclusive here past visual perception.", "Rule 330: 1du exclusive here band."),
    ("syntax", "Rows 11-20 used Ngaliny TARGET yinyay nyajin for first-dual-inclusive there past visual perception.", "Rule 330: 1du inclusive there band."),
    ("syntax", "Rows 21-30 used Yubal TARGET yaluy nyajin for second-dual here past visual perception.", "Rule 330: 2du here band."),
    ("syntax", "Rows 31-40 used Yurra TARGET yinyay nyajin for second-plural there past visual perception.", "Rule 330: 2pl there band."),
    ("syntax", "Rows 41-50 used Ngana TARGET yaluy nyajin for first-plural-inclusive here past visual perception.", "Rule 330: 1pl inclusive here band."),
    ("fidelity", "English stayed literal past visual-perception and reference prose: selected observer, exact conservative fish label, here or there, and saw only. Labels do not assert source commentary's morphology, dialect, habitat, subsistence, danger, relation, or event content.", "Rule 330: literal English."),
    ("fidelity", "No duplicate dictionary record, Patz stonefish event, synonym, dialect note, reduplication analysis, compound analysis, or older corpus row was converted into identity, habitat, catching, food, danger, source, possession, property, equivalence, or cultural-practice claims.", "Rule 330: source and prior-row restraint."),
    ("register", "Rows remained neutral elicitation-style fish-reference clauses. Orthotactically invalid, dialect-only, conflicting, and culturally/event-heavy candidates were excluded; no ecological or subsistence event was generated.", "Rule 330: register and candidate ethics."),
    ("process", "Every Rule 330 sentence is an explicit sequence-specific row call with exact Kuku, English, analysis, evidence, and confidence contracts; no runtime generator authored content.", "Rule 330: explicit-row authorship evidence."),
    ("process", "Rules 149-329 carry-forward traps stayed closed, including fish habitat/use, shellfish identity, food preparation, loan, source, toxicity, craft equivalence, polysemy, near-form, route, material, dialect, homograph, case, compound, productive reduplication, and support-inflection traps.", "Rule 330: cumulative trap closure."),
    ("handoff", "Next batch may continue from 18,697 if this closes; Rule 331 requires a fresh source and coverage audit and must not infer closed fish duplicate-record identity, morphology, dialect, habitat, catching, food, danger, source, possession, cultural event, case, perception, or support fields.", "Rule 330: next cadence."),
]

LEXEMES = [
    ("bububay", "noun", None, "NA", "blacktip shark", "batch-372: blacktip-shark reference from complete record only; placeholder/spelling/body/fin/colour/pattern/habitat/movement/catching/danger/food/possession/source/event content closed."),
    ("baban", "noun", None, "NA", "angelfish", "batch-372: angelfish reference only; spelling/body/colour/pattern/habitat/catching/food/possession/source/event content closed."),
    ("kundurr-kundurr", "noun", None, "NA", "crescent perch", "batch-372: exact whole head only; base/productive-reduplication/shape/habitat/catching/food/possession/source/event content closed."),
    ("kurruba", "noun", None, "NA", "freshwater fish", "batch-372: freshwater-fish reference only; species/habitat/size/appearance/movement/catching/food/possession/source/equivalence/event content closed."),
    ("murrabal", "noun", None, "NA", "barramundi", "batch-372: Yalanji barramundi reference only; dialect-equivalence/habitat/size/catching/spearing/cooking/eating/possession/source/event content closed."),
    ("dukul-barangkal", "noun", None, "NA", "flathead fish", "batch-372: exact whole head only; decomposition/productive-compounding/synonym/shape/habitat/catching/food/possession/source/event content closed."),
    ("jalil-jalil", "noun", None, "NA", "mudskipper", "batch-372: exact whole head only; base/productive-reduplication/habitat/movement/amphibious/catching/food/possession/source/event content closed."),
    ("kurrubal", "noun", None, "NA", "freshwater cod", "batch-372: freshwater-cod reference only; species/habitat/size/appearance/movement/catching/food/possession/source/event content closed."),
    ("murraja", "noun", None, "NA", "stonefish", "batch-372: bare stonefish reference only; potent/case/poking/injury/body/danger/story/habitat/catching/food/possession/source/event content closed."),
    ("kubu-kubu", "noun", None, "NA", "tadpole", "batch-372: exact whole head only; base/productive-reduplication/frog/transformation/life-stage/habitat/movement/catching/food/possession/source/event content closed."),
    ("ngali", "pronoun", None, "NA", "first dual exclusive nominative", "batch-372: bare nominative subject support only."),
    ("ngaliny", "pronoun", None, "NA", "first dual inclusive nominative", "batch-372: bare nominative subject support only."),
    ("yubal", "pronoun", None, "NA", "second dual nominative", "batch-372: bare nominative subject support only."),
    ("yurra", "pronoun", None, "NA", "second plural nominative", "batch-372: bare nominative subject support only."),
    ("ngana", "pronoun", None, "NA", "first plural inclusive nominative", "batch-372: bare nominative subject support only."),
    ("yaluy", "demonstrative", None, "NA", "here", "batch-372: bare Yalanji local demonstrative support only."),
    ("yinyay", "demonstrative", None, "NA", "there", "batch-372: bare Yalanji local demonstrative support only."),
    ("nyajin", "verb-tr", "ny", "NA", "see; see.PST", "batch-372: dictionary-attested past visual-perception surface corresponding to Patz nyaji-ny."),
]


def main() -> int:
    if len(LESSONS)!=35:raise SystemExit(f"lesson inventory changed: {len(LESSONS)} != 35")
    if len(LEXEMES)!=18:raise SystemExit(f"lexeme inventory changed: {len(LEXEMES)} != 18")
    db=sqlite3.connect(DB);db.execute("PRAGMA foreign_keys=ON");db.execute("BEGIN");ensure_schema(db)
    batch=db.execute("SELECT id FROM batches WHERE label=?",(LABEL,)).fetchone()
    if not batch:raise SystemExit(f"{LABEL} has not been banked")
    bid=batch[0];total,ur,ac=db.execute("SELECT COUNT(*),COUNT(*) FILTER(WHERE status='under_review'),COUNT(*) FILTER(WHERE status IN ('accepted','revised')) FROM sentences WHERE batch_id=?",(bid,)).fetchone()
    if total!=50 or ur!=50 or ac:raise SystemExit(f"close precheck failed total={total} under_review={ur} accepted={ac}")
    er=db.execute("SELECT COUNT(*) FROM reviews WHERE sentence_id IN (SELECT id FROM sentences WHERE batch_id=?)",(bid,)).fetchone()[0];el=db.execute("SELECT COUNT(*) FROM lessons WHERE batch_id=?",(bid,)).fetchone()[0];eg=db.execute("SELECT COUNT(*) FROM lexeme_log WHERE action='batch-372-verify'").fetchone()[0]
    if er or el or eg:raise SystemExit(f"non-destructive close precheck failed reviews={er} lessons={el} logs={eg}")
    total_rows=db.execute("SELECT COUNT(*) FROM sentences WHERE status!='rejected'").fetchone()[0];before,words_before=db.execute("SELECT COUNT(*),SUM(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1) FROM sentences WHERE status IN ('accepted','revised')").fetchone();ids=[r[0] for r in db.execute("SELECT id FROM sentences WHERE batch_id=? ORDER BY seq",(bid,))]
    db.executemany("INSERT INTO reviews(sentence_id,round,lens,verdict,issues,suggested_fix,reviewer,created_at) VALUES(?,?,?,?,?,?,?,?)",[(sid,1,lens,"pass",None,None,"codex-main-five-lens",NOW) for sid in ids for lens in LENSES])
    db.executemany("INSERT INTO lessons(batch_id,category,lesson,guide_change,created_at) VALUES(?,?,?,?,?)",[(bid,c,l,g,NOW) for c,l,g in LESSONS])
    for head,pos,conj,case_class,gloss,note in LEXEMES:
        senses=json.dumps([{"sense":gloss,"definition":note,"status":"attested"}],ensure_ascii=False);att=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=excluded.status,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""",(head,None,pos,conj,case_class,gloss,senses,att,None,None,None,note,None,0,"verified",NOW,note,LABEL))
        db.execute("INSERT INTO lexeme_log(headword,action,detail,created_at) VALUES(?,?,?,?)",(head,"batch-372-verify",note,NOW))
    record_event(db,batch_label=LABEL,phase="lint",event_type="under-review-lint-green",actor="codex",detail=f"After banking, whole-corpus lint scanned {total_rows} rows and found 0 issues; verified remained {before} before close.",command="python3 lint_corpus.py",output_excerpt=f"lint: {total_rows} rows scanned, 0 issues; GOAL {before}/20000 ({words_before} words)",artifact_path=PROCESS_LOG,created_at=NOW)
    record_event(db,batch_label=LABEL,phase="review",event_type="five-lens-pass",actor="codex",detail="Manual five-lens review passed all 50 Rule 330 rows: bare fish O morphology, bare nominative non-singular pronoun A, fixed Yalanji local/past support, exact dictionary fish heads and conservative labels, literal English fidelity, and neutral elicitation register; all duplicate identity, morphology, dialect, habitat, catching, food, danger, source, possession, and event semantics remained closed.",artifact_path=PROCESS_LOG,created_at=NOW)
    record_event(db,batch_label=LABEL,phase="review",event_type="review-detail",actor="codex",detail="No bububay placeholder/body/colour claim, baban spelling proposition, kundurr-kundurr/jalil-jalil/kubu-kubu productive reduplication, kurruba/kurrubal species claim, murrabal dialect substitution, dukul-barangkal synonym/decomposition, murraja case/poking/danger, target case, nonpast/purposive/reduplicated perception, or support-inflection drift remained.",artifact_path=PROCESS_LOG,created_at=NOW)
    record_event(db,batch_label=LABEL,phase="review",event_type="lesson-inventory",actor="codex",detail=f"Inserted {len(LESSONS)} Rule 330 lessons covering source, duplicate reconciliation, target occurrence/sense, fauna O syntax, exclusions, coverage, lexical boundaries, morphology, syntax, fidelity, register, authorship, traps, and handoff.",artifact_path=PROCESS_LOG,created_at=NOW)
    record_event(db,batch_label=LABEL,phase="review",event_type="lexeme-source-log",actor="codex",detail=f"Inserted {len(LEXEMES)} lexeme/source-log rows: ten targets and eight support heads.",artifact_path=PROCESS_LOG,created_at=NOW)
    db.execute("UPDATE sentences SET status='accepted',updated_at=? WHERE batch_id=?",(NOW,bid));db.execute("UPDATE batches SET notes=?,cheatsheet_version=?,guide_version=? WHERE id=?",(NOTES,"Rule 330 accepted","operator-guide-2026-07-10",bid))
    after,words_after=db.execute("SELECT COUNT(*),SUM(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1) FROM sentences WHERE status IN ('accepted','revised')").fetchone();accepted=db.execute("SELECT COUNT(*) FROM sentences WHERE batch_id=? AND status='accepted'",(bid,)).fetchone()[0];reviews=db.execute("SELECT COUNT(*) FROM reviews WHERE sentence_id IN (SELECT id FROM sentences WHERE batch_id=?)",(bid,)).fetchone()[0];lessons=db.execute("SELECT COUNT(*) FROM lessons WHERE batch_id=?",(bid,)).fetchone()[0];logs=db.execute("SELECT COUNT(*) FROM lexeme_log WHERE action='batch-372-verify'").fetchone()[0]
    record_event(db,batch_label=LABEL,phase="close",event_type="close-success",actor="codex",detail=f"Closed Batch 372 / Rule 330: {accepted} accepted, {reviews} reviews, {lessons} lessons, {logs} lexeme logs; verified {before}->{after}; Kuku words {words_before}->{words_after}.",command="python3 batch_scripts/close_372.py",output_excerpt=f"closed batch-372 accepted={accepted} reviews={reviews} lessons={lessons} lexeme_logs={logs} verified {before}->{after} words {words_before}->{words_after}",artifact_path=PROCESS_LOG,created_at=NOW)
    db.commit();print(f"closed batch-372: accepted={accepted}, reviews={reviews}, lessons={lessons}, lexeme_logs={logs}, verified {before}->{after}, kuku_words {words_before}->{words_after}");return 0


if __name__=="__main__":raise SystemExit(main())
