"""
Reporte de los bloques PENDIENTES de revision (REJECT con motivo 'REVISAR PROMPT').

Para cada uno te da lo que hace falta para decidir si se queda o se va:
  - que imagenes hay que mirar (los .png de Output/test_blocks/<pool>/)
  - el .txt de origen del WD14
  - el LoRA y la linea COMPLETA del personaje tal cual va en el prompt
  - los CHOQUES entre la linea del personaje y el bloque de pose

Un choque es cuando el personaje y el bloque se contradicen, y el bloque pierde.
Los casos que mas se han visto en las auditorias:
  - el LoRA trae ROPA horneada y el bloque pide 'nude'  (Yelan, Furina, Nilou)
  - el LoRA fue entrenado con un TAMANO de busto que el bloque no puede cambiar
  - el bloque trae 'solo' y a la vez pide 1boy/hetero (dos personas)
  - el personaje es de complexion pequena y el bloque empuja a 'breasts'

    python Engine/review_pendientes.py
    python Engine/review_pendientes.py --pool explicit
    python Engine/review_pendientes.py --block 7      # ver uno solo
"""
import argparse
import json
import re
import sys
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(Path(__file__).resolve().parent))
from prompt_builder import PromptBuilder  # noqa: E402
from wd14_blocks import (  # noqa: E402
    REJECT, DOWNWEIGHT, apply_weight, severity, strip_weights_of,
)

HARVEST = BASE_DIR / "Archivos" / "wildcards_harvest"
TEST_BLOCKS = BASE_DIR / "Output" / "test_blocks"
POOL_NAMES = ("desnudo", "vestido", "sfw", "spicy", "explicit",
              "sfw_vestido", "spicy_vestido", "explicit_vestido")

# --- patrones para detectar choques -------------------------------------------------
CLOTHING = re.compile(
    r"shirt|blouse|dress|skirt|panties|^bra$|.*_bra$|thighhighs|thigh_highs|"
    r"kneehighs|stockings|pantyhose|uniform|gloves|socks|shoes|boots|jacket|"
    r"coat|sweater|apron|lingerie|bikini|swimsuit|leotard|bodysuit|harness|"
    r"collar|^tie$|necktie|bow|ribbon|scarf|^belt$|jewelry|earrings|necklace|"
    r"neckwear|bracelet|anklet|wristband|armband|robe|kimono|armor|"
    r"^top$|top_|bottom$|legwear|underwear|^pants$|corset|garter|stocking|"
    r"camisole|cardigan|blazer|waistband|choker|^hat$|^cap$|^shoes$|"
    r"^boots$|arm_warmers|leg_warmers|tights|sock_pulled_up",
    re.I,
)
BUST_SIZE = re.compile(
    r"large_breasts|small_breasts|medium_breasts|huge_breasts|big_breasts|"
    r"giant_breasts|flat_chest|small_chest|medium_chest|large_chest|"
    r"breast_size|solop breasts|small_tits|large_tits",
    re.I,
)
NUDITY = re.compile(r"^nude$|completely_nude|topless|bottomless|undressed|naked", re.I)
PAIRS = re.compile(r"^1boy$|^hetero$|^couple$|^group_sex$|^twins$", re.I)
BREASTS = re.compile(r"^breasts$|^boobs$|^tits$|^bust$", re.I)
SMALL_FRAME = re.compile(
    r"^loli$|^child$|^shota$|^minigirl$|^petite$|^slim$|^slender$|"
    r"^small_breasts$|^medium_breasts$|^flat_chest$|^small_chest$",
    re.I,
)


def find_image(pool, fname):
    """Localiza el PNG reales. Prioridad: <pool>/, luego las carpetas de
    veredicto (APROBADO / DESCARTADO / REVISAR PROMPT) que creaste tu, porque
    moviste ahi los ficheros y la ruta del manifest ya no vale."""
    direct = TEST_BLOCKS / pool / fname
    if direct.exists():
        return direct
    for verdict in ("APROBADO", "DESCARTADO", "REVISAR PROMPT", "revisar"):
        cand = TEST_BLOCKS / pool / verdict / fname
        if cand.exists():
            return cand
    hits = list(TEST_BLOCKS.rglob(fname))
    return hits[0] if hits else direct


