#!/usr/bin/env python3
"""Close batch-130 after main-loop five-lens review."""

from __future__ import annotations

import datetime as dt
import json
import sqlite3
import sys

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-130"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")

LENSES = ("morphology", "lexicon", "syntax", "fidelity", "register")

NOTES = (
    "CONSTRUCTION: NUMBER AND QUANTIFICATION (Patz §3.2.3.5 number, "
    "§3.2.5 morphology of quantifiers, §3.5 personal pronoun number, "
    "§3.6.1 interrogatives, §4.1 noun-phrase structure, §4.5 possession, "
    "§4.6 comitative possession, §4.8 questions, text 36 and text 51). "
    "50 rows, avg Kuku words/row recorded at banking. DESIGN: high-control "
    "construction batch, mostly direct Patz examples or minimal variants of "
    "explicit Patz frames. CORE CONTRASTS: a bare noun is normally singular; "
    "plurality is expressed by quantifiers, reduplication, kin plural, or "
    "specific pair morphology. `jambul` simply means two; `-bulal` marks an "
    "intrinsic pair whose members belong/act together. `bula` is the 3du "
    "pronoun and can co-occur with a dual/pair NP; `yubal` is the 2du "
    "pronoun. BASIC NUMERALS: `nyubun` one, `jambul` two, `jambul-jambul` "
    "four, `kulur` a few/three. KULUR NOTE: Patz explicitly says `kulur` can "
    "mean an unspecified small number larger than two and smaller than "
    "`kanbal`; the text-36 row is therefore glossed 'some/few' rather than "
    "hard 'three'. QUANTIFIER CONTRASTS: `kanbal` several/some, `buban` too "
    "few/insufficient, `wubul` many identifiable items, `jirray` mass/plenty "
    "or size/large crowd. `narmba-ku` and `wubul-ku` cover all/every where "
    "attested; `yambayamba` is everything; `yamba-kari`/text surface "
    "`yambakari` is nothing/none. HALF/PARTITIVE: Patz's quantifier table "
    "lists `kankur` half, but text 51 uses `kanku`; the batch uses only the "
    "narrative-attested `kanku` rows and logs the source tension. QUESTIONS: "
    "`wanjarrinya` is the how-many interrogative built on the `wanja` family. "
    "PROCESS RECORD: the batch has an external process dossier under "
    "synthetic process_logs, plus DB notes, 250 five-lens review rows, "
    "lessons, and lexeme_log entries. Cross-corpus duplicate check was run "
    "before banking. Full corpus lint passed before close with 6597 rows "
    "scanned and 0 issues."
)

LESSONS = [
    (
        "morphology",
        "`jambul` and `-bulal` are not interchangeable: `jambul` counts two referents; `-bulal` derives a pair whose members belong or act together.",
        "cheatsheet Rule 88: jambul vs -bulal pair distinction.",
    ),
    (
        "morphology",
        "The pair suffix can occur with human and nonhuman nouns, and a pair NP may also include `jambul` and/or dual pronoun `bula`.",
        "cheatsheet Rule 88: pair morphology examples from Patz ex.65-67 and text 51.",
    ),
    (
        "syntax",
        "Quantifier placement is flexible in the source examples: `jarba jambul`, `jambul juku`, and `jambul kaya kuli-ji` are all attested patterns, with adjective+quantifier NPs tending to put the quantifier before the head.",
        "cheatsheet Rule 88: number/quantifier NP-order note.",
    ),
    (
        "semantics",
        "`kulur` should be treated as 'a few/three'; in text 36 it translates 'some', so do not force arithmetic three where the discourse only means a small quantity.",
        "errata Batch-130: kulur small-number caution.",
    ),
    (
        "semantics",
        "`kanbal` marks some/several; `buban` marks too few or insufficient. They are not stylistic variants.",
        "cheatsheet Rule 88: kanbal vs buban contrast.",
    ),
    (
        "semantics",
        "`wubul` is many identifiable/countable items; `jirray` is mass/plenty/size. This matters in pairs like `birra wubul` vs `birra jirray`.",
        "cheatsheet Rule 88: wubul vs jirray contrast.",
    ),
    (
        "semantics",
        "`narmba-ku`, `wubul-ku`, `yambayamba`, and `yamba-kari` cover all/everything/nothing territory; avoid English-shaped universal quantifiers.",
        "errata Batch-130: all/everything/nothing inventory.",
    ),
    (
        "morphology",
        "Text 51 uses `kanku` for half even though the quantifier list has `kankur`; bank only text-attested `kanku` half rows until the variant relationship is better documented.",
        "errata Batch-130: kanku/kankur source tension.",
    ),
    (
        "morphology",
        "`yindu` has a suppletive y-final stem before inflection (`yinduy-mbu` in text 51); record it as partitive one-of/other, not as a free higher numeral.",
        "cheatsheet Rule 88: yindu/yinduy partitive note.",
    ),
    (
        "syntax",
        "`wanjarrinya` is the how-many interrogative; it can ask over an intransitive subject NP (`wanjarrinya bama ...`) and over an object in controlled transitive questions.",
        "cheatsheet Rule 88: wanjarrinya question frame.",
    ),
    (
        "process",
        "Batch 130 adds an external process dossier in `process_logs/` so source-gathering, rejected risks, and verification commands survive outside the compact batch notes.",
        "operator guide: process record requirement after every accepted batch.",
    ),
    (
        "process",
        "For construction batches, the evidence field should name exact Patz examples where possible and label minimal variants as controlled, not attested.",
        "operator guide: evidence-label discipline for future batches.",
    ),
]

