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

NOTES = (
    "LOW-COVERAGE FOOD NON-SINGULAR PAST PERCEPTION AFTER RULE 327 (Rule 328). 50 rows. TARGETS: balja "
    "food, birukay egg yolk, biyangkal yam, jadamu bananas, jamal egg white, jandu starch, jungu-jungu "
    "white ant eggs, kajimarra food, milkul soup, wawukuna long wild yam. SUPPORT: bare nominative ngali, "
    "ngaliny, yubal, yurra, nganjin; Yalanji yaluy/yinyay; dictionary-attested past nyajin. Exact "
    "conservative food labels only. No explicit loans, preparation, cooking, eating, toxicity, source, "
    "material decomposition, productive reduplication, equivalence, property, possession, target case, "
    "nonpast/purposive/reduplicated perception, support inflection, or event proposition was opened."
)

LESSONS = [
    ("source", "Rule 328 selected ten direct-dictionary, noun-typed, low-coverage food heads: balja, birukay, biyangkal, jadamu, jamal, jandu, jungu-jungu, kajimarra, milkul, and wawukuna.", "Rule 328: food inventory."),
    ("source", "Dictionary lines reread: balja 953-962, birukay 3255-3263, biyangkal 3356-3366, jadamu 7006-7014, jamal 7650-7661, jandu 7935-7945, jungu-jungu 10950-10963, kajimarra 12023-12032, milkul 19502-19511, and wawukuna 27046-27056.", "Rule 328: dictionary source lines."),
    ("source", "Exact Patz searches found milkul at lines 5171-5172 in its soup sense; the other nine target heads were absent. Dictionary readings remain the source for every admitted food sense.", "Rule 328: Patz target occurrence and sense audit."),
    ("source", "Patz lines 1474-1476 state that nominals refer to objects and nouns predominantly to corporeal objects, licensing bare visible food reference without inferring preparation, consumption, source, property, or cultural event content.", "Rule 328: nominal object source."),
    ("source", "Patz lines 2577-2586 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 328: construction source."),
    ("source", "The food audit rejected explicit English loans, undefined bulawu, mortuary-context mandi-mandi, culturally or lexically ambiguous candidates, and entries without a stable direct referential reading.", "Rule 328: candidate exclusions."),
    ("process", "Rule 328 was scoped as bare visible food reference in non-singular past visual perception, not preparation, cooking, eating, toxicity, source, plant relation, material decomposition, equivalence, property, possession, or event content.", "Rule 328: 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 328: authorship and preflight."),
    ("process", "Target coverage before banking was balja 0, birukay 5, biyangkal 5, jadamu 5, jamal 5, jandu 0, jungu-jungu 5, kajimarra 0, milkul 5, and wawukuna 6 accepted/revised token rows.", "Rule 328: target coverage audit."),
    ("process", "Support coverage before banking was ngali 563, ngaliny 505, yubal 515, yurra 500, nganjin 258, yaluy 4042, yinyay 4294, and nyajin 1500.", "Rule 328: support coverage audit."),
    ("lexicon", "Balja was retained as the conservative label balja food only; ingredient identity, fire, cooking, grinding, eating, preparation sequence, property, source, and events stayed closed.", "Rule 328: balja."),
    ("lexicon", "Birukay was retained as egg yolk only; egg source, shell, egg-white relation, breaking, separating, cooking, eating, colour, texture, possession, and events stayed closed.", "Rule 328: birukay."),
    ("lexicon", "Biyangkal was retained as yam only; toxicity, danger, roasting, grinding, leaching, water, preparation, eating, safety, plant/source, digging, and events stayed closed.", "Rule 328: biyangkal."),
    ("lexicon", "Jadamu was retained as bananas only; number claims beyond the lexical translation, plant/source, bunch, ripeness, colour, peeling, gathering, carrying, cooking, eating, possession, and events stayed closed.", "Rule 328: jadamu."),
    ("lexicon", "Jamal was retained as egg white only; egg source, yolk relation, shell, breaking, separating, cooking, eating, edibility, colour, texture, possession, and events stayed closed.", "Rule 328: jamal."),
    ("lexicon", "Jandu was retained as jandu starch only; flour, water, thickness, soup comparison, cooking, preparation, post-contact history, eating, ingredient source, and events stayed closed.", "Rule 328: jandu."),
    ("lexicon", "Jungu-jungu was retained as the exact whole white-ant-eggs head; base use, productive reduplication, ants, gathering, food use, habitat, nest, cooking, eating, number derivation, and events stayed closed.", "Rule 328: jungu-jungu."),
    ("lexicon", "Kajimarra was retained as the conservative label kajimarra food only; flour equivalence, marra or junjun decomposition, leaves, water, duration, grinding, wrapping, soaking, roasting, preparation, eating, and events stayed closed.", "Rule 328: kajimarra."),
    ("lexicon", "Milkul was retained as soup only; generated stew-equivalence claims, ingredients, grass or wuju relation, cooking, heat, eating, preparation, possession, and events stayed closed.", "Rule 328: milkul."),
    ("lexicon", "Wawukuna was retained as long wild yam only; edibility, plant/source, length predication, digging, ground/depth, preparation, cooking, eating, gathering, possession, and events stayed closed.", "Rule 328: wawukuna."),
    ("morphology", "All Rule 328 food heads stayed bare absolutive objects, with no case, number, possession, derivation, event, property, source, preparation, or material morphology added.", "Rule 328: bare food O."),
    ("morphology", "Jungu-jungu remained an exact hyphenated dictionary head. Its full-reduplication analysis did not license a free base, decomposition, productive reduplication, new number reading, or equivalence; broad English food labels likewise did not collapse distinct heads.", "Rule 328: lexicalized-form and head-distinction restraint."),
    ("morphology", "All subject forms stayed bare nominatives: ngali, ngaliny, yubal, yurra, and nganjin. No accusative, dative, locative, ablative, comitative, possessive, alternative, expanded, or non-selected pronoun was admitted.", "Rule 328: bare pronoun A forms."),
    ("morphology", "Yaluy, yinyay, and nyajin stayed fixed support; no demonstrative case, nonpast nyajil, purposive nyaji-nka, reduplicated watching, hearing or smell compound, or support inflection was admitted.", "Rule 328: fixed support morphology."),
    ("syntax", "Rows 1-10 used Ngali TARGET yinyay nyajin for first-dual-exclusive there past visual perception.", "Rule 328: 1du exclusive there band."),
    ("syntax", "Rows 11-20 used Ngaliny TARGET yaluy nyajin for first-dual-inclusive here past visual perception.", "Rule 328: 1du inclusive here band."),
    ("syntax", "Rows 21-30 used Yubal TARGET yinyay nyajin for second-dual there past visual perception.", "Rule 328: 2du there band."),
    ("syntax", "Rows 31-40 used Yurra TARGET yaluy nyajin for second-plural here past visual perception.", "Rule 328: 2pl here band."),
    ("syntax", "Rows 41-50 used Nganjin TARGET yinyay nyajin for first-plural-exclusive there past visual perception.", "Rule 328: 1pl exclusive there band."),
    ("fidelity", "English stayed literal past visual-perception and reference prose: selected observer, exact conservative food label, here or there, and saw only. Labels do not assert source commentary's preparation, consumption, source, relation, property, or event content.", "Rule 328: literal English."),
    ("fidelity", "No dictionary preparation note, Patz grass-and-soup example, polysemous relation, loan candidate, reduplication analysis, or older corpus row was converted into cooking, eating, toxicity, source, material, possession, property, equivalence, or cultural-practice claims.", "Rule 328: source and prior-row restraint."),
    ("register", "Rows remained neutral elicitation-style food-reference clauses. Explicit loans, undefined entries, mortuary-context honeycomb, culturally or lexically ambiguous candidates, and event-heavy readings were excluded.", "Rule 328: register and candidate ethics."),
    ("process", "Every Rule 328 sentence is an explicit sequence-specific row call with exact Kuku, English, analysis, evidence, and confidence contracts; no runtime generator authored content.", "Rule 328: explicit-row authorship evidence."),
    ("process", "Rules 149-327 carry-forward traps stayed closed, including food preparation/use, loan, source, toxicity, wax/ochre equivalence, craft use, polysemy, near-form, route, material, dialect, homograph, case, compound, productive reduplication, and support-inflection traps.", "Rule 328: cumulative trap closure."),
    ("handoff", "Next batch may continue from 18,597 if this closes; Rule 329 requires a fresh source and coverage audit and must not infer closed food preparation, consumption, source, toxicity, material, equivalence, cultural event, morphology, case, perception, or support fields.", "Rule 328: next cadence."),
]

