"""
PLUMLOOM ENGINE - Batch Progression Generator v2
Genera secuencias de progresión NSFW (7 pasos) para múltiples personajes.
Estructura de prompt por paso:
  Step 1: MASTER + char + OUTFIT + POSE + CAMERA + SCENE
  Step 2: MASTER + char + OUTFIT + UNDRESS + POSE + CAMERA + SCENE
  Step 3: MASTER + char + UNDERWEAR + POSE + CAMERA + SCENE
  Step 4: MASTER + char + nude + PRESENTING + CAMERA + SCENE
  Step 5: MASTER + char + SEXUAL + CAMERA + SCENE
  Step 6: MASTER + char + EXPLICIT + MALE + CAMERA + SCENE
  Step 7: MASTER + char + MALE + CLIMAX(+POSE) + CAMERA + SCENE
"""
import requests
import base64
import random
import time
import re
import os
from pathlib import Path

BASE_DIR = Path(__file__).parent.parent
FORGE_URL = "http://127.0.0.1:7860"
OUTPUT_DIR = BASE_DIR / "Output" / "batch_progression_v2"
WILDCARD_DIR = BASE_DIR / "Archivos" / "progression_wildcards"
CHAR_FILE = BASE_DIR / "Archivos" / "personajes.txt"

MASTER = (
    "masterpiece, best quality, absurdres, highres, score_9, score_8_up, "
    "<lora:IFL_v1.0_IL:0.7> <lora:add-detail-xl:0.5> "
    "<lora:blackwhiplash(bwl)-guy90-Illust-Lorav3:0.6> blaash, "
    "<lora:scara2b_ill_v1.00:0.7> scara2b-style, "
    "<lora:Add_color_ILXLv1:3>,"
)

MALE_TAGS = (
    "1boy, faceless male, no face, black hair, short hair, "
    "muscular body, broad shoulders, large hands, masculine physique, "
    "nude male, larger male, taller dominant"
)

NEG = (
    "lowres, bad anatomy, bad hands, error, missing fingers, extra digit, "
    "fewer digits, cropped, worst quality, low quality, normal quality, "
    "jpeg artifacts, signature, watermark, username, blurry, artist name, "
    "bad feet, multiple views, splits, 2girls, extra people, "
    "duplicate_character, cloned_character, ugly, deformed, disfigured, "
    "mutation, mutated, extra_limbs, fused_fingers, long_neck, cross-eyed, "
    "censored, mosaic censoring, bar censor, "
    "mirror, reflection, mirrored, glass_reflection, reflective_surface"
)

STEP_NAMES = [
    "01_outfit",
    "02_undress",
    "03_underwear",
    "04_presenting",
    "05_sexual",
    "06_explicit",
    "07_climax",
]

STEP_LABELS = [
    "OUTFIT",
    "UNDRESS",
    "UNDERWEAR",
    "PRESENTING",
    "SEXUAL",
    "EXPLICIT",
    "CLIMAX",
]


def load_wildcards():
    wildcards = {}
    for filename in [
        "step1_outfit.txt",
        "step2_undress.txt",
        "step3_underwear.txt",
        "extra_poses.txt",
        "step4_presenting.txt",
        "step5_sexual.txt",
        "step6_explicit.txt",
        "step7_climax.txt",
        "cameras.txt",
        "scenes.txt",
    ]:
        filepath = WILDCARD_DIR / filename
        if filepath.exists():
            with open(filepath, "r", encoding="utf-8") as f:
                lines = [l.strip() for l in f if l.strip() and not l.startswith("#")]
            key = filename.replace(".txt", "")
            wildcards[key] = lines
    # Las poses extra sirven a los pasos 1, 2 y 3. Antes solo llegaban al 3:
    # el codigo pedia la clave 'step2_extra_poses' y el fichero se llamaba
    # step3_extra_poses.txt, asi que los pasos 1 y 2 salian sin pose.
    if "extra_poses" in wildcards:
        for step in ("step1_extra_poses", "step2_extra_poses", "step3_extra_poses"):
            wildcards[step] = wildcards["extra_poses"]
    return wildcards


def load_characters(count=None):
    with open(CHAR_FILE, "r", encoding="utf-8") as f:
        lines = [l.strip() for l in f if l.strip()]
    if count:
        return random.sample(lines, min(count, len(lines)))
    return lines


def extract_char_name(char_line):
    match = re.search(r">([^,]+)", char_line)
    if match:
        raw = match.group(1).strip()
        return re.sub(r"[^a-zA-Z0-9]", "_", raw)[:20]
    return "unknown"


def pick(wildcards, key):
    """Pick random from wildcard, return empty string if not found."""
    lst = wildcards.get(key, [])
    return random.choice(lst) if lst else ""


