#!/usr/bin/env python3
"""Close Batch 387 after explicit 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-387"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-387-complementary-sense-qualified-reptile-set-c-nonsingular-past-perception-after-rule344-process.md"
LENSES = ("morphology", "lexicon", "syntax", "fidelity", "register")

NOTES = (
    "COMPLEMENTARY SENSE-QUALIFIED REPTILE SET-C NON-SINGULAR PAST PERCEPTION AFTER RULE 344 (Rule 345). "
    "50 accepted exact deictic complements over the ten set-C heads. Every candidate was empty and every opposite "
    "exactly Batch 386 seq 1-50. Patz ngali 1du.excl / ngaliny 1du.incl continues to override dictionary "
    "misgloss/omission; deferred ngana remains grammatical. All target boundaries remain closed. Completion "
    "necessarily changes deictic gap 162->172."
)

LESSONS = [
    ("source", "Rule 345 re-hashed dictionary and Patz; both remained byte-identical to Rule 344 inputs.", "Rule 345: source hashes."),
    ("source", "Fresh target record counts remained 2/1/2/3/1/2/1/1/1/1.", "Rule 345: target records."),
    ("source", "Support cards remained ngali 1, ngaliny 0, yubal 1, yurra 1, nganjin 1 and ngana 1.", "Rule 345: support cards."),
    ("source", "Patz still controls ngali 1du exclusive and ngaliny 1du inclusive over the dictionary misgloss/omission.", "Rule 345: clusivity ruling."),
    ("source", "Deferred ngana remains grammatical 1pl inclusive at 1,115 uses; Rule 345 licenses no absence claim.", "Rule 345: ngana restraint."),
    ("source", "The ten generic creature labels remain unchanged; deictic completion adds no zoological sense.", "Rule 345: no new sense."),
    ("source", "Patz target hits delimit excluded case, kinship, myth, bite and kill contexts; no source event entered.", "Rule 345: source boundaries."),
    ("process", "All 50 selected complements were empty and every opposite mapped exactly to Batch 386 sequence 1-50.", "Rule 345: complement proof."),
    ("process", "Completion changes deictic gap 162->172; the apparent 152-gap inverse is inadmissible Batch 386 duplication.", "Rule 345: balance versus duplication."),
    ("process", "Raw target coverage before banking was 20/25/26/28/28/29/29/33/35/36.", "Rule 345: target baseline."),
    ("process", "Support before banking was ngali 663, ngaliny 605, yubal 615, yurra 600, nganjin 328, ngana 1115, yaluy 4512, yinyay 4674 and nyajin 2300.", "Rule 345: support baseline."),
    ("lexicon", "Kalkamuku remained generic snake; duplicate identity, property/habitat, danger and events stayed closed.", "Rule 345: kalkamuku."),
    ("lexicon", "Karrkurr remained generic frog; property, habitat, weather/sound/food/knowledge and events stayed closed.", "Rule 345: karrkurr."),
    ("lexicon", "Jinkalmu remained generic snake; taxonomy, body, boys/eating/fighting, bite and kill stayed closed.", "Rule 345: jinkalmu."),
    ("lexicon", "Barka selected snake only; nut/flora, record alignment, property/habitat, food and events stayed closed.", "Rule 345: barka."),
    ("lexicon", "Yalnga remained generic turtle; habitat, poison/sacs/removal/butchery, food and events stayed closed.", "Rule 345: yalnga."),
    ("lexicon", "Biwul selected snake only; kinship/avoidance, property, case, social events and danger stayed closed.", "Rule 345: biwul."),
    ("lexicon", "Yirku-yirku remained the exact whole frog head; base/process, sound, habitat and events stayed closed.", "Rule 345: yirku-yirku."),
    ("lexicon", "Jadi remained generic goanna; habitat, age/taboo, eating/food, hunting and events stayed closed.", "Rule 345: jadi."),
    ("lexicon", "Walkarr remained bare generic goanna; property/habitat, case, hunting/food and events stayed closed.", "Rule 345: walkarr."),
    ("lexicon", "Bajabaja remained the exact whole lizard head; base/process, myth/place, property/body and events stayed closed.", "Rule 345: bajabaja."),
    ("morphology", "All targets remained bare absolutive O; no case, derivation, property or event morphology entered.", "Rule 345: bare O."),
    ("morphology", "Yirku-yirku and bajabaja remained exact lexicalized whole heads; no productive rule entered.", "Rule 345: whole heads."),
    ("morphology", "Ngali, ngaliny, yubal, yurra and nganjin remained bare nominative A with number/clusivity explicit.", "Rule 345: pronoun A."),
    ("morphology", "Yaluy, yinyay and nyajin remained fixed; no dialect, case, nonpast, purposive or perception compound entered.", "Rule 345: support."),
    ("syntax", "Rows 1-10 used Ngali TARGET yinyay nyajin with explicit 1du exclusive English.", "Rule 345: 1du exclusive there."),
    ("syntax", "Rows 11-20 used Ngaliny TARGET yaluy nyajin with explicit 1du inclusive English.", "Rule 345: 1du inclusive here."),
    ("syntax", "Rows 21-30 used Yubal TARGET yinyay nyajin with explicit second-dual English.", "Rule 345: 2du there."),
    ("syntax", "Rows 31-40 used Yurra TARGET yaluy nyajin with explicit second-plural English.", "Rule 345: 2pl here."),
    ("syntax", "Rows 41-50 used Nganjin TARGET yinyay nyajin with explicit 1pl exclusive English.", "Rule 345: 1pl exclusive there."),
    ("fidelity", "English preserved number/clusivity, generic creature label, location and past seeing only.", "Rule 345: fidelity."),
    ("fidelity", "No alternate record, taxonomy, flora/kinship, culture/food, property/habitat, source/case or event entered English.", "Rule 345: exclusion fidelity."),
    ("register", "Rows remained neutral elicitation clauses; source defects and cultural prose stayed metadata.", "Rule 345: register."),
    ("process", "All 50 sentences were explicit row calls; no runtime generator authored content and Rules 149-344 traps stayed closed.", "Rule 345: authorship and cumulative traps."),
    ("handoff", "Next batch may continue from 19,447 only after a fresh lexical-field/source/gap audit; the completed grid must not be repeated.", "Rule 345: handoff."),
]

LEXEMES = [
    ("kalkamuku", "noun", None, "NA", "snake", "batch-387: complementary non-singular generic snake only; prior boundaries preserved."),
    ("karrkurr", "noun", None, "NA", "frog", "batch-387: complementary non-singular generic frog only; prior boundaries preserved."),
    ("jinkalmu", "noun", None, "NA", "snake", "batch-387: complementary non-singular generic snake only; taxonomy/cultural boundaries preserved."),
    ("barka", "noun", None, "NA", "snake", "batch-387: complementary snake sense only; nut/flora boundaries preserved."),
    ("yalnga", "noun", None, "NA", "turtle", "batch-387: complementary generic turtle only; food-safety boundaries preserved."),
    ("biwul", "noun", None, "NA", "snake", "batch-387: complementary snake sense only; kinship/avoidance boundaries preserved."),
    ("yirku-yirku", "noun", None, "NA", "frog", "batch-387: complementary exact whole frog head; base/process boundaries preserved."),
    ("jadi", "noun", None, "NA", "goanna", "batch-387: complementary generic goanna only; taboo/food boundaries preserved."),
    ("walkarr", "noun", None, "NA", "goanna", "batch-387: complementary bare generic goanna; property/case/use boundaries preserved."),
    ("bajabaja", "noun", None, "NA", "lizard", "batch-387: complementary exact whole lizard head; myth/place/base boundaries preserved."),
    ("ngali", "pronoun", None, "NA", "first dual exclusive nominative", "batch-387: Patz controls over dictionary misgloss; bare A only."),
    ("ngaliny", "pronoun", None, "NA", "first dual inclusive nominative", "batch-387: Patz controls despite absent exact dictionary card; bare A only."),
    ("yubal", "pronoun", None, "NA", "second dual nominative", "batch-387: bare A only."),
    ("yurra", "pronoun", None, "NA", "second plural nominative", "batch-387: bare A only."),
    ("nganjin", "pronoun", None, "NA", "first plural exclusive nominative", "batch-387: bare A only; distinct from deferred ngana."),
    ("yaluy", "demonstrative", None, "NA", "here", "batch-387: Yalanji local support only."),
    ("yinyay", "demonstrative", None, "NA", "there", "batch-387: Yalanji local support only."),
    ("nyajin", "verb-tr", "l", "NA", "see; see.PST", "batch-387: l-conjugation root nyaji-l with past -ny; running surface."),
]


def main() -> int:
    if len(LESSONS) != 35:
        raise SystemExit(f"lessons {len(LESSONS)}")
    if len(LEXEMES) != 18:
        raise SystemExit(f"lexemes {len(LEXEMES)}")
    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("not banked")
    batch_id = batch[0]
    counts = db.execute(
        """SELECT COUNT(*),COUNT(*) FILTER(WHERE status='under_review'),
                  COUNT(*) FILTER(WHERE status IN('accepted','revised')) FROM sentences WHERE batch_id=?""",
        (batch_id,),
    ).fetchone()
    if counts != (50, 50, 0):
        raise SystemExit(f"precheck {counts}")
    if db.execute("SELECT COUNT(*) FROM reviews WHERE sentence_id IN(SELECT id FROM sentences WHERE batch_id=?)", (batch_id,)).fetchone()[0]:
        raise SystemExit("reviews already exist")
    if db.execute("SELECT COUNT(*) FROM lessons WHERE batch_id=?", (batch_id,)).fetchone()[0]:
        raise SystemExit("lessons already exist")
    if db.execute("SELECT COUNT(*) FROM lexeme_log WHERE action='batch-387-verify'").fetchone()[0]:
        raise SystemExit("lexeme logs already exist")
    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()
    sentence_ids = [row[0] for row in db.execute("SELECT id FROM sentences WHERE batch_id=? ORDER BY seq", (batch_id,))]
    db.executemany(
        "INSERT INTO reviews(sentence_id,round,lens,verdict,issues,suggested_fix,reviewer,created_at) VALUES(?,?,?,?,?,?,?,?)",
        [(sentence_id, 1, lens, "pass", None, None, "codex-main-five-lens", NOW) for sentence_id in sentence_ids for lens in LENSES],
    )
    db.executemany(
        "INSERT INTO lessons(batch_id,category,lesson,guide_change,created_at) VALUES(?,?,?,?,?)",
        [(batch_id, category, lesson, guide_change, NOW) for category, lesson, guide_change in LESSONS],
    )
    for headword, pos, conjugation, 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""",
            (headword, None, pos, conjugation, 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(?,?,?,?)", (headword, "batch-387-verify", note, NOW))
    events = [
        ("lint", "under-review-lint-green", f"Whole-corpus lint scanned {total_rows} rows with zero issues; verified remained {before}."),
        ("review", "five-lens-pass", "All 250 judgments passed; complementary number/clusivity, target senses, syntax, fidelity and register retained."),
        ("review", "review-detail", "Pronoun conflict, ngana distinction, target record/polysemy/taxonomy/cultural/food/whole-head/property/habitat/source/case/event and support drift remained controlled."),
        ("review", "lesson-inventory", f"Inserted {len(LESSONS)} Rule 345 lessons."),
        ("review", "lexeme-source-log", f"Inserted {len(LEXEMES)} Rule 345 lexeme/source logs."),
    ]
    for phase, event_type, detail in events:
        record_event(db, batch_label=LABEL, phase=phase, event_type=event_type, actor="codex", detail=detail, artifact_path=PROCESS_LOG, created_at=NOW)
    db.execute("UPDATE sentences SET status='accepted',updated_at=? WHERE batch_id=?", (NOW, batch_id))
    db.execute("UPDATE batches SET notes=?,cheatsheet_version=?,guide_version=? WHERE id=?", (NOTES, "Rule 345 accepted", "operator-guide-2026-07-10", batch_id))
    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'", (batch_id,)).fetchone()[0]
    review_count = db.execute("SELECT COUNT(*) FROM reviews WHERE sentence_id IN(SELECT id FROM sentences WHERE batch_id=?)", (batch_id,)).fetchone()[0]
    lesson_count = db.execute("SELECT COUNT(*) FROM lessons WHERE batch_id=?", (batch_id,)).fetchone()[0]
    log_count = db.execute("SELECT COUNT(*) FROM lexeme_log WHERE action='batch-387-verify'").fetchone()[0]
    record_event(
        db, batch_label=LABEL, phase="close", event_type="close-success", actor="codex",
        detail=f"Closed Rule 345: accepted={accepted_count}, reviews={review_count}, lessons={lesson_count}, logs={log_count}; verified {before}->{after}; words {words_before}->{words_after}.",
        command="python3 batch_scripts/close_387.py", artifact_path=PROCESS_LOG, created_at=NOW,
    )
    db.commit()
    print(f"closed batch-387: accepted={accepted_count} reviews={review_count} lessons={lesson_count} logs={log_count} verified {before}->{after} words {words_before}->{words_after}")
    return 0


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