LEXEMES = [
    ("balja", "noun", None, "NA", "food; ground food", "batch-370: conservative balja-food reference only; ingredient/fire/cooking/grinding/eating/preparation/property/source/event content closed."),
    ("birukay", "noun", None, "NA", "egg yolk", "batch-370: egg-yolk reference only; source/shell/white relation/breaking/separating/cooking/eating/colour/texture/possession/event content closed."),
    ("biyangkal", "noun", None, "NA", "yam", "batch-370: yam reference only; toxicity/danger/roasting/grinding/leaching/water/preparation/eating/safety/source/digging/event content closed."),
    ("jadamu", "noun", None, "NA", "bananas", "batch-370: banana reference only; plant/source/bunch/ripeness/colour/peeling/gathering/carrying/cooking/eating/possession/event content closed."),
    ("jamal", "noun", None, "NA", "egg white; albumen", "batch-370: egg-white reference only; source/yolk/shell/breaking/separating/cooking/eating/edibility/colour/texture/possession/event content closed."),
    ("jandu", "noun", None, "NA", "starch; thick prepared food", "batch-370: jandu-starch reference only; flour/water/thickness/soup-comparison/cooking/preparation/post-contact/eating/source/event content closed."),
    ("jungu-jungu", "noun", None, "NA", "white ant eggs", "batch-370: exact whole head only; base/productive-reduplication/ants/gathering/use/habitat/nest/cooking/eating/number/event content closed."),
    ("kajimarra", "noun", None, "NA", "food; flour", "batch-370: conservative kajimarra-food reference only; flour-equivalence/decomposition/leaves/water/duration/grinding/wrapping/soaking/roasting/preparation/eating/event content closed."),
    ("milkul", "noun", None, "NA", "soup; stew", "batch-370: soup reference only; generated equivalence/ingredients/grass/wuju/cooking/heat/eating/preparation/possession/event content closed."),
    ("wawukuna", "noun", None, "NA", "long wild yam", "batch-370: long-wild-yam reference only; edibility/plant/source/length/digging/depth/preparation/cooking/eating/gathering/possession/event content closed."),
    ("ngali", "pronoun", None, "NA", "first dual exclusive nominative", "batch-370: bare nominative subject support only."),
    ("ngaliny", "pronoun", None, "NA", "first dual inclusive nominative", "batch-370: bare nominative subject support only."),
    ("yubal", "pronoun", None, "NA", "second dual nominative", "batch-370: bare nominative subject support only."),
    ("yurra", "pronoun", None, "NA", "second plural nominative", "batch-370: bare nominative subject support only."),
    ("nganjin", "pronoun", None, "NA", "first plural exclusive nominative", "batch-370: bare nominative subject support only."),
    ("yaluy", "demonstrative", None, "NA", "here", "batch-370: bare Yalanji local demonstrative support only."),
    ("yinyay", "demonstrative", None, "NA", "there", "batch-370: bare Yalanji local demonstrative support only."),
    ("nyajin", "verb-tr", "ny", "NA", "see; see.PST", "batch-370: 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, 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=?",
        (bid,),
    ).fetchone()
    if total != 50 or under_review != 50 or accepted:
        raise SystemExit(f"close precheck failed total={total} under_review={under_review} accepted={accepted}")
    existing_reviews = db.execute("SELECT COUNT(*) FROM reviews WHERE sentence_id IN (SELECT id FROM sentences WHERE batch_id=?)", (bid,)).fetchone()[0]
    existing_lessons = db.execute("SELECT COUNT(*) FROM lessons WHERE batch_id=?", (bid,)).fetchone()[0]
    existing_logs = db.execute("SELECT COUNT(*) FROM lexeme_log WHERE action='batch-370-verify'").fetchone()[0]
    if existing_reviews or existing_lessons or existing_logs:
        raise SystemExit(f"non-destructive close precheck failed reviews={existing_reviews} lessons={existing_lessons} logs={existing_logs}")
    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 = [row[0] for row 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, category, lesson, guide_change, NOW) for category, lesson, guide_change in LESSONS],
    )
    for head, pos, conj, 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=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, 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-370-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 328 rows: bare food O morphology, bare nominative non-singular pronoun A, fixed Yalanji local and past support, exact dictionary food heads and conservative labels, literal English fidelity, and neutral elicitation register; all loan, preparation, cooking, eating, toxicity, source, material, equivalence, property, 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 balja process, birukay/jamal egg-part relation, biyangkal toxicity or leaching, jadamu plant/ripeness, jandu flour/cooking/history, jungu-jungu productive reduplication or gathering, kajimarra decomposition/preparation, milkul grass/eating, wawukuna edibility/digging, 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 328 lessons covering source, target occurrence and sense, food O syntax, candidate exclusions, coverage, 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: 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 328 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_count = 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-370-verify'").fetchone()[0]
    record_event(db, batch_label=LABEL, phase="close", event_type="close-success", actor="codex", detail=f"Closed Batch 370 / Rule 328: {accepted_count} accepted, {reviews} reviews, {lessons} lessons, {logs} lexeme logs; verified {before}->{after}; Kuku words {words_before}->{words_after}.", command="python3 batch_scripts/close_370.py", output_excerpt=f"closed batch-370 accepted={accepted_count} 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-370: accepted={accepted_count}, reviews={reviews}, lessons={lessons}, lexeme_logs={logs}, verified {before}->{after}, kuku_words {words_before}->{words_after}")
    return 0


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