#!/usr/bin/env python3
"""Bank batch-237: Rule 195 property-adjective lexical expansion."""

from __future__ import annotations

import datetime as dt
import json
import re
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-237"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-237-property-adjective-lexical-expansion-after-rule194-process.md"

TARGET_HEADS = {
    "yalbay": "big",
    "karrkay": "small",
    "kalbay": "long",
    "kulka": "short",
    "kulngkul": "heavy",
    "wuban": "strong",
    "yayji": "thin",
    "wumbul": "hot",
    "kiway-kiway": "cold",
    "marral": "dry",
}
SUBJECT_NOUNS = {
    "bayan": "house",
    "baral": "road",
    "juku": "wood/tree",
    "kulji": "stone",
    "bama": "person",
    "wungar": "sun",
    "kaba": "rain",
    "bana": "water",
    "bubu": "ground",
}
DENY_KUKU = {
    "dakwuy", "bambay", "dukul-wuri", "kala-burra", "warngku-kaday", "wawu-mini", "wawu-buyun",
    "milka-murruji", "ngulkurr", "juwamalmal", "baja-burray", "jibabadi-y", "murru-kaday",
    "mulkay-warri", "wawu-wulay", "wawu-daray", "walu-jirrbu-jirrbu", "ngalu-ngalu",
    "dukul-kulnkul", "muna", "yanji", "wunbu", "nuka", "waju", "dinda", "kada", "dunga",
    "warri", "bayka", "nyaji", "nyajil", "nyajin", "binal", "wawu", "milka", "dukul",
}
DENY_ENGLISH = (
    "eat", "food", "cook", "travel", "walk", "road event", "country", "own", "belong", "pregnant",
    "child", "sick", "doctor", "medicine", "diagnosis", "moral", "good", "bad", "body", "head",
    "ear", "soul", "cause", "because", "become", "make", "very", "rather", "more", "less",
)


def js(values: list[str]) -> str:
    return json.dumps(values, ensure_ascii=False)


def row(seq: int, english: str, kuku: str, analysis: str, frame: str, words: list[str], evidence: str) -> tuple:
    return (seq, english, kuku, analysis, frame, 3, js(words), js([]), evidence, "0.86")


