#!/usr/bin/env python3
"""Bank batch-329: Rule 287 complementary artefact/tool non-singular nonpast perception."""

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-329"
NOW = dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
PROCESS_LOG = "process_logs/batch-329-visible-artefact-tool-nonsingular-complementary-nonpast-pronoun-perception-after-rule286-process.md"

TARGETS = [
    ("badur", "hook", "hook", "Dictionary lines 256-268 give badur as noun 'hook', with definition 'hook and line'. Rule 287 selects visible hook reference only and leaves fishing action closed."),
    ("balji", "dilly bag", "dilly.bag", "Dictionary lines 963-974 give balji as noun 'bag/dilly bag', specifically a woman's dilly bag made from grass or black palm. Rule 287 selects visible bag reference only and leaves making/material rows closed."),
    ("balur", "spear thrower", "spear.thrower", "Dictionary lines 1154-1170 give balur as noun 'spear thrower' and mark a duplicate empty entry as the same noun. Rule 287 selects visible woomera/spear-thrower reference only."),
    ("kaljan", "outrigger canoe", "outrigger.canoe", "Dictionary lines 12560-12568 give kaljan as noun 'outrigger canoe'. Rule 287 selects visible canoe reference only and leaves travel/rowing/use rows closed."),
    ("dajal", "fighting spear", "fighting.spear", "Dictionary lines 4499-4509 give dajal as noun 'spear', specifically fighting spear. Rule 287 selects visible spear reference only and leaves fighting/spearing action closed."),
    ("dakilji", "cup", "cup", "Dictionary lines 4665-4676 give dakilji as noun 'cup', also pannikin only rarely used now. Rule 287 selects visible cup reference only."),
    ("jidi", "torch", "torch", "Dictionary lines 8842-8851 give jidi as noun 'torch', a paperbark-tree-bark torch. Rule 287 selects visible torch reference only and leaves fire/burning/making rows closed."),
    ("jindal", "three-pronged spear", "three.pronged.spear", "Dictionary lines 9423-9433 give jindal as noun 'spear with three prongs' and warn it is homophonous with jindal 'bite'. Rule 287 selects the noun only and leaves the biting verb closed."),
    ("jundi", "pumice stone", "pumice.stone", "Dictionary lines 10846-10857 give jundi as noun 'pumice stone/carborundum stone'. Rule 287 selects visible stone/tool reference only and leaves sharpening/rubbing use closed."),
    ("karra", "rope", "rope", "Dictionary lines 13534-13544 give karra as noun 'rope, cord, string', with synonym ngarra. Rule 287 selects visible rope reference only and leaves tying/use rows closed."),
]

FRAME_SPECS = [
    ("ngali", "1du.EXCL.NOM", "We two (not you)", "yaluy", "here", "1du_excl_here"),
    ("ngaliny", "1du.INCL.NOM", "You and I", "yinyay", "there", "1du_incl_there"),
    ("yubal", "2du.NOM", "You two", "yaluy", "here", "2du_here"),
    ("yurra", "2pl.NOM", "You all", "yinyay", "there", "2pl_there"),
    ("jana", "3pl.NOM", "They", "yaluy", "here", "3pl_here"),
]

DENY_KUKU = {
    "birungubay", "biruwybay", "birubay", "badurriji", "baluriji", "dama-l", "dama-ny",
    "damal", "damany", "daman", "damba", "jindal-ny", "jindalny", "jindal-ji", "jarrjal",
    "jarrkal", "milbayarr", "mingu", "marrakan", "knife", "pipe", "blanket", "hat",
    "ngalingan", "ngalinyan", "yubalan", "yurrangan", "janangan", "ngalininy", "ngalinyiny",
    "yubaliny", "yurraniny", "jananiny", "ngalinka", "ngalinyanka", "yubalanka", "yurranka",
    "jananka", "ngalinda", "ngalinyanda", "yubalanda", "yurranda", "jananda", "ngalinji",
    "ngalinyanji", "yubalanji", "yurranji", "jananji", "ngalinga", "ngalinyanga", "yubalanga",
    "yurranga", "jananga", "nyajin", "nyaji-ny", "nyaji-nka", "nyaji-l-nyaji", "yinyangka",
    "yinyayngka", "yinyamba", "yinyamuny", "yalungku", "yaluyngku", "yaluymbu", "yaluyundu",
    "yalamuny",
}
DENY_ENGLISH = (
    "fishing", "fish with", "row", "paddle with", "oar", "made", "make", "used", "use",
    "throwing", "threw", "thrown", "fighting with", "fight with", "kill", "killed", "bite",
    "bit", "bad news", "message", "euphemism", "penis", "fire", "burn", "burned", "cooking",
    "travel", "route", "went in", "place", "story", "ceremony", "mortuary", "coffin",
)

