#!/usr/bin/env python3
"""Close Batch 389 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-389"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-389-complementary-bird-set-d-singular-third-past-perception-after-rule346-process.md"
LENSES = ("morphology", "lexicon", "syntax", "fidelity", "register")
NOTES = (
    "COMPLEMENTARY BIRD SET-D SINGULAR/THIRD PAST PERCEPTION AFTER RULE 346 (Rule 347). 50 accepted exact "
    "deictic complements; every candidate empty and every opposite exactly Batch 388 seq 1-50. Fresh two-card "
    "inventory and corrected wakuka three-hit profile unchanged. All unknown/conflicting taxonomy, card/domain, "
    "whole-head, culture, property/habitat/place/source/event boundaries remain closed. Completion changes gap 162->172."
)

LESSONS = [
    ("source", "Rule 347 re-hashed dictionary and Patz; both remained byte-identical to Rule 346 inputs.", "Rule 347: source hashes."),
    ("source", "Fresh YAML parsing retained exactly two records for every set-D bird head.", "Rule 347: records."),
    ("source", "Exact Patz profile remained wakuka three and every other target zero; Wakuka/Wakooka Station remained proper-name context.", "Rule 347: Patz profile."),
    ("source", "Nine zero-hit results remained source-coverage observations, not negative lexical claims.", "Rule 347: absence discipline."),
    ("source", "Rule 346 conservative labels were retained unchanged; complement adds no sense.", "Rule 347: labels."),
    ("process", "All 50 selected complements were empty and every opposite mapped exactly to Batch 388 sequence 1-50.", "Rule 347: complement proof."),
    ("process", "Completion changes deictic gap 162->172; apparent 152-gap inverse is inadmissible Batch 388 duplication.", "Rule 347: balance."),
    ("process", "Raw target coverage before banking was 10/10/10/10/11/11/11/11/11/12.", "Rule 347: target baseline."),
    ("process", "Support before banking was ngayu 1491, yundu 1087, nyulu 762, bula 558, jana 1263, yaluy 4562, yinyay 4724 and nyajin 2400.", "Rule 347: support baseline."),
    ("lexicon", "Diwalngku remained generic bird; unknown taxonomy, migration, route/habitat and events stayed closed.", "Rule 347: diwalngku."),
    ("lexicon", "Bujil-yiran remained ibis; empty cognition card, fear-base decomposition, free components and events stayed closed.", "Rule 347: bujil-yiran."),
    ("lexicon", "Karrmbil remained generic parakeet; taxonomic alignment, colour/habitat and events stayed closed.", "Rule 347: karrmbil."),
    ("lexicon", "Yiki remained generic owl; night/species resolution, nocturnal behavior, sound/hunting and events stayed closed.", "Rule 347: yiki."),
    ("lexicon", "Bajal remained generic bower bird; duplicate collapse, habitat, building/collecting/stealing and events stayed closed.", "Rule 347: bajal."),
    ("lexicon", "Biru-biru remained exact whole rainbow-bird head; taxonomy, base/reduplication, nesting/honey signs and events stayed closed.", "Rule 347: biru-biru."),
    ("lexicon", "Jangkan remained swift bird noun; speed/property, flight, season and events stayed closed.", "Rule 347: jangkan."),
    ("lexicon", "Kurranday remained generic seagull; duplicate, habitat, warning/sound/feeding/flight and events stayed closed.", "Rule 347: kurranday."),
    ("lexicon", "Ngarkay remained generic waterhen; duplicate, habitat, walking/feeding/hunting and events stayed closed.", "Rule 347: ngarkay."),
    ("lexicon", "Wakuka remained generic bird; crow/kookaburra, messenger/death, station place-name, sound and events stayed closed.", "Rule 347: wakuka."),
    ("morphology", "All targets remained bare absolutive O; no case, derivation, property or event morphology entered.", "Rule 347: bare O."),
    ("morphology", "Bujil-yiran and biru-biru remained exact whole heads; no free component or productive process entered.", "Rule 347: whole heads."),
    ("morphology", "Ngayu, yundu, nyulu, bula and jana remained bare nominative A.", "Rule 347: pronouns."),
    ("morphology", "Yaluy, yinyay and nyajin remained fixed; no dialect, case, nonpast, purposive or compound entered.", "Rule 347: support."),
    ("syntax", "Rows 1-10 used Ngayu TARGET yinyay nyajin.", "Rule 347: 1sg there."),
    ("syntax", "Rows 11-20 used Yundu TARGET yaluy nyajin.", "Rule 347: 2sg here."),
    ("syntax", "Rows 21-30 used Nyulu TARGET yinyay nyajin.", "Rule 347: 3sg there."),
    ("syntax", "Rows 31-40 used Bula TARGET yaluy nyajin.", "Rule 347: 3du here."),
    ("syntax", "Rows 41-50 used Jana TARGET yinyay nyajin.", "Rule 347: 3pl there."),
    ("fidelity", "English preserved participant, conservative bird label, location and past seeing only.", "Rule 347: fidelity."),
    ("fidelity", "No alternate card, taxonomy, composition, culture, property/behavior/habitat/place/source or event entered English.", "Rule 347: exclusions."),
    ("register", "Rows remained neutral elicitation clauses; beliefs, place and ecological prose stayed metadata.", "Rule 347: register."),
    ("process", "All 50 sentences were explicit row calls; no runtime generator authored content.", "Rule 347: authorship."),
    ("process", "Rules 149-346 traps stayed closed, including completed set-C and Rule 346 bird boundaries.", "Rule 347: cumulative traps."),
    ("process", "Source/card/Patz audits, explicit rows, scripts, review, export and publication remain in the mounted-drive process record.", "Rule 347: storage."),
    ("handoff", "Next batch may continue from 19,547 only after a fresh source/gap audit; completed singular/third set-D cells must not be reused.", "Rule 347: handoff."),
]

LEXEMES = [
    ("diwalngku", "noun", None, "NA", "bird", "batch-389: complementary generic bird; unknown taxonomy/migration/event boundaries preserved."),
    ("bujil-yiran", "noun", None, "NA", "ibis", "batch-389: complementary ibis; cognition-card/component/fear/event boundaries preserved."),
    ("karrmbil", "noun", None, "NA", "parakeet", "batch-389: complementary generic parakeet; taxonomy/property/event boundaries preserved."),
    ("yiki", "noun", None, "NA", "owl", "batch-389: complementary generic owl; night/species/behavior boundaries preserved."),
    ("bajal", "noun", None, "NA", "bower bird", "batch-389: complementary generic bower bird; duplicate/build/collect/event boundaries preserved."),
    ("biru-biru", "noun", None, "NA", "rainbow bird", "batch-389: complementary exact whole head; taxonomy/base/reduplication/ecology boundaries preserved."),
    ("jangkan", "noun", None, "NA", "swift bird", "batch-389: complementary bird noun; speed/flight/event boundaries preserved."),
    ("kurranday", "noun", None, "NA", "seagull", "batch-389: complementary generic seagull; habitat/warning/feeding/event boundaries preserved."),
    ("ngarkay", "noun", None, "NA", "waterhen", "batch-389: complementary generic waterhen; habitat/motion/feeding/event boundaries preserved."),
    ("wakuka", "noun", None, "NA", "bird", "batch-389: complementary generic bird; taxonomy/belief/place/source-event boundaries preserved."),
    ("ngayu", "pronoun", None, "NA", "first singular nominative", "batch-389: bare A only."),
    ("yundu", "pronoun", None, "NA", "second singular nominative", "batch-389: bare A only."),
    ("nyulu", "pronoun", None, "NA", "third singular nominative", "batch-389: bare A only."),
    ("bula", "pronoun", None, "NA", "third dual nominative", "batch-389: bare A only."),
    ("jana", "pronoun", None, "NA", "third plural nominative", "batch-389: bare A only."),
    ("yaluy", "demonstrative", None, "NA", "here", "batch-389: Yalanji local support only."),
    ("yinyay", "demonstrative", None, "NA", "there", "batch-389: Yalanji local support only."),
    ("nyajin", "verb-tr", "l", "NA", "see; see.PST", "batch-389: 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] or db.execute("SELECT COUNT(*) FROM lessons WHERE batch_id=?", (batch_id,)).fetchone()[0] or db.execute("SELECT COUNT(*) FROM lexeme_log WHERE action='batch-389-verify'").fetchone()[0]:
        raise SystemExit("close artifacts 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-389-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 morphology, conservative bird labels, syntax, fidelity and register retained."),
        ("review", "review-detail", "Taxonomy/card-domain/whole-head/cultural/property/habitat/place/source/case/event and support drift remained closed."),
        ("review", "lesson-inventory", f"Inserted {len(LESSONS)} Rule 347 lessons."),
        ("review", "lexeme-source-log", f"Inserted {len(LEXEMES)} Rule 347 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 347 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-389-verify'").fetchone()[0]
    record_event(db, batch_label=LABEL, phase="close", event_type="close-success", actor="codex", detail=f"Closed Rule 347: 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_389.py", artifact_path=PROCESS_LOG, created_at=NOW)
    db.commit()
    print(f"closed batch-389: 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())
