#!/usr/bin/env python3
"""Bank batch-131: weather, sky, and time-of-day lexical field.

Rows are hand-authored in the main loop from Patz weather/time examples and
dictionary-confirmed sky-weather headwords. The script only stores the rows.
"""

from __future__ import annotations

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

DENY = {
    "nyawa", "jinbi", "jibin", "walba", "julmbu", "wanan", "ngampula",
    "yalama", "ngana-jinji", "jubuny", "wural", "bigan", "jiwuny",
    "warngkul", "wanaba", "jarrkunji", "munjal", "murku", "buni",
    "kunya", "milkan", "kulur-jambul", "janyji", "buburr", "jarrmba",
    "jinji", "daga", "wanka", "yumbul", "kuku-yumbul", "jubi",
    "yilbay", "jarrakay", "janka-yukil", "jukarr", "kunkul",
    "bubu-jandu", "bubu-warral", "jani-warra", "balbay-nja",
    "bunjurril",
}


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


ROWS = [
    (1, "Rain is coming close.",
     "Kaba ngundu kaday.",
     "kaba ngundu kada-y | rain close come-NONPAST",
     "weather_intransitive_controlled", 3, js(["kaba", "ngundu", "kaday"]), js([]),
     "Controlled from Patz ex.226 Kaba kada-y plus ngundu close; avoids duplicate Kaba kaday.", "0.86"),
    (2, "There was heavy rain.",
     "Kaba yalbay.",
     "kaba yalbay | rain big/heavy",
     "attested_heavy_rain_nominal", 3, js(["kaba", "yalbay"]), js([]),
     "Patz text 3 line 7: Kaba yalbay, lit. rain big.", "0.90"),
    (3, "Rain fell.",
     "Kaba daran.",
     "kaba dara-ny | rain fall-PAST",
     "rain_fall_controlled", 4, js(["kaba", "daray"]), js([]),
     "Controlled from Patz ex.454 kaba dara-nyjiku 'before rain falls'.", "0.86"),
    (4, "There was no rain at all.",
     "Kaba kariku.",
     "kaba kari-ku | rain NEG-EMPH",
     "attested_rain_absent_emphatic", 4, js(["kaba", "kari", "-ku"]), js([]),
     "Patz text 3 line 29: Kaba kari-ku.", "0.90"),
    (5, "There was no rain any more.",
     "Kaba yambakari-da.",
     "kaba yambakari-da | rain nothing-TEMP1",
     "attested_rain_none_anymore", 4, js(["kaba", "yamba-kari", "-da"]), js([]),
     "Patz text 3 line 27: Kaba yambakari-da.", "0.90"),
    (6, "There are many clouds.",
     "Ngurrban wubul.",
     "ngurrban wubul | cloud many",
     "weather_nominal_quantifier", 3, js(["ngurrban", "wubul"]), js([]),
     "Dictionary ngurrban 'cloud'; wubul many from Patz §3.2.5.", "0.86"),
    (7, "The sky is cloudy.",
     "Jiri ngurrban-ji.",
     "jiri ngurrban-ji | sky cloud-COMIT",
     "comitative_weather_state", 4, js(["jiri", "ngurrban"]), js([]),
     "Controlled comitative state: sky with clouds; both lexemes dictionary-confirmed.", "0.86"),
    (8, "There was heavy fog.",
     "Ngurrbal yalbay.",
     "ngurrbal yalbay | fog big/heavy",
     "fog_nominal_state", 3, js(["ngurrbal", "yalbay"]), js([]),
     "Dictionary ngurrbal 'fog' with heavy-fog usage.", "0.86"),
    (9, "Fog was on the river.",
     "Wurrbal wawubaja-nga.",
     "wurrbal wawubaja-nga | fog river-LOC",
     "fog_locative_state", 4, js(["wurrbal", "wawubaja"]), js([]),
     "Dictionary wurrbal 'fog/mist rising from river'; locative frame controlled.", "0.86"),
    (10, "The sun is bright.",
     "Wungar dayirr.",
     "wungar dayirr | sun bright",
     "sun_property", 3, js(["wungar", "dayirr"]), js([]),
     "Dictionary dayirr gives sun-bright example; wungar 'sun'.", "0.86"),
    (11, "The sun rises.",
     "Wungar dakal.",
     "wungar daka-l | sun climb-NONPAST",
     "attested_sunrise_compound", 4, js(["wungar", "dakal"]), js([]),
     "Dictionary wungar dakal 'sunrise'; Patz §3.4 notes wungar dakal.", "0.90"),
    (12, "The sun set.",
     "Wungar daran.",
     "wungar dara-ny | sun fall-PAST",
     "sunset_past", 4, js(["wungar", "wungar daray"]), js([]),
     "Dictionary wungar daray 'sunset'; elder past surface daran.", "0.86"),
    (13, "The sun came out from behind the cloud.",
     "Wungar jalamal.",
     "wungar jalama-l | sun jump/out-INCHO-NONPAST",
     "attested_sun_peek", 4, js(["wungar", "wungar jalamal"]), js([]),
     "Dictionary wungar jalamal: sun comes out from behind cloud.", "0.90"),
    (14, "The sun is coming up.",
     "Wungar wandil-wandil.",
     "wungar wandi-l-wandi-l | sun rise-l-REDUP",
     "attested_sun_rising", 4, js(["wungar", "wungar wandil-wandil"]), js([]),
     "Dictionary wungar wandil-wandil: the sun is coming up.", "0.90"),
    (15, "It is midday.",
     "Wungar wangkar-wangkar.",
     "wungar wangkar-wangkar | sun above-REDUP",
     "attested_midday", 4, js(["wungar", "wungar wangkar-wangkar"]), js([]),
     "Dictionary and Patz §3.4: wungar wangkarwangkar mid-morning to midday/early afternoon.", "0.90"),
    (16, "Rain came at daybreak.",
     "Muduwajuny kaba kadan.",
     "muduwaju-ny kaba kada-ny | daybreak-TEMP rain come-PAST",
     "time_weather_past", 4, js(["muduwaju", "kaba", "kaday"]), js([]),
     "Patz text 36 has muduwaju-ny daybreak; Kaba kaday from Patz ex.226.", "0.86"),
    (17, "In the morning there were many clouds.",
     "Wunkuwingku ngurrban wubul.",
     "wunkuwingku ngurrban wubul | morning cloud many",
     "time_weather_nominal", 4, js(["wunkuwingku", "ngurrban", "wubul"]), js([]),
     "Dictionary wunkuwingku morning and ngurrban cloud; quantifier frame controlled.", "0.86"),
    (18, "The wind came in the late afternoon.",
     "Yilay-yilay muyar kadan.",
     "yilay-yilay muyar kada-ny | late.afternoon wind come-PAST",
     "time_weather_motion", 4, js(["yilay-yilay", "muyar", "kaday"]), js([]),
     "Dictionary yilay-yilay late afternoon; Patz ex.455 muyar kada-nyjiku.", "0.86"),
    (19, "A thunderstorm came in the night.",
     "Wujurr-bu jarramali kadan.",
     "wujurr-bu jarramali kada-ny | night-LOC thunderstorm come-PAST",
     "night_weather_motion", 4, js(["wujurr", "jarramali", "kaday"]), js([]),
     "Dictionary jarramali thunder/thunderstorm; wujurr-bu night/darkness LOC from Patz §3.4.", "0.86"),
    (20, "At night there were many stars.",
     "Wujurr-bu dawar wubul.",
     "wujurr-bu dawar wubul | night-LOC star many",
     "night_sky_quantifier", 3, js(["wujurr", "dawar", "wubul"]), js([]),
     "Dictionary dawar star; wujurr-bu night/darkness LOC; controlled quantifier frame.", "0.86"),
    (21, "There are many stars in the sky.",
     "Jiri-nga dawar wubul.",
     "jiri-nga dawar wubul | sky-LOC star many",
     "sky_locative_quantifier", 4, js(["jiri", "dawar", "wubul"]), js([]),
     "Dictionary jiri sky and dawar star; locative after i-final root controlled.", "0.86"),
    (22, "The hot season is hot.",
     "Wungar-iji wumbul.",
     "wungar-iji wumbul | sun-COMIT hot",
     "attested_hot_season_state", 4, js(["wungar-iji", "wumbul"]), js([]),
     "Patz ex.539 wungar-iji hot season; dictionary wumbul hot.", "0.90"),
    (23, "The cold season is cold.",
     "Bulur-iji kiway.",
     "bulur-iji kiway | chill/dew-COMIT cold",
     "attested_cold_season_state", 4, js(["bulur-iji", "kiway"]), js([]),
     "Patz ex.540 bulur-iji cold season; dictionary kiway cold.", "0.90"),
    (24, "When will you come back in the hot season?",
     "Wanjawanja yundu kaday-baja, wungariji?",
     "wanjawanja yundu kada-y-baja, wungar-iji | when you come-NONPAST-again hot.season",
     "attested_hot_season_question", 5, js(["wanjawanja", "yundu", "kaday", "wungar-iji"]), js([]),
     "Patz ex.582: Wanjawanja yundu kada-y-baja, wungariji?", "0.90"),
    (25, "His grandfather died in the hot season.",
     "Nyungu ngaji wungar-iji wulan.",
     "nyungu ngaji wungar-iji wula-ny | his grandfather hot.season die-PAST",
     "attested_hot_season_event", 5, js(["nyungu", "ngaji", "wungar-iji", "wulay"]), js([]),
     "Patz ex.541: Nyungu ngaji wungar-iji wula-ny.", "0.90"),
    (26, "I went by boat, but there was a strong wind.",
     "Ngayu buwun-ji dungan, kari muyar junkuji.",
     "ngayu buwun-ji dunga-ny, kari muyar junkuji | I boat-COMIT go-PAST but wind strong",
     "attested_wind_hindrance", 5, js(["ngayu", "buwun", "dungay", "kari", "muyar", "junkuji"]), js([]),
     "Patz ex.388: strong wind as a hindering kari clause.", "0.90"),
    (27, "The wind makes me shiver.",
     "Muyar-abu nganya yararri-manil.",
     "muyar-abu nganya yararri-mani-l | wind-ERG me.ACC shiver-CAUS-NONPAST",
     "attested_wind_causative", 5, js(["muyar", "nganya", "yararri", "-manil"]), js([]),
     "Patz ex.325: Muyar-abu nganya yararri-mani-l.", "0.90"),
    (28, "I went before the wind came.",
     "Ngayu dungan, muyar kada-nyjiku.",
     "ngayu dunga-ny, muyar kada-nyjiku | I go-PAST wind come-SUCC",
     "attested_wind_successive", 5, js(["ngayu", "dungay", "muyar", "kaday", "-nyjiku"]), js([]),
     "Patz ex.455: no-coreferent successive construction with wind.", "0.90"),
    (29, "The wind came.",
     "Muyar kadan.",
     "muyar kada-ny | wind come-PAST",
     "wind_intransitive", 3, js(["muyar", "kaday"]), js([]),
     "Controlled from Patz ex.455 muyar kada-nyjiku.", "0.86"),
    (30, "There was a strong wind.",
     "Muyar junkuji.",
     "muyar junkuji | wind strong",
     "attested_wind_strong_nominal", 3, js(["muyar", "junkuji"]), js([]),
     "Patz ex.388: kari muyar junkuji.", "0.90"),
    (31, "A thunderstorm is coming.",
     "Jarramali kaday.",
     "jarramali kada-y | thunderstorm come-NONPAST",
     "thunderstorm_intransitive", 3, js(["jarramali", "kaday"]), js([]),
     "Controlled from dictionary jarramali and Patz weather subject Kaba kaday.", "0.86"),
    (32, "A thunderstorm struck the cow on the mountain.",
     "Jarramali-ngka bulki dalbarrin manjal-ba.",
     "jarramali-ngka bulki dalbarri-ny manjal-ba | thunderstorm-ERG cow strike-PAST mountain-LOC",
     "attested_storm_strike", 5, js(["jarramali", "bulki", "dalbarril", "manjal"]), js([]),
     "Patz ex.504: Jarramali-ngka bulki dalbarri-ny, manjal-ba.", "0.90"),
    (33, "Thunder came from the west.",
     "Jabulbina kuwa-muny kadan.",
     "jabulbina kuwa-muny kada-ny | thunder west-ABL come-PAST",
     "thunder_west_controlled", 4, js(["jabulbina", "kuwa", "kaday"]), js([]),
     "Dictionary jabulbina: thunder out west/start of storms; ablative motion frame controlled.", "0.86"),
    (34, "Lightning came.",
     "Balbay kadan.",
     "balbay kada-ny | lightning come-PAST",
     "lightning_intransitive", 3, js(["balbay", "kaday"]), js([]),
     "Dictionary balbay lightning; simple weather-S motion frame controlled.", "0.86"),
    (35, "Lightning was in the sky.",
     "Balbay jiri-nga.",
     "balbay jiri-nga | lightning sky-LOC",
     "lightning_locative_state", 4, js(["balbay", "jiri"]), js([]),
     "Dictionary balbay lightning and jiri sky; avoids denied balbay-nja form.", "0.86"),
    (36, "A cyclone came from the sea.",
     "Wadjal jalun-muny kadan.",
     "wadjal jalun-muny kada-ny | cyclone sea-ABL come-PAST",
     "cyclone_ablative_motion", 4, js(["wadjal", "jalun", "kaday"]), js([]),
     "Dictionary wadjal cyclone; controlled ablative motion frame.", "0.86"),
    (37, "A rainbow is there.",
     "Yiril yinyay.",
     "yiril yinyay | rainbow there",
     "rainbow_nominal_state", 3, js(["yiril", "yinyay"]), js([]),
     "Dictionary yiril rainbow; simple locational nominal sentence.", "0.86"),
    (38, "We went in the rain.",
     "Kaba-nga-ku nganjin dungan.",
     "kaba-nga-ku nganjin dunga-ny | rain-LOC-EMPH we.excl go-PAST",
     "attested_rain_locative_motion", 5, js(["kaba", "nganjin", "dungay"]), js([]),
     "Patz text 3 line 2: Kaba-nga-ku nganjin dunga-ny.", "0.90"),
    (39, "We drove in the rain.",
     "Kaba-nga-ku warrin.",
     "kaba-nga-ku warri-ny | rain-LOC-EMPH run/drive-PAST",
     "attested_rain_driving", 5, js(["kaba", "warri"]), js([]),
     "Patz text 3 line 3: Kaba-nga-ku warri-ny, driving/running in the rain.", "0.90"),
    (40, "The ground was slippery.",
     "Bubu julbarr.",
     "bubu julbarr | ground slippery",
     "slippery_ground_state", 3, js(["bubu", "julbarr"]), js([]),
     "Controlled from Patz text 3 bubu julbarr-bi; non-emphatic to avoid duplicate.", "0.86"),
    (41, "The ground was dry then.",
     "Bubu marral-da.",
     "bubu marral-da | ground dry-TEMP1",
     "attested_ground_dry_then", 4, js(["bubu", "marral", "-da"]), js([]),
     "Patz text 3 line 28: Bubu marral-da.", "0.90"),
    (42, "His hair is wet.",
     "Nyungu mungka duna.",
     "nyungu mungka duna | his hair wet",
     "attested_wet_body_part", 4, js(["nyungu", "mungka", "duna"]), js([]),
     "Patz ex.519: Nyungu mungka duna.", "0.90"),
    (43, "The wood is wet.",
     "Juku duna.",
     "juku duna | wood/tree wet",
     "wet_wood_state", 3, js(["juku", "duna"]), js([]),
     "Controlled from dictionary duna wet and Patz ex.60/519 wet predicates.", "0.86"),
    (44, "The wood is dry.",
     "Juku marral.",
     "juku marral | wood/tree dry",
     "dry_wood_state", 3, js(["juku", "marral"]), js([]),
     "Controlled from dictionary marral dry and verified juku wood/tree.", "0.86"),
    (45, "The child slept in the night.",
     "Karrkay wujurr-bu wunan.",
     "karrkay wujurr-bu wuna-ny | child night-LOC lie/sleep-PAST",
     "night_sleep_controlled", 4, js(["karrkay", "wujurr", "wunay"]), js([]),
     "Controlled from Patz wujurr-bu night LOC and verified wuna-ny sleep/lie.", "0.86"),
    (46, "The night is dark.",
     "Wujurr ngumbu.",
     "wujurr ngumbu | night/darkness dark",
     "dark_night_state", 3, js(["wujurr", "ngumbu"]), js([]),
     "Dictionary wujurr darkness/night; grammar colour/property ngumbu dark.", "0.86"),
    (47, "Cold is coming.",
     "Kiway kaday.",
     "kiway kada-y | cold come-NONPAST",
     "cold_weather_intransitive", 3, js(["kiway", "kaday"]), js([]),
     "Controlled from Patz ex.443 kiway kada-nyji.", "0.86"),
    (48, "The day is very hot.",
     "Wungar wumbul bajaku.",
     "wungar wumbul bajaku | day/sun hot very",
     "hot_day_state", 3, js(["wungar", "wumbul", "bajaku"]), js([]),
     "Dictionary wumbul hot and Patz wungar as day/sun; bajaku intensifier.", "0.86"),
    (49, "Lightning flashed.",
     "Balbay balban.",
     "balbay balba-ny | lightning shine-PAST",
     "lightning_shine_controlled", 4, js(["balbay", "balbal"]), js([]),
     "Dictionary balbal shine; subject balbay lightning, avoiding denied balbay-nja.", "0.86"),
    (50, "A big thunderstorm came.",
     "Jarramali yalbay kadan.",
     "jarramali yalbay kada-ny | thunderstorm big come-PAST",
     "big_thunderstorm_controlled", 4, js(["jarramali", "yalbay", "kaday"]), js([]),
     "Dictionary jarramali thunderstorm; Patz Kaba yalbay gives weather + yalbay heavy/big frame.", "0.86"),
]


