#!/usr/bin/env python3
"""
Iterative Auto-Correction Loop
Detects failing frames → Enhances prompt → Regenerates → Re-checks → Repeats until pass
"""

import requests
import time
import os
from PIL import Image
import imagehash

COMFYUI_URL = "https://bvlnsjffdkgdkt-8188.proxy.runpod.net"
GENERATED_DIR = "/mnt/c/Users/fbmor/broken-spire-comparison/generated"
REFERENCES_DIR = "/mnt/c/Users/fbmor/broken-spire-comparison/references"

# Base character profiles with MAXIMUM detail
CHARACTER_DETAILS = {
    "Ash": {
        "reference": "Ash.png",
        "base_prompt": "masterpiece, best quality, very aesthetic, absurdres, 1boy, solo, young adult, early twenties, blond hair, medium length, messy, falling across forehead, pale skin, slim athletic body, dancer build, large expressive anime eyes, blue eyes, strong cheekbones, soft androgynous face, black tribal tattoos on arm and shoulder, sharp organic claw flame pattern tattoos, detailed face, detailed eyes, looking at viewer, dark background, dramatic lighting",
        "negative": "lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, artist name, different face, wrong hair color, missing tattoos"
    },
    "Far-Future Ash": {
        "reference": "Far-Future Ash.png",
        "base_prompt": "masterpiece, best quality, very aesthetic, absurdres, 1man, solo, mature, late thirties, long white hair flowing, pale skin, glowing red eyes, intense expression, terrifying, dark aura, menacing smile, slim muscular body, dark armor with glowing red hexcore patterns, multiple arms with weapons, floating, surrounded by dark energy, skull decorations, god of death, apocalyptic background, destroyed city, red sky, horror, scary, evil, brutal, villain",
        "negative": "lowres, bad anatomy, bad hands, text, error, missing fingers, cropped, worst quality, low quality, blurry, artist name, young, blond, wrong eye color, missing armor, missing weapons"
    },
    "Everly": {
        "reference": "Everly.png",
        "base_prompt": "masterpiece, best quality, very aesthetic, absurdres, 1woman, solo, mid thirties, short dark hair, sharp features, intense eyes, dark makeup, fit athletic body, military uniform, tactical gear, multiple weapons, dark expression, confident pose, hand on weapon, scars visible, dramatic lighting, cinematic, from below, powerful, dangerous, cyberpunk, dark sci-fi, detailed face, detailed eyes",
        "negative": "lowres, bad anatomy, bad hands, text, error, missing fingers, cropped, worst quality, low quality, blurry, artist name, wrong hair color, wrong age, missing uniform, missing weapons, missing scars"
    },
    "Éva Moreau": {
        "reference": "Éva Moreau.png",
        "base_prompt": "masterpiece, best quality, very aesthetic, absurdres, 1woman, solo, french woman, professional, average build, shoulder length brown hair, soft features, intelligent eyes, focused expression, lab coat, medical clothing, surgical gloves, sterile environment, blue medical lights, detailed face, cyberpunk clinic, dramatic lighting, cinematic",
        "negative": "lowres, bad anatomy, bad hands, text, error, missing fingers, cropped, worst quality, low quality, blurry, artist name, wrong hair color, wrong hair length, missing lab coat, missing gloves"
    },
    "Nova": {
        "reference": "Nova Human.png",
        "base_prompt": "masterpiece, best quality, very aesthetic, absurdres, 1woman, solo, young adult, fighter build, long flowing red hair, fierce expression, playful smile, combat gear, multiple stylish weapons, dynamic pose, action pose, battle stance, glowing energy weapons, dramatic lighting, cinematic, anime action, intense, stylish, war scene, detailed face, detailed eyes",
        "negative": "lowres, bad anatomy, bad hands, text, error, missing fingers, cropped, worst quality, low quality, blurry, artist name, wrong hair color, wrong hair style, missing weapons, wrong outfit"
    },
    "Violet": {
        "reference": "Violet Devil.png",
        "base_prompt": "masterpiece, best quality, very aesthetic, absurdres, 1woman, solo, elegant, long purple hair, red devil horns, glowing red eyes, devil form, dark wings, elegant dangerous pose, hand with claws, beautiful and terrifying, dark aura, crimson energy, night background, dramatic lighting, horror, beautiful, duality, dark fantasy, detailed face",
        "negative": "lowres, bad anatomy, bad hands, text, error, missing fingers, cropped, worst quality, low quality, blurry, artist name, wrong hair color, missing horns, missing wings, human form instead of devil"
    },
    "Lin Weishan": {
        "reference": "Lin Weishan.png",
        "base_prompt": "masterpiece, best quality, very aesthetic, absurdres, 1woman, solo, asian woman, fighter athletic build, long black hair, determined expression, combat clothing, martial arts pose, detailed face, cyberpunk, action, intense, dramatic lighting",
        "negative": "lowres, bad anatomy, bad hands, text, error, missing fingers, cropped, worst quality, low quality, blurry, artist name, wrong ethnicity, wrong hair color, wrong outfit"
    },
    "TC-23": {
        "reference": "TC-23.png",
        "base_prompt": "masterpiece, best quality, very aesthetic, absurdres, 1woman, solo, mechanical augmented body, cybernetic augments visible, glowing cybernetic eyes, esper powers, glowing energy surrounding, futuristic outfit, robot parts, detailed machinery, dramatic lighting, sci-fi, cyberpunk, esper, mechanical, detailed face",
        "negative": "lowres, bad anatomy, bad hands, text, error, missing fingers, cropped, worst quality, low quality, blurry, artist name, wrong augments, missing cybernetic eyes, human instead of mechanical"
    },
    "Jonas": {
        "reference": "Jonas.png",
        "base_prompt": "masterpiece, best quality, very aesthetic, absurdres, 1man, solo, large muscular build, imposing, scarred face, short brown hair, warm smile, kind eyes, weathered skin, military combat gear, detailed face, dramatic lighting, cinematic",
        "negative": "lowres, bad anatomy, bad hands, text, error, missing fingers, cropped, worst quality, low quality, blurry, artist name, wrong build, missing scars, wrong hair color, young instead of weathered"
    }
}

