#!/usr/bin/env python3
"""Close batch-294 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-294"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-294-reptile-amphibian-perception-debt-sweep-after-rule251-process.md"
LENSES = ("morphology", "lexicon", "syntax", "fidelity", "register")

NOTES = (
    "REPTILE/AMPHIBIAN PERCEPTION DEBT SWEEP AFTER RULE 251 (Rule 252). 50 rows. TARGETS: bajuy lizard, "
    "bilngkumu crocodile, binanjal frill necked lizard, bindimu black snake, bubal tree snake, jijirrbay whip "
    "snake, kabul rock python, kalajarr scrub goanna, kundurr death adder, and yuday frog. SUPPORT: bare "
    "nominative ngayu, nyulu, yurra, bula, and jana plus yaluy/yinyay and nyajil. FRAMES: Ngayu TARGET yinyay "
    "nyajil, Nyulu TARGET yaluy nyajil, Yurra TARGET yinyay nyajil, Bula TARGET yaluy nyajil, and Jana TARGET "
    "yinyay nyajil. REVIEW: no Rule 175-176 exact-surface duplicate, biwul black-snake/mother-in-law homograph, "
    "story-site/myth row, poison/biting/danger row, hunting/catching/trapping/killing row, food/edible/cooking row, "
    "egg/skin/scale/body-part row, habitat/water/river/sea/swamp row, tree-climbing/firestick/fighter/event row, "
    "colour predicate, target case, target compound, productive reduplication, pronoun case, demonstrative case, "
    "perception compound, support inflection, mammal-reference drift, fish-reference drift, or recent bird/dwelling/"
    "landform/body/fire drift was opened."
)

LESSONS = [
    ("source", "Rule 252 selected ten checked Rule 220 reptile/amphibian noun heads: bajuy, bilngkumu, binanjal, bindimu, bubal, jijirrbay, kabul, kalajarr, kundurr, and yuday.", "Rule 252: target inventory."),
    ("source", "Dictionary line readback was explicit: bajuy 559-567, bilngkumu 2575-2585, binanjal 2726-2734, bindimu 2884-2901, bubal 3397-3417, jijirrbay 8955-8972, kabul 11816-11833, kalajarr 12243-12251, kundurr 15704-15720, and yuday 30662-30670.", "Rule 252: dictionary source lines."),
    ("source", "Patz grammar lines 2553-2603 supplied the bare nominative pronoun forms used as subject support in this batch.", "Rule 252: pronoun source."),
    ("source", "Patz Table 3.12 lines 2838-2845 supplied Yalanji yaluy 'here' and yinyay 'there'; Rule 252 used only the bare local demonstratives.", "Rule 252: demonstrative source."),
    ("source", "Patz section 3.8.3.2 lines 3043-3049 supplied nyaji-l 'see'; Rule 252 used only the nonpast form nyajil and did not build perception compounds or purposive rows.", "Rule 252: perception source."),
    ("process", "Hard exclusions retained: exact Rule 175-176 reptile/amphibian surfaces, biwul black-snake/mother-in-law homograph, story-site/myth-heavy entries, poison/biting/food/event-heavy entries, water/habitat-heavy entries, and productive-reduplication pressure.", "Rule 252: hard exclusions."),
    ("process", "Exact duplicate preflight over all 50 planned Rule 252 Kuku surfaces returned no accepted, revised, or under-review collisions.", "Rule 252: duplicate preflight."),
    ("process", "Token-aware target coverage before banking after Rule 251 was bajuy 13, bilngkumu 73, binanjal 9, bindimu 11, bubal 7, jijirrbay 7, kabul 23, kalajarr 9, kundurr 9, and yuday 17.", "Rule 252: target coverage audit."),
    ("process", "Support coverage before banking after Rule 251 was ngayu 1246, nyulu 1767, yurra 180, bula 184, jana 2089, yaluy 2167, yinyay 2430, and nyajil 2470.", "Rule 252: support coverage audit."),
    ("lexicon", "Bajuy was admitted as lizard only; slippery, catching, and food readings stayed closed.", "Rule 252: bajuy."),
    ("lexicon", "Bilngkumu was admitted as crocodile only; saltwater, river, fear, biting, danger, hunting, and story readings stayed closed.", "Rule 252: bilngkumu."),
    ("lexicon", "Binanjal was admitted as frill-necked-lizard only; neck/body-part, display, frightening, and event readings stayed closed.", "Rule 252: binanjal."),
    ("lexicon", "Bindimu was admitted as black snake only; red-bellied alternate and colour-predicate readings stayed closed.", "Rule 252: bindimu."),
    ("lexicon", "Bubal was admitted as tree snake only; tree/habitat, biting, and death-event readings stayed closed.", "Rule 252: bubal."),
    ("lexicon", "Jijirrbay was admitted as whip snake only; whip/object, speed, and bite prose stayed closed.", "Rule 252: jijirrbay."),
    ("lexicon", "Kabul was admitted as rock python only; rock/place, food, squeezing, swallowing, and hunting prose stayed closed.", "Rule 252: kabul."),
    ("lexicon", "Kalajarr was admitted as scrub goanna only; scrub/habitat, climbing, nest-raiding, food, cooking, and digging prose stayed closed.", "Rule 252: kalajarr."),
    ("lexicon", "Kundurr was admitted as death adder only; danger, biting, poison, fear, and sickness prose stayed closed.", "Rule 252: kundurr."),
    ("lexicon", "Yuday was admitted as frog only; large, edible, rain, singing, swamp, and metamorphosis prose stayed closed.", "Rule 252: yuday."),
    ("morphology", "All Rule 252 target nouns stayed bare: no LOC, DAT, ABL, PERL, ERG, INST, COMIT, POSS, PRIV, EXCESS, plural, derivation, or target suffix was admitted.", "Rule 252: bare targets."),
    ("morphology", "English modifiers inside animal labels, such as frill necked, black, tree, whip, rock, scrub, and death, remained lexical labels rather than Kuku morphology or separate predicates.", "Rule 252: label morphology guard."),
    ("morphology", "The subject support forms stayed bare nominatives: no pronoun case forms, possessive readings, or person/number extensions were introduced.", "Rule 252: bare nominative pronouns."),
    ("morphology", "Yaluy, yinyay, and nyajil stayed uninflected support heads; no demonstrative case, local-derived form, verb nominalization, purposive, or support suffix was admitted.", "Rule 252: uninflected support."),
    ("morphology", "No target compound, lexicalized trap compound, or productive reduplication was built from the reptile/amphibian heads.", "Rule 252: no compounds or reduplication."),
    ("syntax", "Rows 1-10 used Ngayu TARGET yinyay nyajil.", "Rule 252: 1sg there perception band."),
    ("syntax", "Rows 11-20 used Nyulu TARGET yaluy nyajil.", "Rule 252: 3sg here perception band."),
    ("syntax", "Rows 21-30 used Yurra TARGET yinyay nyajil.", "Rule 252: 2pl there perception band."),
    ("syntax", "Rows 31-40 used Bula TARGET yaluy nyajil.", "Rule 252: 3du here perception band."),
    ("syntax", "Rows 41-50 used Jana TARGET yinyay nyajil.", "Rule 252: 3pl there perception band."),
    ("fidelity", "English stayed literal perception/reference prose: subject pronoun, selected reptile/amphibian label, here/there, and see only.", "Rule 252: literal English."),
    ("fidelity", "The batch did not convert dictionary examples or source prose into hunting, catching, trapping, killing, biting, poison, danger, food, cooking, egg, skin, scale, habitat, water, river, sea, swamp, story, myth, body-part, colour, or event claims.", "Rule 252: source-prose restraint."),
    ("register", "Rows remained plain elicitation-style perception clauses; no warning, hunting report, food statement, habitat claim, or mythological register was introduced.", "Rule 252: register."),
    ("process", "Rules 149-251 carry-forward traps stayed closed, especially reptile-use/action traps, mammal-use/action traps, fish-use/action traps, bird event/use/lore traps, dwelling action/use/source traps, fire action/use/homograph traps, landform route/water/event drift, and body-part possession/body-state drift.", "Rule 252: carry-forward closure."),
    ("handoff", "Next batch may continue from 14,797 if this closes; do not infer reptile behavior, danger, food, habitat, story, body-part, homograph, colour-predicate, target-case, pronoun-case, demonstrative-case, perception-compound, or support-inflection grammar from Rule 252.", "Rule 252: next cadence."),
]

LEXEMES = [
    ("bajuy", "noun", None, "NA", "lizard", "batch-294: lizard perception/reference only; slippery/catching/food readings closed."),
    ("bilngkumu", "noun", None, "NA", "crocodile", "batch-294: crocodile perception/reference only; saltwater/river/fear/biting/danger/hunting/story readings closed."),
    ("binanjal", "noun", None, "NA", "frill necked lizard", "batch-294: frill-necked-lizard perception/reference only; neck/body-part/display/frightening/event readings closed."),
    ("bindimu", "noun", None, "NA", "black snake", "batch-294: black-snake perception/reference only; red-bellied alternate and colour-predicate readings closed."),
    ("bubal", "noun", None, "NA", "tree snake", "batch-294: tree-snake perception/reference only; tree/habitat/biting/death-event readings closed."),
    ("jijirrbay", "noun", None, "NA", "whip snake", "batch-294: whip-snake perception/reference only; whip/object/speed/bite prose closed."),
    ("kabul", "noun", None, "NA", "rock python", "batch-294: rock-python perception/reference only; rock/place/food/squeezing/swallowing/hunting prose closed."),
    ("kalajarr", "noun", None, "NA", "scrub goanna", "batch-294: scrub-goanna perception/reference only; scrub/habitat/climbing/nest-raiding/food/cooking/digging prose closed."),
    ("kundurr", "noun", None, "NA", "death adder", "batch-294: death-adder perception/reference only; danger/biting/poison/fear/sickness prose closed."),
    ("yuday", "noun", None, "NA", "frog", "batch-294: frog perception/reference only; large/edible/rain/singing/swamp/metamorphosis prose closed."),
    ("ngayu", "pronoun", None, "NA", "first singular nominative", "batch-294: bare nominative subject support only."),
    ("nyulu", "pronoun", None, "NA", "third singular nominative", "batch-294: bare nominative subject support only."),
    ("yurra", "pronoun", None, "NA", "second plural nominative", "batch-294: bare nominative subject support only."),
    ("bula", "pronoun", None, "NA", "third dual nominative", "batch-294: bare nominative subject support only."),
    ("jana", "pronoun", None, "NA", "third plural nominative", "batch-294: bare nominative subject support only."),
    ("yaluy", "demonstrative", None, "NA", "here", "batch-294: bare local demonstrative support only."),
    ("yinyay", "demonstrative", None, "NA", "there", "batch-294: bare local demonstrative support only."),
    ("nyajil", "verb", "l", "NA", "see", "batch-294: nonpast perception support only."),
]


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")
    batch_id = 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=?
        """,
        (batch_id,),
    ).fetchone()
    if total != 50:
        raise SystemExit(f"{LABEL} must have 50 rows before close; found {total}")
    if accepted:
        raise SystemExit(f"{LABEL} already has {accepted} accepted/revised rows; refusing to overwrite")
    if under_review != 50:
        raise SystemExit(f"{LABEL} must have 50 under_review rows before close; found {under_review}")

    total_rows = db.execute("SELECT COUNT(*) FROM sentences WHERE status!='rejected'").fetchone()[0]
    verified_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("DELETE FROM reviews WHERE sentence_id=?", [(sid,) for sid in sentence_ids])
    db.execute("DELETE FROM lessons WHERE batch_id=?", (batch_id,))
    db.executemany(
        "INSERT INTO reviews(sentence_id, round, lens, verdict, issues, suggested_fix, reviewer, created_at) VALUES(?,?,?,?,?,?,?,?)",
        [
            (sid, 1, lens, "pass", None, None, "claude-main-five-lens", NOW)
            for sid 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, conj_class, 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,
                conj_class,
                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-294-verify", note, NOW),
        )

    record_event(
        db,
        batch_label=LABEL,
        phase="lint",
        event_type="under-review-lint-green",
        actor="claude-main",
        detail=f"After banking, lint scanned {total_rows} rows and found 0 issues; verified total remained {verified_before} before close.",
        command="python3 lint_corpus.py",
        output_excerpt=f"lint: {total_rows} rows scanned, 0 issues flagged; GOAL METRIC: {verified_before} / 20000 verified sentences ({words_before or 0} Kuku words)",
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="review",
        event_type="five-lens-pass",
        actor="claude-main",
        detail="Manual five-lens review passed all 50 Rule 252 rows. Morphology checked bare reptile/amphibian nouns, bare nominative pronouns, bare yaluy/yinyay, and nyajil nonpast only. Lexicon checked selected animal labels only. Syntax checked five pronoun-perception frame bands. Fidelity checked literal reference/perception English. Register stayed plain elicitation-style.",
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="review",
        event_type="review-detail",
        actor="claude-main",
        detail="Final review retained no Rule 175-176 exact surface duplicate, no biwul black-snake/mother-in-law homograph, no story-site/myth row, no poison/biting/danger row, no hunting/catching/trapping/killing row, no food/edible/cooking row, no egg/skin/scale/body-part row, no habitat/water/river/sea/swamp row, no tree-climbing/firestick/fighter/event row, no colour predicate, no target case, no productive compound, no productive reduplication, no pronoun case, no demonstrative case, no perception compound, and no support inflection.",
        artifact_path=PROCESS_LOG,
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="review",
        event_type="lesson-inventory",
        actor="claude-main",
        detail=f"Inserted {len(LESSONS)} Rule 252 lessons covering source checks, lexical target senses, hard exclusions, morphology, syntax, fidelity, register, process traps, and handoff.",
        artifact_path="batch_scripts/close_294.py",
        created_at=NOW,
    )
    record_event(
        db,
        batch_label=LABEL,
        phase="review",
        event_type="lexeme-source-log",
        actor="claude-main",
        detail=f"Inserted {len(LEXEMES)} lexeme/source-log rows for Rule 252 targets and support heads.",
        artifact_path="batch_scripts/close_294.py",
        created_at=NOW,
    )
    db.execute("UPDATE batches SET notes=? WHERE id=?", (NOTES, batch_id))
    db.execute("UPDATE sentences SET status='accepted', updated_at=? WHERE batch_id=?", (NOW, batch_id))
    record_event(
        db,
        batch_label=LABEL,
        phase="close",
        event_type="close-success",
        actor="claude-main",
        detail="Closed Batch 294: 50 accepted Rule 252 reptile/amphibian perception rows, five-lens reviews, lessons, lexeme/source logs, and process evidence stored.",
        command="python3 batch_scripts/close_294.py",
        artifact_path="batch_scripts/close_294.py",
        created_at=NOW,
    )
    db.commit()

    reviews = db.execute(
        "SELECT COUNT(*) FROM reviews WHERE sentence_id IN (SELECT id FROM sentences WHERE batch_id=?)",
        (batch_id,),
    ).fetchone()[0]
    lessons = db.execute("SELECT COUNT(*) FROM lessons WHERE batch_id=?", (batch_id,)).fetchone()[0]
    lexeme_logs = db.execute(
        "SELECT COUNT(*) FROM lexeme_log WHERE action='batch-294-verify'"
    ).fetchone()[0]
    verified_after, words_after = db.execute(
        """
        SELECT
          COUNT(*),
          SUM(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1)
        FROM sentences
        WHERE status IN ('accepted','revised')
        """
    ).fetchone()
    print(
        f"closed {LABEL}: accepted=50, reviews={reviews}, lessons={lessons}, "
        f"lexeme_logs={lexeme_logs}, verified {verified_before}->{verified_after}, "
        f"kuku_words {words_before}->{words_after}"
    )
    return 0


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