def scan_static() -> list[str]:
    issues: list[str] = []
    if len(ROWS) != 50:
        issues.append(f"expected 50 rows, got {len(ROWS)}")
    seqs = [r[0] for r in ROWS]
    if seqs != list(range(1, 51)):
        issues.append(f"seqs must be 1..50, got {seqs[:5]}...{seqs[-5:]}")
    seen_ku: set[str] = set()
    for row in ROWS:
        seq, english, kuku, analysis, _frame, _tier, words_used, loans_used, _evidence, _confidence = row
        label = f"{LABEL}.{seq:02d}"
        if not english[:1].isupper() or english.rstrip("'\"").rstrip()[-1:] not in ".!?":
            issues.append(f"{label}: English must be capitalized and final-punctuated")
        if " | " not in analysis:
            issues.append(f"{label}: analysis missing gloss separator")
        if not kuku[:1].isupper():
            issues.append(f"{label}: Kuku surface must start uppercase")
        for field_name, payload in (("words_used", words_used), ("loans_used", loans_used)):
            try:
                json.loads(payload)
            except Exception as exc:
                issues.append(f"{label}: {field_name} is not JSON: {exc}")
        low = f" {kuku.lower()} {analysis.lower()} "
        for word in DENY:
            if re.search(rf"(?<![a-z]){re.escape(word)}(?![a-z])", low):
                issues.append(f"{label}: denied phantom/wrong-sense token {word!r}")
        if kuku.lower() in seen_ku:
            issues.append(f"{label}: duplicate Kuku surface within batch")
        seen_ku.add(kuku.lower())
    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")
    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 89 draft: weather and sky lexical field", "operator-guide-2026-07-07", "under_review", NOW),
        )
        batch_id = cur.lastrowid

    dupes = []
    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

    db.executemany(
        """
        INSERT INTO sentences(
            batch_id, seq, english, kuku, analysis, frame, tier, words_used,
            loans_used, evidence, status, confidence, rights_status, author,
            created_at, updated_at
        ) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
        """,
        [
            (
                batch_id, seq, english, kuku, analysis, frame, tier, words_used,
                loans_used, evidence, "under_review", confidence,
                "project_approved_synthetic_pending_elder_verification",
                "claude-main", 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]
    avg_words = db.execute(
        "SELECT AVG(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1.0) FROM sentences WHERE batch_id=?",
        (batch_id,),
    ).fetchone()[0]
    print(f"banked {LABEL}: {count} rows under_review; avg Kuku words/row={avg_words:.2f}")
    return 0


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