def build_step_prompt(step_num, char_line, wildcards, camera, scene):
    """Build prompt according to step-specific structure."""
    outfit = pick(wildcards, "step1_outfit")
    undress = pick(wildcards, "step2_undress")
    pose_undress = pick(wildcards, "step2_extra_poses")
    underwear = pick(wildcards, "step3_underwear")
    pose_underwear = pick(wildcards, "step3_extra_poses")
    presenting = pick(wildcards, "step4_presenting")
    sexual = pick(wildcards, "step5_sexual")
    explicit = pick(wildcards, "step6_explicit")
    climax = pick(wildcards, "step7_climax")

    if step_num == 1:
        # MASTER + 1girl,solo + char + OUTFIT + POSE(step2_extra) + CAMERA + SCENE
        prompt = f"{MASTER} 1girl, solo, {char_line}, age up, {outfit}, {pose_undress}, {camera}, {scene}"

    elif step_num == 2:
        # MASTER + 1girl,solo + char + OUTFIT + UNDRESS + POSE(step2_extra) + CAMERA + SCENE
        prompt = f"{MASTER} 1girl, solo, {char_line}, age up, {outfit}, {undress}, {pose_undress}, {camera}, {scene}"

    elif step_num == 3:
        # MASTER + 1girl,solo + char + UNDERWEAR + POSE(step3_extra) + CAMERA + SCENE
        prompt = f"{MASTER} 1girl, solo, {char_line}, age up, {underwear}, {pose_underwear}, {camera}, {scene}"

    elif step_num == 4:
        # MASTER + 1girl,solo + char + nude,naked,nipples,pussy + PRESENTING + CAMERA + SCENE
        prompt = f"{MASTER} 1girl, solo, {char_line}, age up, nude, naked, nipples, pussy, {presenting}, {camera}, {scene}"

    elif step_num == 5:
        # MASTER + 1girl,solo + char + SEXUAL(pose included) + CAMERA + SCENE
        prompt = f"{MASTER} 1girl, solo, {char_line}, age up, {sexual}, {camera}, {scene}"

    elif step_num == 6:
        # MASTER + 1girl + char + EXPLICIT + MALE + CAMERA + SCENE
        prompt = f"{MASTER} 1girl, {char_line}, age up, {explicit}, {MALE_TAGS}, {camera}, {scene}"

    elif step_num == 7:
        # MASTER + 1girl + char + nude + MALE + CLIMAX(+ pose from step6) + CAMERA + SCENE
        prompt = f"{MASTER} 1girl, {char_line}, age up, nude, naked, nipples, pussy, {MALE_TAGS}, {climax}, {explicit}, {camera}, {scene}"

    return prompt


def generate_image(prompt, outpath):
    payload = {
        "prompt": prompt,
        "negative_prompt": NEG,
        "width": 832,
        "height": 1216,
        "steps": 28,
        "cfg_scale": 5,
        "sampler_name": "DPM++ 2M Karras",
        "batch_size": 1,
        "n_iter": 1,
        "seed": -1,
        "clip_skip": 2,
    }

    try:
        resp = requests.post(f"{FORGE_URL}/sdapi/v1/txt2img", json=payload, timeout=300)
        if resp.status_code == 200:
            img = base64.b64decode(resp.json()["images"][0])
            with open(outpath, "wb") as f:
                f.write(img)
            return True, len(img) // 1024
        return False, resp.status_code
    except Exception as e:
        return False, str(e)


def run_batch(char_count=3, dry_run=False):
    wildcards = load_wildcards()
    characters = load_characters(char_count)

    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

    print("=" * 55)
    print(f"  PROGRESSION BATCH v2 - {len(characters)} chars x 7 steps")
    print("=" * 55)

    total = len(characters) * 7
    done = 0

    for ci, char_line in enumerate(characters):
        char_name = extract_char_name(char_line)
        scene = pick(wildcards, "scenes")

        print(f"\n--- {char_name} ---")

        for si in range(7):
            step_num = si + 1
            camera = pick(wildcards, "cameras")
            prompt = build_step_prompt(step_num, char_line, wildcards, camera, scene)

            filename = f"{char_name}_{STEP_NAMES[si]}.png"
            outpath = OUTPUT_DIR / filename

            done += 1
            print(f"  [{done}/{total}] {STEP_LABELS[si]}")

            if dry_run:
                print(f"    [DRY] {filename}")
                print(f"    {prompt[:120]}...")
            else:
                ok, result = generate_image(prompt, outpath)
                if ok:
                    print(f"    [OK] {result}KB")
                else:
                    print(f"    [FAIL] {result}")
                time.sleep(2)

    print("\n" + "=" * 55)
    print(f"  COMPLETADO - {done} images in {OUTPUT_DIR}")
    print("=" * 55)


if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description="Progression Batch v2")
    parser.add_argument("--chars", type=int, default=3, help="Number of characters")
    parser.add_argument("--dry-run", action="store_true", help="Print prompts only")
    args = parser.parse_args()

    run_batch(char_count=args.chars, dry_run=args.dry_run)