def get_hash_similarity(img1_path, img2_path):
    """Get perceptual hash similarity"""
    try:
        h1 = imagehash.phash(Image.open(img1_path))
        h2 = imagehash.phash(Image.open(img2_path))
        return 1 - (h1 - h2) / len(h1.hash) ** 2
    except:
        return 0

def get_ssim(path1, path2):
    """Get SSIM score"""
    import cv2
    from skimage.metrics import structural_similarity as ssim
    from skimage.color import rgb2gray
    
    img1 = cv2.imread(path1)
    img2 = cv2.imread(path2)
    if img1 is None or img2 is None:
        return 0
    
    img1 = cv2.resize(img1, (256, 256))
    img2 = cv2.resize(img2, (256, 256))
    
    gray1 = rgb2gray(cv2.cvtColor(img1, cv2.COLOR_BGR2RGB))
    gray2 = rgb2gray(cv2.cvtColor(img2, cv2.COLOR_BGR2RGB))
    
    return ssim(gray1, gray2, data_range=1.0)

def check_consistency(char_name, ref_file, gen_file):
    """Quick consistency check"""
    ref_path = os.path.join(REFERENCES_DIR, ref_file)
    gen_path = os.path.join(GENERATED_DIR, gen_file)
    
    if not os.path.exists(gen_path):
        return 0
    
    hash_sim = get_hash_similarity(ref_path, gen_path)
    ssim_score = get_ssim(ref_path, gen_path)
    
    # Combined score
    overall = (hash_sim * 0.6 + ssim_score * 0.4)
    return overall

def queue_generation(prompt, negative_prompt, filename_prefix):
    """Queue a generation"""
    payload = {
        "prompt": {
            "1": {"class_type": "CheckpointLoaderSimple", "inputs": {"ckpt_name": "animagine-xl-3.1.safetensors"}},
            "2": {"class_type": "CLIPTextEncode", "inputs": {"text": prompt, "clip": ["1", 1]}},
            "3": {"class_type": "CLIPTextEncode", "inputs": {"text": negative_prompt, "clip": ["1", 1]}},
            "4": {"class_type": "EmptyLatentImage", "inputs": {"width": 832, "height": 1216, "batch_size": 1}},
            "5": {"class_type": "KSampler", "inputs": {
                "seed": 123, "steps": 30, "cfg": 7, "sampler_name": "euler_ancestral", 
                "scheduler": "normal", "denoise": 1.0, "model": ["1", 0], 
                "positive": ["2", 0], "negative": ["3", 0], "latent_image": ["4", 0]
            }},
            "6": {"class_type": "VAEDecode", "inputs": {"samples": ["5", 0], "vae": ["1", 2]}},
            "7": {"class_type": "SaveImage", "inputs": {"filename_prefix": filename_prefix, "images": ["6", 0]}}
        }
    }
    
    resp = requests.post(f"{COMFYUI_URL}/api/prompt", json=payload)
    return resp.json()

