#!/usr/bin/env python3
"""Close batch-362 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-362"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-362-zero-use-flora-nonsingular-nonpast-perception-after-rule319-process.md"
LENSES = ("morphology", "lexicon", "syntax", "fidelity", "register")

NOTES = (
    "ZERO-USE FLORA NON-SINGULAR NONPAST PERCEPTION SWEEP AFTER RULE 319 (Rule 320). 50 rows. TARGETS: "
    "bikarrakal fruit, dikirr waterlily, dukul-dukul bush, kakarr grass, kalkurr yam, marrku fruit, milmarr cane, "
    "warrabuka yam, wubali yam, and wulku fig. SUPPORT: bare nominative ngali, ngaliny, yubal, yurra, ngana, "
    "Yalanji yaluy/yinyay, and dictionary-attested nonpast nyajil. Bikarrakal is fruit, kakarr grass, and "
    "dukul-dukul an indivisible dictionary head. No grub/bait, blanket/year, edibility/food/preparation, habitat, "
    "plant use, properties, taxonomy, prior-row proposition, case, new word formation, past/purposive/reduplicated "
    "perception, support inflection, or carried trap was opened."
)

LESSONS = [
    ("source", "Rule 320 selected ten direct-dictionary zero-use flora nouns: bikarrakal, dikirr, dukul-dukul, kakarr, kalkurr, marrku, milmarr, warrabuka, wubali, and wulku.", "Rule 320: zero-use flora inventory."),
    ("source", "Dictionary lines reread: bikarrakal 2369-2380, dikirr 5674-5685, dukul-dukul 6332-6346, kakarr 12122-12136, kalkurr 12741-12752, marrku 18676-18687, milmarr 19536-19547, warrabuka 26560-26570, wubali 27436-27446, wulku 27854-27862.", "Rule 320: dictionary source lines."),
    ("source", "All ten flora heads were checked against Patz and none occurs as a lexical target. The dictionary supplies their bare forms; Patz supplies pronoun, case, tense, and perception-clause licensing.", "Rule 320: Patz lexical cross-check."),
    ("source", "Patz lines 2577-2582 and 2674-2684 supplied ngali, ngaliny, yubal, yurra, ngana; lines 2834-2847 supplied Yalanji yaluy/yinyay; lines 3043-3049 and 3149-3151 supplied nonpast nyaji-l; lines 4256-4265 supplied perception A/O syntax; dictionary lines 24282-24296 supplied running-form nyajil.", "Rule 320: morphosyntactic source."),
    ("source", "Sense contracts locked bikarrakal to fruit and kakarr to grass, while dukul-dukul remained a whole attested head. Conservative labels avoided importing grub/bait, tree, blanket/year, or source properties.", "Rule 320: polysemy and lexical-form discipline."),
    ("source", "Source prose was restraint data: cooking, edibility, food, water habitat, oven covering, blanket/year, wild/native properties, taxonomy, seeds, beach growth, leaf shine, tubers, gathering, preparation, and use stayed closed.", "Rule 320: commentary restraint."),
    ("process", "Rule 320 was scoped as bare zero-use flora non-singular nonpast visual perception, not botany, ecology, food, preparation, artefact use, time/counting, productive morphology, or event content.", "Rule 320: bounded flora lexical-debt scope."),
    ("process", "All selected target forms passed intended-frame lint and all 50 exact planned surfaces returned no accepted, revised, or under-review collisions.", "Rule 320: form and duplicate preflight."),
    ("process", "Every selected target had zero accepted/revised token coverage before banking.", "Rule 320: target coverage audit."),
    ("process", "Support coverage before banking was ngali 523, ngaliny 465, yubal 475, yurra 460, ngana 1045, yaluy 3842, yinyay 4094, nyajil 3947.", "Rule 320: support coverage audit."),
    ("lexicon", "Bikarrakal was retained as visible fruit only; grub, bait, tree, cooking, edibility, preparation, use, and event propositions stayed closed.", "Rule 320: bikarrakal."),
    ("lexicon", "Dikirr was retained as visible waterlily only; edibility, food, collection, preparation, aquatic habitat, flower-part analysis, and event stayed closed.", "Rule 320: dikirr."),
    ("lexicon", "Dukul-dukul was retained as the whole bush noun only; productive reduplication, leaf properties, oven/kurrma, covering, cooking, material use, and event stayed closed.", "Rule 320: dukul-dukul."),
    ("lexicon", "Kakarr was retained in its primary grass sense only; blanket, lying/use, year, counting, number, time, and event stayed closed.", "Rule 320: kakarr."),
    ("lexicon", "Kalkurr was retained as visible yam only; wildness, taxonomy, food, edibility, tuber, gathering, preparation, and event stayed closed.", "Rule 320: kalkurr."),
    ("lexicon", "Marrku was retained as visible fruit only; edibility, food, wild/native property, cherry comparison, seed number, beach habitat, growth, leaf/shine, and event stayed closed.", "Rule 320: marrku."),
    ("lexicon", "Milmarr was retained as visible cane only; lawyer-cane subtype, edibility, food, gathering, weaving/material use, preparation, and event stayed closed.", "Rule 320: milmarr."),
    ("lexicon", "Warrabuka was retained as visible yam only; wildness, taxonomy, food, edibility, tuber, gathering, preparation, and event stayed closed.", "Rule 320: warrabuka."),
    ("lexicon", "Wubali was retained as visible yam only; taxonomy, edibility, food, tuber, gathering, preparation, and event stayed closed.", "Rule 320: wubali."),
    ("lexicon", "Wulku was retained as visible fig only; fruit-part/tree analysis, edibility, food, colour, ripeness, collection, preparation, and event stayed closed.", "Rule 320: wulku."),
    ("morphology", "All Rule 320 flora heads stayed bare absolutive objects with no case or derivation; dictionary hyphenation in dukul-dukul was preserved exactly.", "Rule 320: bare targets and source orthography."),
    ("morphology", "All subject forms stayed bare nominatives; no accusative, dative, locative, ablative, comitative, possessive, alternative, expanded, or non-selected pronoun was admitted.", "Rule 320: bare pronoun A forms."),
    ("morphology", "Yaluy, yinyay, and nyajil stayed fixed support; no demonstrative case, past nyajin, purposive nyaji-nka, reduplicated watching, hearing compound, or perception compound was admitted.", "Rule 320: fixed support morphology."),
    ("morphology", "Dukul-dukul was copied as an indivisible dictionary head, while bikarrakal and kakarr were sense-disambiguated without changing form; no new compound or productive reduplication was built.", "Rule 320: lexicalized form and polysemy boundary."),
    ("syntax", "Rows 1-10 used Ngali TARGET yaluy nyajil for first dual exclusive here nonpast visual perception.", "Rule 320: 1du exclusive here band."),
    ("syntax", "Rows 11-20 used Ngaliny TARGET yinyay nyajil for first dual inclusive there nonpast visual perception.", "Rule 320: 1du inclusive there band."),
    ("syntax", "Rows 21-30 used Yubal TARGET yaluy nyajil for second dual here nonpast visual perception.", "Rule 320: 2du here band."),
    ("syntax", "Rows 31-40 used Yurra TARGET yinyay nyajil for second plural there nonpast visual perception.", "Rule 320: 2pl there band."),
    ("syntax", "Rows 41-50 used Ngana TARGET yaluy nyajil for first plural inclusive here nonpast visual perception.", "Rule 320: 1pl inclusive here band."),
    ("fidelity", "English stayed literal present visual perception/reference prose: exact subject value, conservative lexical flora label, here/there, and see only.", "Rule 320: literal English."),
    ("fidelity", "No dictionary commentary was converted into homograph, dietary, preparation, habitat, property, taxonomy, counting/time, artefact-use, or event claims.", "Rule 320: source restraint."),
    ("register", "Rows remained plain elicitation-style perception clauses; no botanical, ecological, culinary, gathering, artefact, temporal/counting, or narrative register was introduced.", "Rule 320: register."),
    ("process", "Every Rule 320 sentence is an explicit row call with exact sequence-specific Kuku and English contracts; no runtime generator authored content.", "Rule 320: explicit-row authorship evidence."),
    ("process", "Rules 149-319 carry-forward traps stayed closed, including Rule 319 reptile mythology/property/homograph traps plus older place/story, case, productive word-formation, and support-inflection traps.", "Rule 320: cumulative trap closure."),
    ("handoff", "Next batch may continue from 18,197 if this closes; Rule 321 requires fresh source and coverage audit and must not infer closed flora homograph, food/preparation, habitat, property, taxonomy, use, counting/time, case, word-formation, perception, or support fields.", "Rule 320: next cadence."),
]

LEXEMES = [
    ("bikarrakal", "noun", None, "NA", "fruit", "batch-362: fruit sense only; grub/bait, tree, cooking, edibility, preparation, use, event closed."),
    ("dikirr", "noun", None, "NA", "waterlily", "batch-362: visible waterlily only; edibility, food, collection, preparation, habitat, flower-part analysis, event closed."),
    ("dukul-dukul", "noun", None, "NA", "bush", "batch-362: whole dictionary bush head only; productive reduplication, leaf properties, oven/covering, cooking, material use, event closed."),
    ("kakarr", "noun", None, "NA", "grass", "batch-362: grass sense only; blanket/use, year/counting, number/time, event closed."),
    ("kalkurr", "noun", None, "NA", "yam", "batch-362: visible yam only; wildness, taxonomy, food, edibility, tuber, gathering, preparation, event closed."),
    ("marrku", "noun", None, "NA", "fruit", "batch-362: visible fruit only; edibility, wild/native/cherry, seeds, beach habitat, growth, leaf/shine, event closed."),
    ("milmarr", "noun", None, "NA", "cane", "batch-362: visible cane only; subtype, edibility, food, gathering, weaving/material use, preparation, event closed."),
    ("warrabuka", "noun", None, "NA", "yam", "batch-362: visible yam only; wildness, taxonomy, food, edibility, tuber, gathering, preparation, event closed."),
    ("wubali", "noun", None, "NA", "yam", "batch-362: visible yam only; taxonomy, edibility, food, tuber, gathering, preparation, event closed."),
    ("wulku", "noun", None, "NA", "fig", "batch-362: visible fig only; tree/fruit-part, edibility, food, colour, ripeness, collection, preparation, event closed."),
    ("ngali", "pronoun", None, "NA", "first dual exclusive nominative", "batch-362: bare nominative subject support only."),
    ("ngaliny", "pronoun", None, "NA", "first dual inclusive nominative", "batch-362: bare nominative subject support only."),
    ("yubal", "pronoun", None, "NA", "second dual nominative", "batch-362: bare nominative subject support only."),
    ("yurra", "pronoun", None, "NA", "second plural nominative", "batch-362: bare nominative subject support only."),
    ("ngana", "pronoun", None, "NA", "first plural inclusive nominative", "batch-362: bare nominative subject support only."),
    ("yaluy", "demonstrative", None, "NA", "here", "batch-362: bare Yalanji local demonstrative support only."),
    ("yinyay", "demonstrative", None, "NA", "there", "batch-362: bare Yalanji local demonstrative support only."),
    ("nyajil", "verb-tr", "l", "NA", "see; see.NPST", "batch-362: dictionary-attested nonpast visual perception surface corresponding to Patz nyaji-l."),
]


def main() -> int:
    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, under, accepted = 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 under != 50 or accepted:
        raise SystemExit(f"close precheck failed total={total} under={under} accepted={accepted}")
    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("DELETE FROM reviews WHERE sentence_id=?", [(sid,) for sid in ids])
    db.execute("DELETE FROM lessons WHERE batch_id=?", (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, category, lesson, guide_change, NOW) for category, lesson, guide_change in LESSONS],
    )
    for head, pos, conj, case, 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=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, gloss, senses, attestation, None, None, None, note, None, 0, "verified", NOW, note, LABEL),
        )
        db.execute("INSERT INTO lexeme_log(headword,action,detail,created_at) VALUES(?,?,?,?)", (head, "batch-362-verify", note, NOW))
    record_event(db, batch_label=LABEL, phase="lint", event_type="under-review-lint-green", actor="codex", detail=f"After banking, 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 320 rows: bare flora O morphology, nominative non-singular pronoun A, fixed Yalanji local/nonpast support, conservative plant senses, exact English fidelity, and elicitation register; homographs, food/preparation, habitat, properties, taxonomy, use, source-prose, and event content 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 grub/bait, tree, cooking/edibility/food, collection/preparation, aquatic/beach habitat, leaf/seed/property, oven/covering/material use, blanket/year/counting, wild/native/cherry/subtype/tuber taxonomy, case, new compound/reduplication, past/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 320 lessons covering source, zero-use coverage, polysemy/lexical boundaries, morphology, syntax, fidelity, register, explicit 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.", 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 320 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()
    acc = 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-362-verify'").fetchone()[0]
    record_event(db, batch_label=LABEL, phase="close", event_type="close-success", actor="codex", detail=f"Closed Batch 362 / Rule 320: {acc} accepted, {reviews} reviews, {lessons} lessons, {logs} lexeme logs; verified {before}->{after}; Kuku words {words_before}->{words_after}.", command="python3 batch_scripts/close_362.py", output_excerpt=f"closed batch-362 accepted={acc} 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-362: accepted={acc}, reviews={reviews}, lessons={lessons}, lexeme_logs={logs}, verified {before}->{after}, kuku_words {words_before}->{words_after}")
    return 0


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