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

NOTES = (
    "LOW-COVERAGE VISIBLE ENVIRONMENT NON-SINGULAR NONPAST PERCEPTION AFTER RULE 325 (Rule 326). 50 rows. "
    "TARGETS: dalngarri flames, jukar-warra whirlwind, kambar white clay, kurrkal shortcut, marrka calm sea, "
    "miral water scum, mulawajarr star, nganyil haze, nyamun natural debris, wurrbal fog. SUPPORT: bare nominative "
    "ngali, ngaliny, yubal, yurra, ngana; Yalanji yaluy/yinyay; dictionary-attested nonpast nyajil. Exact "
    "conservative environmental labels only. No urine/death, compound decomposition, wet-season, travel/route use, "
    "salt-pan/oyster, whale oil, dialect substitution, near-form drift, household waste, river/rising, weather "
    "event, property, source relation, target case, past/purposive/reduplicated perception, support inflection, or "
    "carried trap was opened."
)

LESSONS = [
    ("source", "Rule 326 selected ten direct-dictionary, noun-typed, low-coverage visible-environment heads: dalngarri, jukar-warra, kambar, kurrkal, marrka, miral, mulawajarr, nganyil, nyamun, and wurrbal.", "Rule 326: visible-environment inventory."),
    ("source", "Dictionary lines reread: dalngarri 4979-5001, jukar-warra 10292-10302, kambar/kambarr 12987-13011, kurrkal 16451-16470, marrka 18626-18638, miral 19714-19726, mulawajarr 20370-20380, nganyil/nganyin 22642-22664, nyamun 24330-24340, wurrbal 28402-28413.", "Rule 326: dictionary source lines."),
    ("source", "Exact Patz searches found marrka at lines 2227-2229 only as the salt-pan etymon in marrka-bina and found none of the other nine target heads. Every selected reading therefore remains dictionary-sourced.", "Rule 326: Patz target occurrence and sense audit."),
    ("source", "Patz line 1476 includes geographical/geological items, celestial bodies, weather phenomena, and other corporeal objects among noun referents, licensing bare environment objects rather than inferred events or properties.", "Rule 326: environment noun source."),
    ("source", "Patz lines 2577-2584 source non-singular nominatives; 2834-2847 Yalanji yaluy/yinyay; 3043-3049 nonpast nyaji-l; 3149-3151 nonpast function; 4256-4265 perception A/O; dictionary 24282-24296 running-form nyajil.", "Rule 326: construction source."),
    ("source", "The environment audit rejected undefined/place-name walba-murru, culturally specific nurru ancestral home, conflicting jangka, English-loan maji, event-heavy minji/ngarngkul splash variants, ambiguous yikin bank/river, and non-noun locationals.", "Rule 326: candidate exclusions."),
    ("process", "Rule 326 was scoped as bare visible-environment reference in non-singular nonpast visual perception, not weather event, travel/route use, material use, dialect alignment, source relation, property, polysemy expansion, or event content.", "Rule 326: bounded lexical-debt scope."),
    ("process", "All 50 rows were explicit authored calls; static contracts, row-level linter probes, internal uniqueness, and corpus exact-collision preflight all passed before banking.", "Rule 326: authorship and preflight."),
    ("process", "Target coverage before banking ranged from zero to nine accepted/revised token rows: four zero-use heads and six five-to-nine-use controls.", "Rule 326: target coverage audit."),
    ("process", "Support coverage before banking was ngali 553, ngaliny 495, yubal 505, yurra 490, ngana 1075, yaluy 3992, yinyay 4244, nyajil 4097.", "Rule 326: support coverage audit."),
    ("lexicon", "Dalngarri was retained as flames only; urine/urination, toilet, light causation, fire making, burning, heat, danger, synonym substitution, and events stayed closed.", "Rule 326: dalngarri."),
    ("lexicon", "Jukar-warra was retained as one whirlwind head; decomposition, productive compounding, sand, wind movement, weather prediction, damage, direction, dust, travel, and events stayed closed.", "Rule 326: jukar-warra."),
    ("lexicon", "Kambar was retained as white clay only; wet season, colour predication, digging, painting/body/material use, double-r kambarr corpse liquid, death/fluid, and events stayed closed.", "Rule 326: kambar."),
    ("lexicon", "Kurrkal was retained as shortcut only; travel, route choice, destination, direction, speed, walking/use, derived kurrkal-warri, place identity, and events stayed closed.", "Rule 326: kurrkal."),
    ("lexicon", "Marrka was retained as calm sea only; salt pan, Patz etymological occurrence, marrka-bina oyster, mangrove/swamp, tide, weather cause, water change, boating, fishing, and events stayed closed.", "Rule 326: marrka."),
    ("lexicon", "Miral was retained as water scum only; whale oil, animal source, texture/property, pollution, removal, touching, collecting, use, and events stayed closed.", "Rule 326: miral."),
    ("lexicon", "Mulawajarr was retained as the Yalanji star term; Nyungkul dawar, celestial movement, time/season, direction/navigation, brightness, number, story/totem, weather, and events stayed closed.", "Rule 326: mulawajarr."),
    ("lexicon", "Nganyil was retained as haze only; weather cause/change, visibility effects, smoke/fire, season, location, mist-normalization claims, and near-form nganyin onion/sugar-bag stayed closed.", "Rule 326: nganyil."),
    ("lexicon", "Nyamun was retained as natural debris only; household waste, leaf/beach/scrub subtype choice, decay, gardening/growing use, gathering, clearing, burning, disposal, and events stayed closed.", "Rule 326: nyamun."),
    ("lexicon", "Wurrbal was retained as fog only; river/source location, rising/movement, weather cause/change, visibility, travel danger, season/time, mist normalization, and events stayed closed.", "Rule 326: wurrbal."),
    ("morphology", "All Rule 326 environment heads stayed bare absolutive objects, with no case, number, possession, derivation, event, or property morphology added.", "Rule 326: bare environment O."),
    ("morphology", "Jukar-warra remained an exact whole dictionary head. No component, base, newly productive compound, decomposition, equivalence, or derivation was generated; all selected near-form contrasts remained exact.", "Rule 326: lexicalized-form and near-form restraint."),
    ("morphology", "All subject forms stayed bare nominatives; no accusative, dative, locative, ablative, comitative, possessive, alternative, expanded, or non-selected pronoun was admitted.", "Rule 326: bare pronoun A forms."),
    ("morphology", "Yaluy, yinyay, and nyajil stayed fixed support; no demonstrative case, past nyajin, purposive nyaji-nka, reduplicated watching, hearing/smell compound, or perception compound was admitted.", "Rule 326: fixed support morphology."),
    ("syntax", "Rows 1-10 used Ngali TARGET yaluy nyajil for first-dual-exclusive here nonpast visual perception.", "Rule 326: 1du exclusive here band."),
    ("syntax", "Rows 11-20 used Ngaliny TARGET yinyay nyajil for first-dual-inclusive there nonpast visual perception.", "Rule 326: 1du inclusive there band."),
    ("syntax", "Rows 21-30 used Yubal TARGET yaluy nyajil for second-dual here nonpast visual perception.", "Rule 326: 2du here band."),
    ("syntax", "Rows 31-40 used Yurra TARGET yinyay nyajil for second-plural there nonpast visual perception.", "Rule 326: 2pl there band."),
    ("syntax", "Rows 41-50 used Ngana TARGET yaluy nyajil for first-plural-inclusive here nonpast visual perception.", "Rule 326: 1pl inclusive here band."),
    ("fidelity", "English stayed literal nonpast visual-perception/reference prose: selected observer, exact conservative environment label, here/there, and see only. Labels do not assert source commentary's causes, uses, relations, or events.", "Rule 326: literal English."),
    ("fidelity", "No dictionary example, Patz salt-pan etymology, polysemous sense, near form, or older corpus row was converted into urine, weather, travel, material, death, food, pollution, navigation, waste-use, visibility, or source-location claims.", "Rule 326: source and prior-row restraint."),
    ("register", "Rows remained neutral elicitation-style environmental-reference clauses. Culturally specific ancestral-home, undefined/place-name, loan, conflicting, event-heavy, ambiguous, and non-noun candidates were excluded.", "Rule 326: register and candidate ethics."),
    ("process", "Every Rule 326 sentence is an explicit sequence-specific row call with exact Kuku, English, analysis, evidence, and confidence contracts; no runtime generator authored content.", "Rule 326: explicit-row authorship evidence."),
    ("process", "Rules 149-325 carry-forward traps stayed closed, including environment event/use, dialect, polysemy, near-form, route, material, source, homograph, case, compound, productive-reduplication, fauna, flora, human, and support-inflection traps.", "Rule 326: cumulative trap closure."),
    ("handoff", "Next batch may continue from 18,497 if this closes; Rule 327 requires a fresh source and coverage audit and must not infer closed environment event, polysemy, near-form, route/use, material, dialect, source, property, morphology, case, perception, or support fields.", "Rule 326: next cadence."),
]

