#!/usr/bin/env python3
"""Close batch-272 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-272"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-272-second-low-coverage-named-tree-reference-lexical-expansion-after-rule229-process.md"
LENSES = ("morphology", "lexicon", "syntax", "fidelity", "register")

NOTES = (
    "SECOND LOW-COVERAGE NAMED TREE-REFERENCE LEXICAL EXPANSION AFTER RULE 229 (Rule 230). 50 rows. TARGETS: "
    "bingkajiri bingkajiri tree, durral durral tree, kalbariji kalbariji tree, kalkuri kalkuri tree, "
    "kuji-kuji kuji-kuji tree, kuralbi kuralbi tree, kuray-kuray kuray-kuray tree, mingur mingur tree, "
    "mukukana mukukana tree, and mungka-mungka mungka-mungka tree. SUPPORT: uninflected yinya/yaluy/yinyay. "
    "FRAMES: TARGET yaluy, TARGET yinyay, Yinya TARGET yinyay, TARGET yinya yinyay, and Yinya TARGET yaluy. "
    "REVIEW: all target nouns stayed bare named-tree labels. Insect and shellfish fields were rejected for insufficient "
    "clean unused candidate depth. No earlier flora/tree exact-target repeat, no no-definition target, no fruit/food/seed "
    "row, no medicine/poison/blinding row, no bark/wood/spear/rope/material row, no thorn/branch row, no habitat/ecology/"
    "place/story row, no target case, no productive compounding, no productive reduplication, and no support inflection "
    "was opened."
)

LESSONS = [
    ("process", "Rule 230 did not force the insect field after source audit showed too few clean unused candidates beyond Rules 169, 212, and 222.", "Rule 230: insect field rejected."),
    ("process", "Rule 230 did not force a third shellfish/crustacean pass after Rules 171, 213, and 223 left fewer than ten clean unused heads once jirrum and risk-heavy entries were excluded.", "Rule 230: shellfish field rejected."),
    ("source", "Patz section 3.1 supports nouns referring to flora; this licenses named-tree heads as nominal referents, not tree-event prose.", "Rule 230: flora noun source."),
    ("source", "Patz section 3.7 supports yinya, yaluy, and yinyay as demonstrative/local support and allows demonstratives to co-occur with nouns.", "Rule 230: deictic support."),
    ("source", "Rules 199, 208, and 224 already establish bare named-tree reference frames; Rule 230 reuses only that narrow law.", "Rule 230: construction source."),
    ("process", "Rejected or deferred named-tree candidates included earlier flora/tree exact targets, no-definition entries, high-count homographs, and source-prose-heavy fruit, food, medicine, poison, bark, material, habitat, place, or story entries.", "Rule 230: candidate filtering."),
    ("lexicon", "Bingkajiri stayed bingkajiri-tree reference only; no long-seed, eating, or blinding story was opened.", "Rule 230: bingkajiri."),
    ("lexicon", "Durral stayed durral-tree reference only; no edible-fruit, spear, or wood-source row was opened.", "Rule 230: durral."),
    ("lexicon", "Kalbariji stayed kalbariji-tree reference only; finger-cherry source wording did not license fruit or food prose.", "Rule 230: kalbariji."),
    ("lexicon", "Kalkuri stayed kalkuri-tree reference only; woollybutt/Melaleuca source wording did not license gum, timber, or material prose.", "Rule 230: kalkuri."),
    ("lexicon", "Kuji-kuji stayed kuji-kuji-tree reference only; the hyphenated lexical reduplication was treated as a whole dictionary head.", "Rule 230: kuji-kuji."),
    ("lexicon", "Kuralbi stayed kuralbi-tree reference only despite two older kuralbi bark-source rows; bark, rope, and material-source readings stayed closed.", "Rule 230: kuralbi."),
    ("lexicon", "Kuray-kuray stayed kuray-kuray-tree reference only; the hyphenated lexical reduplication was treated as a whole dictionary head.", "Rule 230: kuray-kuray."),
    ("lexicon", "Mingur stayed mingur-tree reference only; oilnut, fruit, roasting, and cosmetic source prose stayed closed.", "Rule 230: mingur."),
    ("lexicon", "Mukukana stayed mukukana-tree reference only; edible-fruit source prose stayed closed.", "Rule 230: mukukana."),
    ("lexicon", "Mungka-mungka stayed mungka-mungka-tree reference only; thorn and branch source prose stayed closed.", "Rule 230: mungka-mungka."),
    ("morphology", "All target named-tree nouns stayed bare: no target case, possession, comitative, instrumental, locative, plural, derivational, or suffixal morphology.", "Rule 230: bare tree targets."),
    ("morphology", "Hyphenated lexical reduplications kuji-kuji, kuray-kuray, and mungka-mungka stayed whole dictionary headwords, not productive reduplication.", "Rule 230: lexical reduplication."),
    ("morphology", "Yinya, yaluy, and yinyay stayed uninflected support heads.", "Rule 230: support morphology."),
    ("syntax", "Rows 1-10 used TARGET yaluy.", "Rule 230: here frame."),
    ("syntax", "Rows 11-20 used TARGET yinyay.", "Rule 230: there frame."),
    ("syntax", "Rows 21-30 used Yinya TARGET yinyay.", "Rule 230: preposed demonstrative frame."),
    ("syntax", "Rows 31-40 used TARGET yinya yinyay.", "Rule 230: postposed demonstrative frame."),
    ("syntax", "Rows 41-50 used Yinya TARGET yaluy.", "Rule 230: that-here frame."),
    ("fidelity", "English stayed literal reference prose: selected named-tree label plus here/there/that.", "Rule 230: literal English."),
    ("fidelity", "The batch did not turn dictionary tree prose into fruit, food, medicine, poison, bark, wood, spear, rope, material, thorn, branch, habitat, place, story, or use claims.", "Rule 230: source-prose restraint."),
    ("register", "Rows remained plain elicitation-style reference statements, not ecological description, resource-use instruction, food processing, medicine talk, story prose, or material-culture narrative.", "Rule 230: register."),
    ("process", "Duplicate preflight, static guards, whole-corpus lint, five-lens review, lessons, lexeme logs, export, and live docs are required before treating Rule 230 as complete.", "Rule 230: evidence standard."),
    ("handoff", "Next batch may continue from 13,697 if this closes; do not infer named-tree fruit, food, medicine, material, habitat, story, place, or use grammar from Rule 230.", "Rule 230: next cadence."),
]

LEXEMES = [
    ("bingkajiri", "noun", None, "NA", "bingkajiri tree", "batch-272: bingkajiri-tree reference only."),
    ("durral", "noun", None, "NA", "durral tree", "batch-272: durral-tree reference only; no fruit/spear/wood row."),
    ("kalbariji", "noun", None, "NA", "kalbariji tree", "batch-272: kalbariji-tree reference only; no finger-cherry food row."),
    ("kalkuri", "noun", None, "NA", "kalkuri tree", "batch-272: kalkuri-tree reference only; no gum/timber/material row."),
    ("kuji-kuji", "noun", None, "NA", "kuji-kuji tree", "batch-272: whole hyphenated lexical tree head."),
    ("kuralbi", "noun", None, "NA", "kuralbi tree", "batch-272: kuralbi-tree reference only; no bark/rope/material row."),
    ("kuray-kuray", "noun", None, "NA", "kuray-kuray tree", "batch-272: whole hyphenated lexical tree head."),
    ("mingur", "noun", None, "NA", "mingur tree", "batch-272: mingur-tree reference only; no oilnut/roasting/cosmetic row."),
    ("mukukana", "noun", None, "NA", "mukukana tree", "batch-272: mukukana-tree reference only; no edible-fruit row."),
    ("mungka-mungka", "noun", None, "NA", "mungka-mungka tree", "batch-272: whole hyphenated lexical tree head; no thorn/branch row."),
    ("yinya", "demonstrative", None, "NA", "that; those", "batch-272: demonstrative support only."),
    ("yaluy", "demonstrative", None, "NA", "here", "batch-272: local demonstrative support only."),
    ("yinyay", "demonstrative", None, "NA", "there", "batch-272: local demonstrative 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.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-272-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 230 rows. Morphology checked bare named-tree nouns, lexicalized hyphenated reduplications, and uninflected support. Lexicon checked selected named-tree labels only. Syntax checked five deictic frame bands. Fidelity checked literal reference English. Register stayed plain reference statement.",
        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 insect or shellfish forced field, no earlier flora/tree exact-target repeat, no no-definition target, no fruit/food/seed row, no medicine/poison/blinding row, no bark/wood/spear/rope/material row, no thorn/branch row, no habitat/ecology/place/story row, no target case, no productive compound, no productive reduplication, 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 230 lessons covering field audit, source checks, candidate rejection, target senses, morphology, syntax, fidelity, register, and handoff.",
        artifact_path="batch_scripts/close_272.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 230 targets and support heads.",
        artifact_path="batch_scripts/close_272.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 272: 50 accepted Rule 230 second low-coverage named tree-reference rows, five-lens reviews, lessons, lexeme/source logs, and process evidence stored.",
        command="python3 batch_scripts/close_272.py",
        artifact_path="batch_scripts/close_272.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]
    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}, verified {verified_before}->{verified_after}, "
        f"kuku_words {words_before}->{words_after}"
    )
    return 0


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