LEXEMES = [
    ("nyubun", "numeral", None, "neutral/special-cardinal", "one", "batch-130: basic numeral one; special cardinal inflections in Patz Table 3.5"),
    ("jambul", "numeral", None, "neutral/special-cardinal", "two", "batch-130: counts two without implying an intrinsic pair"),
    ("jambul-jambul", "numeral", None, "neutral", "four (two-two)", "batch-130: derived number term in Patz §3.2.5"),
    ("kulur", "quantifier", None, "neutral", "a few; three", "batch-130: can mean small unspecified number as well as cardinal three"),
    ("kanbal", "quantifier", None, "neutral", "some; several", "batch-130: contrast with buban too few/insufficient"),
    ("buban", "quantifier/adjective", None, "neutral", "too few; insufficient; little", "batch-130: Patz ex.102b/103b quantity contrast"),
    ("wubul", "quantifier", None, "neutral", "many; a lot; all in emphatic wubul-ku", "batch-130: quantifier sense kept separate from homophonous body-part 'heart'"),
    ("jirray", "quantifier/adjective", None, "neutral", "much; plenty; mass; large crowd", "batch-130: mass/size quantifier contrasting with countable wubul"),
    ("narmba", "quantifier", None, "neutral", "all", "batch-130: dictionary example Bama narmbaku kadan"),
    ("wubul-ku", "quantifier", None, "NA", "all; every; really many", "batch-130: emphatic all/every use"),
    ("yambayamba", "quantifier", None, "neutral", "everything", "batch-130: Patz ex.517"),
    ("yamba-kari", "quantifier/particle", None, "NA", "nothing; none", "batch-130: dictionary solid/hyphenated contrast with text yambakari"),
    ("yambakari", "particle", None, "NA", "nothing (narrative surface)", "batch-130: text 36/51 solid surface; dictionary normalizes yamba-kari"),
    ("kankur", "quantifier", None, "neutral", "half", "batch-130: Patz §3.2.5 table form; use with caution because dictionary also has manner 'right off'"),
    ("kanku", "quantifier", None, "neutral", "half", "batch-130: text 51 surface used in accepted rows"),
    ("-bulal", "suffix", None, "NA", "dual pair suffix", "batch-130: intrinsic pair, not ordinary cardinal two"),
    ("bula", "pronoun", None, "NA", "third-person dual nominative", "batch-130: dual pronoun in text 51 and Patz examples"),
    ("yubal", "pronoun", None, "NA", "second-person dual nominative", "batch-130: you two"),
    ("wanjarrinya", "interrogative", None, "NA", "how many", "batch-130: how-many interrogative built on wanja family"),
    ("yindu", "quantifier/adjective", None, "neutral", "one of; other; another", "batch-130: distributive/partitive use in text 51"),
    ("yinduy-", "bound stem", None, "neutral", "suppletive y-final stem of yindu before inflection", "batch-130: yinduy-mbu in text 51 line 53"),
    ("mamarra", "quantifier", None, "neutral", "two, old-fashioned/stylish", "batch-130: logged as known but not used productively"),
    ("mara", "noun/quantifier", None, "neutral", "hand; five in Patz numeral table", "batch-130: not banked as a sentential numeral because dictionary primary is hand"),
    ("kambi", "noun", None, "neutral", "clothes", "batch-130: exact kanbal/buban contrast"),
    ("bulawu", "noun", None, "neutral", "flour", "batch-130: loanword in exact Patz kanbal/buban contrast"),
    ("birra", "noun", None, "neutral", "leaf", "batch-130: wubul/jirray contrast on leaves"),
    ("jarruka", "noun", None, "neutral", "scrubhen egg", "batch-130: wubul object example"),
    ("makarr", "noun", None, "neutral", "net", "batch-130: kuyu wubul bangka-nka example"),
    ("ngalkun", "noun", None, "neutral", "mullet", "batch-130: many mullet example"),
    ("walarr", "noun", None, "neutral", "beard", "batch-130: walarr-iji-ngka comitative A modifier"),
    ("jabajaba", "quantifier/adjective", None, "neutral", "whole lot; plentiful", "batch-130: jabajaba-jirr-ka many/plentiful leaves"),
]


def main() -> int:
    db = sqlite3.connect(DB)
    db.execute("PRAGMA foreign_keys = ON")
    db.execute("BEGIN")

    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}")

    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=CASE
                WHEN lexemes.status IN ('phantom-retired', 'watchlist') THEN lexemes.status
                ELSE 'verified'
              END,
              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, "batch-130",
            ),
        )
        db.execute(
            "INSERT INTO lexeme_log(headword, action, detail, created_at) VALUES(?,?,?,?)",
            (headword, "batch-130-verify", note, 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))

    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 = db.execute("SELECT COUNT(*) FROM sentences WHERE status IN ('accepted','revised')").fetchone()[0]
    print(f"closed {LABEL}: 50 accepted rows, {reviews} review passes, verified corpus={verified}")
    return 0


if __name__ == "__main__":
    sys.exit(main())