ROWS = [
    row(1, "The house is big.", "Bayan yalbay.", "bayan yalbay | house big", "r195_np_adj_bayan_yalbay", ["bayan", "yalbay"], "Dictionary bayan 'house/camp/shelter'; yalbay 'big'; Patz NP+ADJ nominal predicate."),
    row(2, "The house is small.", "Bayan karrkay.", "bayan karrkay | house small", "r195_np_adj_bayan_karrkay", ["bayan", "karrkay"], "Dictionary karrkay adjective 'small'; selected adjective sense only."),
    row(3, "The road is long.", "Baral kalbay.", "baral kalbay | road long", "r195_np_adj_baral_kalbay", ["baral", "kalbay"], "Dictionary baral noun 'road/path'; kalbay 'long'."),
    row(4, "The wood is short.", "Juku kulka.", "juku kulka | wood short", "r195_np_adj_juku_kulka", ["juku", "kulka"], "Dictionary juku 'tree/wood'; kulka 'short'."),
    row(5, "The stone is heavy.", "Kulji kulngkul.", "kulji kulngkul | stone heavy", "r195_np_adj_kulji_kulngkul", ["kulji", "kulngkul"], "Dictionary kulji 'pebbles/small stones'; kulngkul 'heavy'."),
    row(6, "The person is strong.", "Bama wuban.", "bama wuban | person strong", "r195_np_adj_bama_wuban", ["bama", "wuban"], "Dictionary bama 'person/people'; wuban 'strong, hard'; selected strong."),
    row(7, "The person is thin.", "Bama yayji.", "bama yayji | person thin", "r195_np_adj_bama_yayji", ["bama", "yayji"], "Dictionary yayji 'thin, emaciated'; selected thin without diagnosis."),
    row(8, "The sun is hot.", "Wungar wumbul.", "wungar wumbul | sun hot", "r195_np_adj_wungar_wumbul", ["wungar", "wumbul"], "Dictionary wungar 'sun'; wumbul 'hot'."),
    row(9, "The rain is cold.", "Kaba kiway-kiway.", "kaba kiway-kiway | rain cold", "r195_np_adj_kaba_kiway_kiway", ["kaba", "kiway-kiway"], "Dictionary kaba 'rain'; kiway-kiway 'cold'."),
    row(10, "The ground is dry.", "Bubu marral.", "bubu marral | ground dry", "r195_np_adj_bubu_marral", ["bubu", "marral"], "Dictionary bubu 'ground/earth'; marral 'dry'."),
    row(11, "The house is not big.", "Bayan kari yalbay.", "bayan kari yalbay | house NEG big", "r195_np_kari_adj_bayan_yalbay", ["bayan", "kari", "yalbay"], "Patz: kari may precede an adjective for negation."),
    row(12, "The house is not small.", "Bayan kari karrkay.", "bayan kari karrkay | house NEG small", "r195_np_kari_adj_bayan_karrkay", ["bayan", "kari", "karrkay"], "Patz pre-adjective kari negation; karrkay adjective sense only."),
    row(13, "The road is not long.", "Baral kari kalbay.", "baral kari kalbay | road NEG long", "r195_np_kari_adj_baral_kalbay", ["baral", "kari", "kalbay"], "Patz pre-adjective kari negation."),
    row(14, "The wood is not short.", "Juku kari kulka.", "juku kari kulka | wood NEG short", "r195_np_kari_adj_juku_kulka", ["juku", "kari", "kulka"], "Patz pre-adjective kari negation."),
    row(15, "The stone is not heavy.", "Kulji kari kulngkul.", "kulji kari kulngkul | stone NEG heavy", "r195_np_kari_adj_kulji_kulngkul", ["kulji", "kari", "kulngkul"], "Patz pre-adjective kari negation; no pregnant reading."),
    row(16, "The person is not strong.", "Bama kari wuban.", "bama kari wuban | person NEG strong", "r195_np_kari_adj_bama_wuban", ["bama", "kari", "wuban"], "Patz pre-adjective kari negation."),
    row(17, "The person is not thin.", "Bama kari yayji.", "bama kari yayji | person NEG thin", "r195_np_kari_adj_bama_yayji", ["bama", "kari", "yayji"], "Patz pre-adjective kari negation; no diagnosis."),
    row(18, "The sun is not hot.", "Wungar kari wumbul.", "wungar kari wumbul | sun NEG hot", "r195_np_kari_adj_wungar_wumbul", ["wungar", "kari", "wumbul"], "Patz pre-adjective kari negation."),
    row(19, "The rain is not cold.", "Kaba kari kiway-kiway.", "kaba kari kiway-kiway | rain NEG cold", "r195_np_kari_adj_kaba_kiway_kiway", ["kaba", "kari", "kiway-kiway"], "Patz pre-adjective kari negation."),
    row(20, "The ground is not dry.", "Bubu kari marral.", "bubu kari marral | ground NEG dry", "r195_np_kari_adj_bubu_marral", ["bubu", "kari", "marral"], "Patz pre-adjective kari negation."),
    row(21, "That tree is big.", "Yinya juku yalbay.", "yinya juku yalbay | that tree big", "r195_dem_np_adj_juku_yalbay", ["yinya", "juku", "yalbay"], "Dictionary yinya 'that'; juku selected as tree; yalbay 'big'."),
    row(22, "That house is small.", "Yinya bayan karrkay.", "yinya bayan karrkay | that house small", "r195_dem_np_adj_bayan_karrkay", ["yinya", "bayan", "karrkay"], "Yinya demonstrative nominal predicate; karrkay adjective sense."),
    row(23, "That road is long.", "Yinya baral kalbay.", "yinya baral kalbay | that road long", "r195_dem_np_adj_baral_kalbay", ["yinya", "baral", "kalbay"], "Yinya demonstrative nominal predicate."),
    row(24, "That wood is short.", "Yinya juku kulka.", "yinya juku kulka | that wood short", "r195_dem_np_adj_juku_kulka", ["yinya", "juku", "kulka"], "Yinya demonstrative nominal predicate."),
    row(25, "That stone is heavy.", "Yinya kulji kulngkul.", "yinya kulji kulngkul | that stone heavy", "r195_dem_np_adj_kulji_kulngkul", ["yinya", "kulji", "kulngkul"], "Yinya demonstrative nominal predicate; no pregnant reading."),
    row(26, "That person is strong.", "Yinya bama wuban.", "yinya bama wuban | that person strong", "r195_dem_np_adj_bama_wuban", ["yinya", "bama", "wuban"], "Yinya demonstrative nominal predicate."),
    row(27, "That person is thin.", "Yinya bama yayji.", "yinya bama yayji | that person thin", "r195_dem_np_adj_bama_yayji", ["yinya", "bama", "yayji"], "Yinya demonstrative nominal predicate; no diagnosis."),
    row(28, "That sun is hot.", "Yinya wungar wumbul.", "yinya wungar wumbul | that sun hot", "r195_dem_np_adj_wungar_wumbul", ["yinya", "wungar", "wumbul"], "Yinya demonstrative nominal predicate."),
    row(29, "That water is cold.", "Yinya bana kiway-kiway.", "yinya bana kiway-kiway | that water cold", "r195_dem_np_adj_bana_kiway_kiway", ["yinya", "bana", "kiway-kiway"], "Yinya demonstrative nominal predicate; water subject avoids duplicate bare row."),
    row(30, "That ground is dry.", "Yinya bubu marral.", "yinya bubu marral | that ground dry", "r195_dem_np_adj_bubu_marral", ["yinya", "bubu", "marral"], "Yinya demonstrative nominal predicate."),
    row(31, "That house is not big.", "Yinya bayan kari yalbay.", "yinya bayan kari yalbay | that house NEG big", "r195_dem_np_kari_adj_bayan_yalbay", ["yinya", "bayan", "kari", "yalbay"], "Yinya demonstrative plus pre-adjective kari negation."),
    row(32, "That house is not small.", "Yinya bayan kari karrkay.", "yinya bayan kari karrkay | that house NEG small", "r195_dem_np_kari_adj_bayan_karrkay", ["yinya", "bayan", "kari", "karrkay"], "Yinya demonstrative plus pre-adjective kari negation."),
    row(33, "That road is not long.", "Yinya baral kari kalbay.", "yinya baral kari kalbay | that road NEG long", "r195_dem_np_kari_adj_baral_kalbay", ["yinya", "baral", "kari", "kalbay"], "Yinya demonstrative plus pre-adjective kari negation."),
    row(34, "That wood is not short.", "Yinya juku kari kulka.", "yinya juku kari kulka | that wood NEG short", "r195_dem_np_kari_adj_juku_kulka", ["yinya", "juku", "kari", "kulka"], "Yinya demonstrative plus pre-adjective kari negation."),
    row(35, "That stone is not heavy.", "Yinya kulji kari kulngkul.", "yinya kulji kari kulngkul | that stone NEG heavy", "r195_dem_np_kari_adj_kulji_kulngkul", ["yinya", "kulji", "kari", "kulngkul"], "Yinya demonstrative plus pre-adjective kari negation; no pregnant reading."),
    row(36, "That person is not strong.", "Yinya bama kari wuban.", "yinya bama kari wuban | that person NEG strong", "r195_dem_np_kari_adj_bama_wuban", ["yinya", "bama", "kari", "wuban"], "Yinya demonstrative plus pre-adjective kari negation."),
    row(37, "That person is not thin.", "Yinya bama kari yayji.", "yinya bama kari yayji | that person NEG thin", "r195_dem_np_kari_adj_bama_yayji", ["yinya", "bama", "kari", "yayji"], "Yinya demonstrative plus pre-adjective kari negation; no diagnosis."),
    row(38, "That sun is not hot.", "Yinya wungar kari wumbul.", "yinya wungar kari wumbul | that sun NEG hot", "r195_dem_np_kari_adj_wungar_wumbul", ["yinya", "wungar", "kari", "wumbul"], "Yinya demonstrative plus pre-adjective kari negation."),
    row(39, "That rain is not cold.", "Yinya kaba kari kiway-kiway.", "yinya kaba kari kiway-kiway | that rain NEG cold", "r195_dem_np_kari_adj_kaba_kiway_kiway", ["yinya", "kaba", "kari", "kiway-kiway"], "Yinya demonstrative plus pre-adjective kari negation."),
    row(40, "That ground is not dry.", "Yinya bubu kari marral.", "yinya bubu kari marral | that ground NEG dry", "r195_dem_np_kari_adj_bubu_marral", ["yinya", "bubu", "kari", "marral"], "Yinya demonstrative plus pre-adjective kari negation."),
    row(41, "The house is not big.", "Bayan yalbay-kari.", "bayan yalbay-kari | house big-PRIV", "r195_np_adj_priv_bayan_yalbay", ["bayan", "yalbay", "-kari"], "Patz: kari may follow an adjective as privative-like negative predicate."),
    row(42, "The house is not small.", "Bayan karrkay-kari.", "bayan karrkay-kari | house small-PRIV", "r195_np_adj_priv_bayan_karrkay", ["bayan", "karrkay", "-kari"], "Patz post-adjective kari negative predicate; karrkay adjective sense only."),
    row(43, "The road is not long.", "Baral kalbay-kari.", "baral kalbay-kari | road long-PRIV", "r195_np_adj_priv_baral_kalbay", ["baral", "kalbay", "-kari"], "Patz post-adjective kari negative predicate."),
    row(44, "The wood is not short.", "Juku kulka-kari.", "juku kulka-kari | wood short-PRIV", "r195_np_adj_priv_juku_kulka", ["juku", "kulka", "-kari"], "Patz post-adjective kari negative predicate."),
    row(45, "The stone is not heavy.", "Kulji kulngkul-kari.", "kulji kulngkul-kari | stone heavy-PRIV", "r195_np_adj_priv_kulji_kulngkul", ["kulji", "kulngkul", "-kari"], "Patz post-adjective kari negative predicate; no pregnant reading."),
    row(46, "The person is not strong.", "Bama wuban-kari.", "bama wuban-kari | person strong-PRIV", "r195_np_adj_priv_bama_wuban", ["bama", "wuban", "-kari"], "Patz post-adjective kari negative predicate."),
    row(47, "The person is not thin.", "Bama yayji-kari.", "bama yayji-kari | person thin-PRIV", "r195_np_adj_priv_bama_yayji", ["bama", "yayji", "-kari"], "Patz post-adjective kari negative predicate; no diagnosis."),
    row(48, "The sun is not hot.", "Wungar wumbul-kari.", "wungar wumbul-kari | sun hot-PRIV", "r195_np_adj_priv_wungar_wumbul", ["wungar", "wumbul", "-kari"], "Patz post-adjective kari negative predicate."),
    row(49, "The rain is not cold.", "Kaba kiway-kiway-kari.", "kaba kiway-kiway-kari | rain cold-PRIV", "r195_np_adj_priv_kaba_kiway_kiway", ["kaba", "kiway-kiway", "-kari"], "Patz post-adjective kari negative predicate."),
    row(50, "The ground is not dry.", "Bubu marral-kari.", "bubu marral-kari | ground dry-PRIV", "r195_np_adj_priv_bubu_marral", ["bubu", "marral", "-kari"], "Patz post-adjective kari negative predicate."),
]


