"""
Saneamiento de las lineas del banco de poses: quita ropa/accesorios y meta.

El banco se cosecho de imagenes reales, asi que cada linea arrastra tags de la
prenda y del atrezo de esa foto (`floral_print`, `beads`, `ring`, `censored`).
En un pack esos tags pelean con el outfit del tema y meten joyas o ropa que no
toca. Aqui se dejan solo pose / estado / expresion / cuerpo / camara; la escena
la pone el tema (y `strip_scene_tags` la limpia en el ensamblado).

    python Engine/clean_bank_lines.py             # dry-run
    python Engine/clean_bank_lines.py --apply
    python Engine/clean_bank_lines.py --apply --blacklist pb0005

Idempotente: si ya no queda nada que quitar, no reescribe.
"""
import argparse
import re
import shutil
import sys
from datetime import datetime

import pose_bank
from wd14_harvest import DROP_META, OUTFIT

sys.stdout.reconfigure(encoding="utf-8", errors="backslashreplace")

# Tags de ropa/atrezo que el regex OUTFIT no cubre (o que son estado de la
# prenda y no queremos que el banco imponga).
DROP_EXTRA = {
    "beads", "ring", "censored", "mosaic_censoring",
    "covered_navel", "covered_nipples", "on_bed",
    "mixed-sex_bathing", "same-sex_bathing", "looking_at_another",
}

_CLOTHES = re.compile(r"clothes|clothing", re.I)
_WEIGHT = re.compile(r"^\(\s*([^():]*?)\s*:\s*[\d.]+\s*\)$")


def tag_key(token: str) -> str:
    t = (token or "").strip()
    m = _WEIGHT.match(t)
    if m:
        t = m.group(1)
    return t.strip().lower()


def is_noise(token: str) -> bool:
    key = tag_key(token)
    return bool(key) and (
        key in DROP_EXTRA or key in DROP_META
        or bool(OUTFIT.search(key)) or bool(_CLOTHES.search(key))
    )


def clean_line(line: str):
    kept, dropped = [], []
    for token in (line or "").split(","):
        token = token.strip()
        if not token:
            continue
        (dropped if is_noise(token) else kept).append(token)
    return ", ".join(kept), dropped


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--apply", action="store_true", help="escribe los cambios")
    ap.add_argument("--blacklist", default=None, metavar="ID[,ID]",
                    help="blacklist inmediata de poses del banco")
    args = ap.parse_args()

    bank = pose_bank.load_bank()
    if not bank:
        print(f"[!] banco vacio: {pose_bank.BANK_FILE}")
        return 1

    changed = 0
    for entry in bank:
        original = entry.get("pose_line", "")
        cleaned, dropped = clean_line(original)
        if dropped:
            changed += 1
            print(f"{entry.get('id')}  -{len(dropped)}: {', '.join(dropped)}")
            print(f"    antes: {original}")
            print(f"    despues: {cleaned}")
            if args.apply:
                entry["pose_line"] = cleaned

    print(f"\n[{'+' if args.apply else '-'}] {changed}/{len(bank)} lineas con ruido")

    blacklist_ids = [x.strip() for x in (args.blacklist or "").split(",") if x.strip()]
    if blacklist_ids:
        avoid = pose_bank.load_avoid()
        by_id = {e.get("id"): e for e in bank}
        motivo = "saneamiento 2026-10-20: tapa entorno / deforma"
        for pid in blacklist_ids:
            entry = by_id.get(pid)
            if not entry:
                print(f"[!] {pid} no existe en el banco")
                continue
            key = pose_bank.normalize_key(entry.get("pose_line", ""))
            row = avoid.get(key) or {"key": key, "fallos": 0, "ultima": ""}
            row.update({
                "motivo": motivo,
                "fallos": max(3, int(row.get("fallos", 0))),
                "blacklist": True,
                "ultima": datetime.now().strftime("%Y-%m-%d"),
            })
            avoid[key] = row
            print(f"[+] blacklist {pid}: {motivo}")
        if args.apply and blacklist_ids:
            pose_bank.save_avoid(avoid)

    if args.apply and changed:
        stamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        backup = pose_bank.BANK_DIR / f"_backup_{stamp}"
        backup.mkdir(parents=True, exist_ok=True)
        shutil.copy2(pose_bank.BANK_FILE, backup / pose_bank.BANK_FILE.name)
        if pose_bank.AVOID_FILE.exists():
            shutil.copy2(pose_bank.AVOID_FILE, backup / pose_bank.AVOID_FILE.name)
        pose_bank.save_bank(bank)
        print(f"[+] banco escrito; backup en {backup}")
    elif args.apply and blacklist_ids:
        # sin cambios de linea, pero hay que persistir el avoid
        print("[+] avoid escrito")
    elif not args.apply:
        print("[-] dry-run: usa --apply para escribir")

    return 0


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