def load_manifests():
    """-> {nombre_txt_origen: [(pool, file, char, block, prompt), ...]}

    OJO: los manifestsViejos estan en la RAIZ de Output/test_blocks/ (antes de
    que el pool fuera subcarpeta) y los nuevos en la subcarpeta del pool. El
    pool de los viejos no viene en el manifest, asi que se deduce del tag que
    lleva el nombre del PNG. Sin esto saldrian rutas tipo 'test_blocks/test_blocks'.
    """
    out = {}
    for m in sorted(TEST_BLOCKS.rglob("manifest_*.json")):
        folder = m.parent.name
        try:
            data = json.loads(m.read_text(encoding="utf-8-sig"))
        except Exception:
            continue
        for e in data:
            src = e.get("original_txt") or ""
            name = Path(src).name
            if not name:
                continue
            fname = e.get("file", "")
            pool = e.get("pool")
            if not pool or pool not in POOL_NAMES:
                # manifest viejo: deducir por el tag del nombre del PNG
                pool = next((p for p in POOL_NAMES if f"_{p}_" in fname),
                            None) or folder
            out.setdefault(name, []).append({
                "pool": pool,
                "file": fname,
                "path": find_image(pool, fname),
                "char": e.get("char", ""),
                "block": e.get("block", ""),
                "prompt": e.get("prompt", ""),
            })
    return out


def pending_rejects():
    """Los REJECT cuyo motivo empieza por 'REVISAR'."""
    return {name: why for name, why in REJECT.items()
            if why.upper().startswith("REVISAR")}


def clashes(char_line, block):
    """Tags del personaje y del bloque que se contradicen. -> [(motivo, detalle)]"""
    ctags = [t.strip() for t in char_line.split(",") if t.strip()]
    cl = [t.lower() for t in ctags]
    bl = [t.lower() for t in strip_weights_of(block).split(",") if t.strip()]

    out = []
    # 1. el LoRA trae ropa y el bloque pide desnuda
    wear = [t for t in ctags if CLOTHING.search(t) and not t.startswith("<lora")]
    nude = [t for t in bl if NUDITY.search(t)]
    if wear and nude:
        out.append(("ropa horneada vs desnuda",
                    f"personaje trae {', '.join(wear[:4])} | bloque pide {', '.join(nude)}"))
    # 2. tamano de busto en el personaje que el bloque no puede cambiar
    bs = [t for t in ctags if BUST_SIZE.search(t)]
    if bs:
        out.append(("tamano de busto fijo en el LoRA",
                    f"{', '.join(bs)} - el bloque no lo puede cambiar"))
    # 3. el bloque dice solo y a la vez dos personas
    if any(PAIRS.match(t) for t in bl):
        solo = "solo" in bl
        if solo:
            out.append(("'solo' dentro de un bloque de dos personas",
                        "el MASTER tambien pone 1girl,solo -> el 'solo' del bloque "
                        "puede borrar al hombre"))
    # 4. el bloque empuja 'breasts' en un personaje de complexion pequena
    if any(BREASTS.match(t) for t in bl):
        small = [t for t in ctags if SMALL_FRAME.search(t)]
        if small:
            out.append(("bloque con 'breasts' en personaje pequeno",
                        f"personaje: {', '.join(small)} | bloque: breasts"))
    # 5. el LoRA no tiene tallaje y aun asi hay conflicto de cuerpo
    return out


def main():
    p = argparse.ArgumentParser(description="Detalle de los bloques pendientes")
    p.add_argument("--pool", default=None, help="filtra por pool de la imagen")
    p.add_argument("--block", type=int, default=None, help="solo el .txt de origen n")
    args = p.parse_args()

    mans = load_manifests()
    pend = pending_rejects()
    if not pend:
        print("[!] No hay bloques pendientes de revisar")
        return

    B = PromptBuilder(str(BASE_DIR))
    n = 0
    for i, (name, why) in enumerate(sorted(pend.items())):
        if args.block is not None and i != args.block:
            continue
        n += 1
        hits = mans.get(name, [])
        if args.pool:
            hits = [h for h in hits if h["pool"] == args.pool]

        print("=" * 78)
        print(f"[{i}] {name}")
        print(f"    motivo : {why}")
        if not hits:
            print("    (sin imagenes generadas todavia: no hay manifest con este .txt)")
            continue
        for h in hits[:3]:
            char = B.get_character(h["char"])
            line = char["base"] if char else "(personaje no encontrado)"
            path = h["path"]
            rel = path.relative_to(BASE_DIR.parent) if path.exists() else path
            mark = "" if path.exists() else "  [NO ENCONTRADO]"
            print(f"\n    --- MIRAR: {rel}{mark}")
            print(f"    personaje: {h['char']}")
            print(f"    LoRA+tags: {line}")
            print(f"    bloque   : {h['block'][:150]}")
            cl = clashes(line, h["block"])
            if cl:
                print("    CHOQUES:")
                for mot, det in cl:
                    print(f"      ! {mot}")
                    print(f"        {det}")
            else:
                print("    CHOQUES: ninguno detectado por regla")
    print("=" * 78)
    print(f"{n} bloques pendientes listados")


if __name__ == "__main__":
    main()