def has_token(low: str, token: str) -> bool:
    return re.search(rf"(?<![a-z-]){re.escape(token)}(?![a-z-])", low) is not None


def has_target(low: str, head: str) -> bool:
    return has_token(low, head) or has_token(low, f"{head}-kari")


def has_forbidden_target_morphology(low: str, head: str, allow_priv: bool) -> bool:
    suffixes = "ngka|ngku|angka|nga|ngu|ba|mba|mbu|muny|darr|burr|bu|ka|nka|nku|ji|jirr|ku|mundu|amu|mu|iji|nda|ndu|anda|undu|unji|nji|baka|baja"
    if re.search(rf"(?<![a-z-]){re.escape(head)}-(?:{suffixes})\b", low):
        return True
    if not allow_priv and re.search(rf"(?<![a-z-]){re.escape(head)}-kari\b", low):
        return True
    return False


def english_has_denied(english_low: str, denied: str) -> bool:
    return denied in english_low if " " in denied else re.search(rf"\b{re.escape(denied)}\b", english_low) is not None


def scan_static() -> list[str]:
    issues: list[str] = []
    if len(ROWS) != 50:
        issues.append(f"{LABEL}: expected 50 rows, found {len(ROWS)}")
    seen_ku: set[str] = set()
    for expected, row_data in enumerate(ROWS, start=1):
        seq, english, kuku, analysis, frame, _tier, words_used, loans_used, evidence, confidence = row_data
        label = f"{LABEL}.{seq:02d}"
        low = kuku.lower()
        english_low = english.lower()
        words = json.loads(words_used)
        allow_priv = 41 <= seq <= 50
        has_free_kari = has_token(low, "kari")
        has_priv = "-kari" in low
        if seq != expected:
            issues.append(f"{label}: sequence mismatch; expected {expected}")
        if not english[:1].isupper() or english[-1:] not in ".!?":
            issues.append(f"{label}: English punctuation/capitalization failure")
        if " | " not in analysis:
            issues.append(f"{label}: analysis missing gloss separator")
        if not frame.startswith("r195_"):
            issues.append(f"{label}: Rule 195 frame must start with r195_")
        if low in seen_ku:
            issues.append(f"{label}: duplicate Kuku surface within batch")
        seen_ku.add(low)
        if json.loads(loans_used):
            issues.append(f"{label}: no loans are admitted in Rule 195")
        for denied in DENY_KUKU:
            if has_token(low, denied):
                issues.append(f"{label}: denied carry-forward token {denied!r}")
        targets = [head for head in TARGET_HEADS if has_target(low, head)]
        if len(targets) != 1:
            issues.append(f"{label}: expected exactly one Rule 195 property target head, found {targets}")
        for head in targets:
            selected = TARGET_HEADS[head]
            if head not in words:
                issues.append(f"{label}: target head {head} missing from words_used")
            if selected not in analysis:
                issues.append(f"{label}: target head {head} must be glossed as {selected}")
            if selected not in english_low:
                issues.append(f"{label}: {head} must be translated with selected sense {selected!r}")
            if has_forbidden_target_morphology(low, head, allow_priv):
                issues.append(f"{label}: target head {head} has forbidden morphology")
        subjects = [head for head in SUBJECT_NOUNS if has_token(low, head)]
        if len(subjects) != 1:
            issues.append(f"{label}: expected exactly one subject noun, found {subjects}")
        if 1 <= seq <= 10 and (has_free_kari or has_priv or low.startswith("yinya ")):
            issues.append(f"{label}: rows 1-10 require bare NP+ADJ")
        if 11 <= seq <= 20 and (not has_free_kari or has_priv or low.startswith("yinya ")):
            issues.append(f"{label}: rows 11-20 require bare NP + kari + ADJ")
        if 21 <= seq <= 30 and (not low.startswith("yinya ") or has_free_kari or has_priv):
            issues.append(f"{label}: rows 21-30 require yinya NP+ADJ")
        if 31 <= seq <= 40 and (not low.startswith("yinya ") or not has_free_kari or has_priv):
            issues.append(f"{label}: rows 31-40 require yinya NP + kari + ADJ")
        if 41 <= seq <= 50 and (has_free_kari or not has_priv or "PRIV" not in analysis):
            issues.append(f"{label}: rows 41-50 require post-adjective -kari with PRIV gloss")
        if (has_free_kari or has_priv) and "not" not in english_low:
            issues.append(f"{label}: negative rows require English not")
        if has_free_kari and " NEG " not in f" {analysis} ":
            issues.append(f"{label}: free kari rows require NEG gloss")
        if not has_free_kari and " NEG " in f" {analysis} ":
            issues.append(f"{label}: non-free-kari rows must not use NEG gloss")
        if any(english_has_denied(english_low, bad) for bad in DENY_ENGLISH):
            issues.append(f"{label}: denied event/source/polysemy field in English")
        if not evidence or not confidence:
            issues.append(f"{label}: missing evidence/confidence")
    return issues