def wait_for_queue():
    """Wait for queue to clear"""
    while True:
        try:
            resp = requests.get(f"{COMFYUI_URL}/api/queue")
            q = resp.json()
            if not q.get('queue_running') and not q.get('queue_pending'):
                break
            time.sleep(2)
        except:
            break

def iterative_correction(max_iterations=5, threshold=0.70):
    """Main iterative correction loop"""
    
    print("=" * 70)
    print("ITERATIVE AUTO-CORRECTION LOOP")
    print("=" * 70)
    
    # Characters to fix (initial state)
    to_fix = [
        ("Everly", "Everly.png", "05_everly_soldier_00002_"),
        ("Violet", "Violet Devil.png", "08_violet_devil_00002_"),
        ("Nova", "Nova Human.png", "07_nova_warrior_00002_"),
        ("Éva Moreau", "Éva Moreau.png", "06_eva_doctor_00002_"),
        ("TC-23", "TC-23.png", "10_tc23_esper_00002_"),
    ]
    
    for iteration in range(1, max_iterations + 1):
        print(f"\n{'='*70}")
        print(f"ITERATION {iteration}/{max_iterations}")
        print(f"{'='*70}")
        
        still_failing = []
        
        for char_name, ref_file, gen_file in to_fix:
            print(f"\n🔍 Checking {char_name}...")
            
            # Check current score
            score = check_consistency(char_name, ref_file, gen_file)
            print(f"   Current score: {score:.1%}")
            
            if score >= threshold:
                print(f"   ✅ PASSED! ({score:.1%})")
                continue
            
            print(f"   ❌ Still failing, regenerating with enhanced prompt...")
            
            # Get character details
            char_data = CHARACTER_DETAILS.get(char_name, {})
            prompt = char_data.get("base_prompt", "")
            negative = char_data.get("negative", "")
            
            # Enhance prompt with iteration number
            enhanced_prompt = f"{prompt}, iteration {iteration}, highest quality, most accurate, matching reference exactly"
            enhanced_negative = f"{negative}, low quality, inaccurate, different character"
            
            # Generate
            result = queue_generation(enhanced_prompt, enhanced_negative, f"{char_name}_iter{iteration}")
            
            if 'prompt_id' in result:
                print(f"   ✅ Generated (ID: {result['prompt_id']})")
                wait_for_queue()
                
                # Update gen_file to check the new one
                new_gen = f"{char_name}_iter{iteration}_00001_.png"
                new_path = os.path.join(GENERATED_DIR, new_gen)
                
                # Check new score
                time.sleep(1)
                new_score = check_consistency(char_name, ref_file, new_gen)
                print(f"   New score: {new_score:.1%}")
                
                if new_score >= threshold:
                    print(f"   ✅ PASSED after regeneration!")
                else:
                    still_failing.append((char_name, ref_file, new_gen))
            else:
                print(f"   ❌ Generation failed: {result}")
                still_failing.append((char_name, ref_file, gen_file))
        
        to_fix = still_failing
        
        if not to_fix:
            print(f"\n✅ ALL CHARACTERS PASSED!")
            break
        
        print(f"\nStill failing: {len(to_fix)} characters")
    
    print(f"\n{'='*70}")
    print("FINAL CHECK")
    print(f"{'='*70}")
    
    # Final verification
    all_pass = True
    for char_name, ref_file, gen_file in to_fix:
        score = check_consistency(char_name, ref_file, gen_file)
        status = "✅ PASS" if score >= threshold else "❌ FAIL"
        print(f"  {char_name}: {score:.1%} {status}")
        if score < threshold:
            all_pass = False
    
    if all_pass:
        print("\n🎉 ALL CHARACTERS CONSISTENT!")
    else:
        print("\n⚠️ Some characters still below threshold")
        print("Note: API-based generation without IP-Adapter has limitations")

if __name__ == "__main__":
    iterative_correction(max_iterations=5, threshold=0.70)