#!/usr/bin/env python3
"""Close batch-275 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-275"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-275-landform-geography-construction-consolidation-after-rule232-process.md"
LENSES = ("morphology", "lexicon", "syntax", "fidelity", "register")

NOTES = (
    "LANDFORM/GEOGRAPHY CONSTRUCTION CONSOLIDATION AFTER RULE 232 (Rule 233). 50 rows. "
    "TARGETS retained from Rule 232: mabil swamp, karrirrangkal coral, jalngarr flat rock, jinali smooth rock, "
    "kangku valley, yalmba seashore, kambal-kambal mud, jalkar high bank, janku hole, and jamba hillside. SUPPORT: "
    "postposed yinya with yaluy, bare nominative yundu/nyulu/ngana/jana, nonpast nyajil, past nyajin, and local "
    "demonstratives yaluy/yinyay. FRAMES: TARGET yinya yaluy, Yundu TARGET yinyay nyajil, Nyulu TARGET yaluy nyajil, "
    "Ngana TARGET yinyay nyajil, and Jana TARGET yaluy nyajin. REVIEW: all target nouns stayed bare reference labels. "
    "Rule 185-186 targets and rejected near candidates stayed closed. No place-name, route/travel/crossing, "
    "river/water-use, burning/clearing, live-ant/insect behavior, ecology, reef/sea/collecting, climbing/falling/"
    "danger, ownership/dispute/legal/social, finding/peeping/causative, rubbish/debris/leaf-mould/growing-use, "
    "target case, target compound, productive reduplication, support inflection, 1sg reuse, or Rule 232 source-prose "
    "drift was opened."
)

LESSONS = [
    ("source", "Rule 233 carries forward only the ten dictionary-checked Rule 232 landform/geography nouns; no new content noun was admitted.", "Rule 233: retained target source."),
    ("source", "Patz section 3.5 personal pronoun paradigm sources yundu, nyulu, ngana, and jana as nominative pronouns.", "Rule 233: pronoun source."),
    ("source", "Patz section 3.7 sources yinya 'that/those', yaluy 'here', and yinyay 'there'; Rule 233 keeps them bare.", "Rule 233: demonstrative source."),
    ("source", "Patz text evidence supports nyaji-l as nonpast 'see'; local corpus convention supports nyajin as the past perception surface.", "Rule 233: perception source."),
    ("process", "Exact duplicate preflight over all 50 planned Rule 233 surfaces returned no accepted, revised, or under-review collisions before banking.", "Rule 233: duplicate preflight."),
    ("lexicon", "Mabil remains swamp reference only; no crossing, route, water-use, habitat, or event row was opened.", "Rule 233: mabil."),
    ("lexicon", "Karrirrangkal remains coral reference only; lime, reef/sea, collecting, habitat, and material-use prose remain closed.", "Rule 233: karrirrangkal."),
    ("lexicon", "Jalngarr remains flat-rock reference only; sitting, creek, water, climbing, and route readings remain closed.", "Rule 233: jalngarr."),
    ("lexicon", "Jinali remains smooth-rock reference only; smooth-property predication and smoothing-use readings remain closed.", "Rule 233: jinali."),
    ("lexicon", "Kangku remains valley reference only; mountain-pass route/travel and cheek/body-part homograph drift remain closed.", "Rule 233: kangku."),
    ("lexicon", "Yalmba remains seashore reference only; sea, tide, beach-collecting, turtle, food, and warning/event rows remain closed.", "Rule 233: yalmba."),
    ("lexicon", "Kambal-kambal remains mud reference only; crocodile, crab, turtle, nest, muddy-water, and event rows remain closed.", "Rule 233: kambal-kambal."),
    ("lexicon", "Jalkar remains high-bank reference only; jalkar-jalkar steep-property rows and river/route/danger readings remain closed.", "Rule 233: jalkar."),
    ("lexicon", "Janku remains hole reference only; waterhole, animal burrow, nest, digging, ground-oven, and body/event readings remain closed.", "Rule 233: janku."),
    ("lexicon", "Jamba remains hillside reference only; root/under-tree drift, burning, social metaphor, and event readings remain closed.", "Rule 233: jamba."),
    ("morphology", "All Rule 233 target nouns stayed bare: no target LOC, DAT, ABL, PERL, ERG, INST, COMIT, POSS, PRIV, EXCESS, plural, derivation, or suffixing.", "Rule 233: bare targets."),
    ("morphology", "Kambal-kambal stayed a whole lexical reduplicated noun; no productive reduplication was opened.", "Rule 233: lexical reduplication."),
    ("morphology", "Jalkar stayed a noun 'high bank'; the related jalkar-jalkar steep-property adjective was not imported.", "Rule 233: jalkar contrast."),
    ("morphology", "Yinya, yaluy, yinyay, yundu, nyulu, ngana, jana, nyajil, and nyajin stayed uninflected support heads.", "Rule 233: support morphology."),
    ("morphology", "The Rule 233 perception rows use bare nominative pronouns before an absolutive object noun; no common-noun transitive A ergative was introduced.", "Rule 233: pronoun A morphology."),
    ("syntax", "Rows 1-10 used TARGET yinya yaluy, extending the sourced demonstrative co-occurrence pattern without opening target morphology.", "Rule 233: postposed that-here frame."),
    ("syntax", "Rows 11-20 used Yundu TARGET yinyay nyajil.", "Rule 233: 2sg nonpast there frame."),
    ("syntax", "Rows 21-30 used Nyulu TARGET yaluy nyajil.", "Rule 233: 3sg nonpast here frame."),
    ("syntax", "Rows 31-40 used Ngana TARGET yinyay nyajil.", "Rule 233: 1pl inclusive nonpast there frame."),
    ("syntax", "Rows 41-50 used Jana TARGET yaluy nyajin.", "Rule 233: 3pl past here frame."),
    ("fidelity", "English stayed literal reference/perception prose: selected landform label plus here/there/that or see/saw.", "Rule 233: literal English."),
    ("fidelity", "The batch did not convert dictionary definitions into route, water-use, ecology, place-name, source-material, danger, ownership, legal/social, or event claims.", "Rule 233: source-prose restraint."),
    ("register", "Rows remained plain elicitation-style reference/perception statements, not travel instruction, ecological description, story prose, law/dispute talk, or resource-use narrative.", "Rule 233: register."),
    ("process", "Duplicate preflight, static guards, whole-corpus lint, five-lens review, lessons, lexeme logs, export, source-book update, render, and live docs are required before treating Rule 233 as complete.", "Rule 233: evidence standard."),
    ("handoff", "Next batch may continue from 13,847 if this closes; do not infer landform case, event, route, water-use, ecology, source, ownership, or danger grammar from Rule 233.", "Rule 233: next cadence."),
]

LEXEMES = [
    ("mabil", "noun", None, "NA", "swamp", "batch-275: swamp reference only; no crossing, route, water-use, or habitat row."),
    ("karrirrangkal", "noun", None, "NA", "coral", "batch-275: coral reference only; no lime, reef/sea, collecting, or material-use row."),
    ("jalngarr", "noun", None, "NA", "flat rock", "batch-275: flat-rock reference only."),
    ("jinali", "noun", None, "NA", "smooth rock", "batch-275: smooth-rock reference only; no smooth-property predication."),
    ("kangku", "noun", None, "NA", "valley", "batch-275: valley reference only; no route/pass event row."),
    ("yalmba", "noun", None, "NA", "seashore", "batch-275: seashore reference only; no sea/tide/collecting row."),
    ("kambal-kambal", "noun", None, "NA", "mud", "batch-275: mud reference only; lexical reduplication as whole headword."),
    ("jalkar", "noun", None, "NA", "high bank", "batch-275: high-bank reference only; jalkar-jalkar steep adjective remains closed."),
    ("janku", "noun", None, "NA", "hole", "batch-275: hole reference only; no waterhole, burrow, nest, or digging row."),
    ("jamba", "noun", None, "NA", "hillside", "batch-275: hillside reference only."),
    ("yinya", "demonstrative", None, "NA", "that; those", "batch-275: demonstrative support only."),
    ("yaluy", "demonstrative", None, "NA", "here", "batch-275: local demonstrative support only."),
    ("yinyay", "demonstrative", None, "NA", "there", "batch-275: local demonstrative support only."),
    ("yundu", "personal-pronoun", None, "NA", "you singular", "batch-275: bare 2sg nominative support."),
    ("nyulu", "personal-pronoun", None, "NA", "third singular", "batch-275: bare 3sg nominative support."),
    ("ngana", "personal-pronoun", None, "NA", "we inclusive", "batch-275: bare 1pl inclusive nominative support."),
    ("jana", "personal-pronoun", None, "NA", "they", "batch-275: bare 3pl nominative support."),
    ("nyajil", "verb", "l", None, "perceive nonpast", "batch-275: nonpast nyaji-l perception predicate."),
    ("nyajin", "verb", "l", None, "perceive past", "batch-275: past nyaji-l perception predicate following established corpus surface."),
]


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-275-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 233 rows. Morphology checked bare target nouns, lexical kambal-kambal, uninflected support heads, bare nominative pronoun A, and no target case. Lexicon checked selected landform labels only. Syntax checked one postposed demonstrative frame band, three nonpast perception bands, and one past perception band. 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 185-186 target repeat, no rejected landform candidate, no place-name row, no route/travel/crossing row, no water-use row, no burning/clearing row, no live-ant or insect behavior row, no ecology/habitat row, no reef/sea/collecting row, no climbing/falling/danger row, no ownership/dispute/legal/social row, no finding/peeping/causative row, no rubbish/debris/leaf-mould/growing-use row, no target case, no productive compound, no productive reduplication, no support inflection, no 1sg drift, and no Rule 232 source-prose expansion.",
        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 233 lessons covering source checks, duplicate preflight, lexical target senses, morphology, syntax, fidelity, register, and handoff.",
        artifact_path="batch_scripts/close_275.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 233 targets and support heads.",
        artifact_path="batch_scripts/close_275.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 275: 50 accepted Rule 233 landform/geography construction-consolidation rows, five-lens reviews, lessons, lexeme/source logs, and process evidence stored.",
        command="python3 batch_scripts/close_275.py",
        artifact_path="batch_scripts/close_275.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())