def main() -> int:
    issues = scan_static()
    if issues:
        print("pre-bank scan failed:")
        for issue in issues:
            print(f"  - {issue}")
        return 1

    db = sqlite3.connect(DB)
    db.execute("PRAGMA foreign_keys = ON")
    db.execute("BEGIN")
    ensure_schema(db)
    existing = db.execute("SELECT id FROM batches WHERE label=?", (LABEL,)).fetchone()
    if existing:
        batch_id = existing[0]
        accepted = db.execute(
            "SELECT COUNT(*) FROM sentences WHERE batch_id=? AND status IN ('accepted','revised')",
            (batch_id,),
        ).fetchone()[0]
        if accepted:
            raise SystemExit(f"{LABEL} already has accepted/revised rows; refusing to overwrite")
        db.execute("DELETE FROM reviews WHERE sentence_id IN (SELECT id FROM sentences WHERE batch_id=?)", (batch_id,))
        db.execute("DELETE FROM sentences WHERE batch_id=?", (batch_id,))
        db.execute("UPDATE batches SET notes=?, created_at=? WHERE id=?", ("rebanked under_review", NOW, batch_id))
    else:
        cur = db.execute(
            "INSERT INTO batches(label, cheatsheet_version, guide_version, notes, created_at) VALUES(?,?,?,?,?)",
            (LABEL, "Rule 195 draft: property-adjective lexical expansion", "operator-guide-2026-07-08", "under_review", NOW),
        )
        batch_id = cur.lastrowid

    dupes: list[str] = []
    for seq, _english, kuku, *_rest in ROWS:
        hit = db.execute(
            """
            SELECT b.label, s.seq
            FROM sentences s JOIN batches b ON b.id=s.batch_id
            WHERE lower(trim(s.kuku))=lower(trim(?))
              AND s.status!='rejected'
              AND s.batch_id!=?
            """,
            (kuku, batch_id),
        ).fetchone()
        if hit:
            dupes.append(f"{LABEL}.{seq:02d} duplicates {hit[0]}.{hit[1]:02d}: {kuku}")
    if dupes:
        db.rollback()
        print("cross-corpus duplicate check failed:")
        for dupe in dupes:
            print(f"  - {dupe}")
        return 1

    record_event(db, batch_label=LABEL, phase="source", event_type="source-pass", actor="claude-main",
                 detail="Rule 195 source pass selected ten dictionary-backed property adjectives and nine noun subjects. Grammar readback confirmed adjectives as nominal property predicates, NP+ADJ nominal sentences, yinya demonstrative nominal predicates, kari before adjectives, and post-adjective -kari as a privative-like negative predicate.",
                 command="dictionary YAML structured adjective/noun readback; Patz adjective/nominal-sentence/kari readback; sqlite exact duplicate preflight",
                 artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="scope", event_type="lexical-scope", actor="claude-main",
                 detail="Rule 195 is a bounded property-adjective lexical pass over yalbay, karrkay, kalbay, kulka, kulngkul, wuban, yayji, wumbul, kiway-kiway, and marral. It excludes case, degree, causative, inchoative, plural, target compounds, productive reduplication, child-noun karrkay, pregnant kulngkul, medical/diagnosis/event readings, material-use, travel, ownership/country, and Rule 193-194 state-predicate traps.",
                 artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="duplicate-preflight", actor="claude-main",
                 detail="Exact duplicate preflight replaced two candidate surfaces before banking and found 50 planned Kuku surfaces, 50 unique within-batch surfaces, and no exact accepted/revised/under_review cross-corpus duplicates.",
                 command="sqlite exact lower(trim(kuku)) duplicate scan before banking", artifact_path="batch_scripts/bank_237.py", created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="static-guard-pass", actor="claude-main",
                 detail="Static guard passed: exactly one Rule 195 target adjective and one subject noun per row, no loans, no denied carry-forward tokens, correct frame distribution, correct NEG/PRIV alignment, and no denied event/polysemy English fields.",
                 command="python3 batch_scripts/bank_237.py pre-bank scan", artifact_path="batch_scripts/bank_237.py", created_at=NOW)

    db.executemany(
        """
        INSERT INTO sentences(
          batch_id, seq, english, kuku, analysis, frame, tier, words_used,
          loans_used, evidence, status, confidence, author, created_at, updated_at
        )
        VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
        """,
        [
            (
                batch_id, seq, english, kuku, analysis, frame, tier, words_used,
                loans_used, evidence, "under_review", confidence, "claude", NOW, NOW,
            )
            for seq, english, kuku, analysis, frame, tier, words_used,
            loans_used, evidence, confidence in ROWS
        ],
    )
    db.commit()

    count = db.execute("SELECT COUNT(*) FROM sentences WHERE batch_id=?", (batch_id,)).fetchone()[0]
    under_review = db.execute("SELECT COUNT(*) FROM sentences WHERE batch_id=? AND status='under_review'", (batch_id,)).fetchone()[0]
    verified = db.execute("SELECT COUNT(*) FROM sentences WHERE status IN ('accepted','revised')").fetchone()[0]
    print(f"banked {LABEL}: rows={count}, under_review={under_review}, verified_before_close={verified}")
    return 0


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