SUPPORT_EVIDENCE = (
    "Patz lines 2577-2603 source bare non-singular nominative pronouns; lines 2674-2686 source non-singular "
    "contrasts; lines 2838-2845 source yaluy/yinyay; and lines 3043-3049 source nyaji-l 'see'."
)


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


def build_rows() -> list[tuple]:
    rows: list[tuple] = []
    seq = 1
    for pronoun, pronoun_gloss, subject, demo, demo_english, frame_band in FRAME_SPECS:
        for word, english_head, analysis_head, evidence in TARGETS:
            english = f"{subject} see the {english_head} {demo_english}."
            kuku = f"{pronoun.capitalize()} {word} {demo} nyajil."
            analysis = f"{pronoun} {word} {demo} nyajil | {pronoun_gloss} {analysis_head} {demo_english} see.NPST"
            frame = f"r287_{frame_band}_artefact_tool_see_{word}"
            rows.append((seq, english, kuku, analysis, frame, 3, js([pronoun, word, demo, "nyajil"]), js([]), f"{evidence} {SUPPORT_EVIDENCE}", "0.86"))
            seq += 1
    return rows


ROWS = build_rows()
TARGET_ORDER = [word for word, _english_head, _analysis_head, _evidence in TARGETS]


def scan_static() -> list[str]:
    issues: list[str] = []
    if len(ROWS) != 50:
        issues.append(f"row count {len(ROWS)} != 50")
    if [r[0] for r in ROWS] != list(range(1, 51)):
        issues.append("sequence is not 1..50")
    kukus = [r[2] for r in ROWS]
    if len(set(kukus)) != len(kukus):
        issues.append("duplicate Kuku surface inside batch")
    for seq, english, kuku, _analysis, frame, _tier, words_json, loans_json, _evidence, _confidence in ROWS:
        words = json.loads(words_json)
        loans = json.loads(loans_json)
        lower_kuku = kuku.lower()
        lower_english = english.lower()
        if loans:
            issues.append(f"{seq}: loans used {loans}")
        denied = sorted({w for w in DENY_KUKU if re.search(rf"(^|\\s){re.escape(w)}([\\s\\.]|$)", lower_kuku)})
        if denied:
            issues.append(f"{seq}: denied Kuku forms {denied}")
        denied_english = [
            term for term in DENY_ENGLISH
            if re.search(rf"\b{re.escape(term.strip())}\b", lower_english)
        ]
        if denied_english:
            issues.append(f"{seq}: denied English drift {denied_english}")
        target_hits = [w for w in TARGET_ORDER if w in words]
        if len(target_hits) != 1:
            issues.append(f"{seq}: target hits {target_hits}")
        if len(words) != 4:
            issues.append(f"{seq}: expected 4 words, got {words}")
        if "nyajil" not in words or not lower_kuku.endswith(" nyajil."):
            issues.append(f"{seq}: missing final nyajil")
        if len([w for w in ("yaluy", "yinyay") if w in words]) != 1:
            issues.append(f"{seq}: demonstrative problem {words}")
        expected_pronoun, _pronoun_gloss, _subject, expected_demo, _demo_english, _frame_band = FRAME_SPECS[(seq - 1) // 10]
        if words[0] != expected_pronoun:
            issues.append(f"{seq}: expected pronoun {expected_pronoun}, got {words[0]}")
        if words[2] != expected_demo:
            issues.append(f"{seq}: expected demonstrative {expected_demo}, got {words[2]}")
        if words[1] not in TARGET_ORDER:
            issues.append(f"{seq}: word 2 is not a target {words[1]}")
        if words[3] != "nyajil":
            issues.append(f"{seq}: word 4 is not nyajil {words[3]}")
        if not frame.startswith("r287_"):
            issues.append(f"{seq}: frame not r287 {frame}")
    return issues


def count_words(db: sqlite3.Connection, words: list[str]) -> dict[str, int]:
    return {
        word: db.execute(
            """
            SELECT COUNT(*)
            FROM sentences s
            WHERE s.status IN ('accepted','revised')
              AND EXISTS (SELECT 1 FROM json_each(s.words_used) WHERE value=?)
            """,
            (word,),
        ).fetchone()[0]
        for word in words
    }


def main() -> int:
    issues = scan_static()
    if issues:
        for issue in issues:
            print(issue)
        raise SystemExit(1)

    db = sqlite3.connect(DB)
    db.execute("PRAGMA foreign_keys = ON")
    db.execute("BEGIN")
    ensure_schema(db)
    if db.execute("SELECT 1 FROM batches WHERE label=?", (LABEL,)).fetchone():
        raise SystemExit(f"{LABEL} already exists")
    collisions = []
    for _seq, _english, kuku, *_rest in ROWS:
        existing = db.execute(
            """
            SELECT b.label, s.seq, s.status
            FROM sentences s
            JOIN batches b ON b.id=s.batch_id
            WHERE lower(s.kuku)=lower(?) AND s.status!='rejected'
            """,
            (kuku,),
        ).fetchall()
        if existing:
            collisions.append((kuku, existing))
    if collisions:
        raise SystemExit(f"duplicate preflight failed: {collisions}")

    target_counts = count_words(db, TARGET_ORDER)
    support_counts = count_words(db, ["ngali", "ngaliny", "yubal", "yurra", "jana", "yaluy", "yinyay", "nyajil"])
    verified_before, words_before = db.execute(
        """
        SELECT COUNT(*), SUM(LENGTH(kuku)-LENGTH(REPLACE(kuku,' ',''))+1)
        FROM sentences
        WHERE status IN ('accepted','revised')
        """
    ).fetchone()

    notes = (
        "VISIBLE ARTEFACT/TOOL NON-SINGULAR COMPLEMENTARY NONPAST PRONOUN PERCEPTION SWEEP AFTER RULE 286 "
        "(Rule 287). 50 under-review rows over badur hook, balji dilly bag, balur spear thrower, kaljan "
        "outrigger canoe, dajal fighting spear, dakilji cup, jidi torch, jindal three-pronged spear, jundi "
        "pumice stone, and karra rope. SUPPORT: bare nominative ngali, ngaliny, yubal, yurra, and jana, plus "
        "yaluy/yinyay and nyajil. FRAMES: Ngali TARGET yaluy nyajil, Ngaliny TARGET yinyay nyajil, Yubal TARGET "
        "yaluy nyajil, Yurra TARGET yinyay nyajil, and Jana TARGET yaluy nyajil. Only bare visible object nonpast "
        "perception/reference rows are opened; fishing, making, material-source, canoe travel, rowing/paddling, "
        "spear throwing, fighting, biting, sharpening/rubbing, tying, fire/burning, post-contact loan objects, "
        "target case, pronoun case, demonstrative case, compounds, productive reduplication, past/purposive/"
        "reduplicated perception drift, and support inflection remain closed."
    )
    cur = db.execute(
        "INSERT INTO batches(label, cheatsheet_version, guide_version, notes, created_at) VALUES(?,?,?,?,?)",
        (LABEL, "Rule 287 under review", "operator-guide-2026-07-09", notes, NOW),
    )
    batch_id = cur.lastrowid
    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", NOW, NOW,
            )
            for seq, english, kuku, analysis, frame, tier, words_used, loans_used, evidence, confidence in ROWS
        ],
    )
    record_event(db, batch_label=LABEL, phase="source", event_type="source-readback", actor="claude-main", detail="Reread dictionary source lines for visible artefact/tool targets and Patz pronoun, non-singular, demonstrative, and nyaji-l nonpast support before authoring Rule 287.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="planning", event_type="construction-consolidation-scope", actor="claude-main", detail="Selected complementary nonpast consolidation of the Rule 286 visible artefact/tool field: Ngali/Yubal/Jana move to yaluy and Ngaliny/Yurra move to yinyay. Birungubay remains excluded.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="coverage-audit", actor="claude-main", detail=f"Token-aware accepted/revised coverage before banking from words_used JSON: targets={target_counts}; support={support_counts}.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="duplicate-preflight", actor="claude-main", detail="Final 50 Rule 287 complementary nonpast surfaces over the Rule 286 artefact/tool target set returned no accepted, revised, or under-review collisions.", artifact_path=PROCESS_LOG, created_at=NOW)
    record_event(db, batch_label=LABEL, phase="preflight", event_type="static-guard-pass", actor="claude-main", detail="Static guard passed: 50 rows, one target each, complementary bare non-singular nominative pronoun/demonstrative bands, final nyajil, no loans, no denied Kuku forms, no fishing/making/canoe-travel/spear-action/fire/use/case/compound drift.", command="python3 -m py_compile batch_scripts/bank_329.py && python3 -c \"import sys; sys.path.insert(0,'batch_scripts'); import bank_329; print(bank_329.scan_static())\"", output_excerpt="issues 0", artifact_path="batch_scripts/bank_329.py", created_at=NOW)
    record_event(db, batch_label=LABEL, phase="bank", event_type="bank-success", actor="claude-main", detail=f"Banked 50 Rule 287 rows as under_review. Verified total remains {verified_before}; Kuku word count remains {words_before}.", command="python3 batch_scripts/bank_329.py", output_excerpt=f"banked batch-329: 50 rows under_review; verified_before={verified_before}; kuku_words_before={words_before}", artifact_path=PROCESS_LOG, created_at=NOW)
    db.commit()
    print("banked batch-329: 50 rows under_review")
    return 0


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