LEXEMES = [
    ("dalngarri", "noun", None, "NA", "flames", "batch-368: flame nonpast-perception reference only; urine/toilet/light-causation/fire/heat/danger/synonym/event content closed."),
    ("jukar-warra", "noun", None, "NA", "whirlwind", "batch-368: indivisible whirlwind head only; decomposition/sand/wind/weather/damage/direction/dust/travel/event content closed."),
    ("kambar", "noun", None, "NA", "white clay", "batch-368: white-clay reference only; wet-season/property/digging/painting/use and kambarr corpse-fluid/death/event content closed."),
    ("kurrkal", "noun", None, "NA", "shortcut", "batch-368: shortcut reference only; travel/route/destination/direction/speed/walking/use/derived verb/place/event content closed."),
    ("marrka", "noun", None, "NA", "calm sea", "batch-368: calm-sea reference only; salt-pan/Patz etymology/oyster/mangrove/tide/weather/boating/fishing/event content closed."),
    ("miral", "noun", None, "NA", "water scum", "batch-368: water-scum reference only; whale-oil/source/property/pollution/removal/contact/collection/use/event content closed."),
    ("mulawajarr", "noun", None, "NA", "star", "batch-368: Yalanji star reference only; Nyungkul substitution/movement/time/navigation/brightness/number/story/weather/event content closed."),
    ("nganyil", "noun", None, "NA", "haze", "batch-368: haze reference only; weather/visibility/smoke/season/location/mist-normalization and nganyin senses closed."),
    ("nyamun", "noun", None, "NA", "natural debris", "batch-368: natural-debris reference only; household/subtype/decay/gardening/gathering/clearing/burning/disposal/event content closed."),
    ("wurrbal", "noun", None, "NA", "fog", "batch-368: fog reference only; river/source/rising/weather/visibility/travel-danger/season/mist-normalization/event content closed."),
    ("ngali", "pronoun", None, "NA", "first dual exclusive nominative", "batch-368: bare nominative subject support only."),
    ("ngaliny", "pronoun", None, "NA", "first dual inclusive nominative", "batch-368: bare nominative subject support only."),
    ("yubal", "pronoun", None, "NA", "second dual nominative", "batch-368: bare nominative subject support only."),
    ("yurra", "pronoun", None, "NA", "second plural nominative", "batch-368: bare nominative subject support only."),
    ("ngana", "pronoun", None, "NA", "first plural inclusive nominative", "batch-368: bare nominative subject support only."),
    ("yaluy", "demonstrative", None, "NA", "here", "batch-368: bare Yalanji local demonstrative support only."),
    ("yinyay", "demonstrative", None, "NA", "there", "batch-368: bare Yalanji local demonstrative support only."),
    ("nyajil", "verb-tr", "l", "NA", "see; see.NPST", "batch-368: dictionary-attested nonpast visual-perception surface corresponding to Patz nyaji-l."),
]


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-368-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 = [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, 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-368-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 326 rows: bare environment O morphology, nominative non-singular pronoun A, fixed Yalanji local/nonpast support, exact dictionary environment heads and conservative labels, literal English fidelity, and neutral elicitation register; all polysemy, near-form, weather-event, route/use, material, dialect, source, property, 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 dalngarri urine, jukar-warra decomposition, kambar wet-season/kambarr death-fluid, kurrkal travel, marrka salt-pan/oyster, miral whale-oil, mulawajarr Nyungkul/navigation, nganyil/nganyin drift, nyamun household/gardening, wurrbal river/rising, target case, 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 326 lessons covering source, target occurrence/sense, environment 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 326 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-368-verify'").fetchone()[0]
    record_event(db, batch_label=LABEL, phase="close", event_type="close-success", actor="codex", detail=f"Closed Batch 368 / Rule 326: {accepted_count} accepted, {reviews} reviews, {lessons} lessons, {logs} lexeme logs; verified {before}->{after}; Kuku words {words_before}->{words_after}.", command="python3 batch_scripts/close_368.py", output_excerpt=f"closed batch-368 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-368: 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())
