import random

def ensure_order(a, b):
    return (a, b) if a <= b else (b, a)

class EndlessNode_Mayhem:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "steps_min": ("INT", {"default": 20, "min": 1, "max": 150}),
                "steps_max": ("INT", {"default": 40, "min": 1, "max": 150}),
                "cfg_min": ("FLOAT", {"default": 6.0, "min": 1.0, "max": 20.0}),
                "cfg_max": ("FLOAT", {"default": 12.0, "min": 1.0, "max": 20.0}),
                "guidance_min": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 6.0}),
                "guidance_max": ("FLOAT", {"default": 3.5, "min": 0.0, "max": 6.0}),
                "height_min": ("INT", {"default": 512, "min": 256, "max": 4096}),
                "height_max": ("INT", {"default": 768, "min": 256, "max": 4096}),
                "width_min": ("INT", {"default": 512, "min": 256, "max": 4096}),
                "width_max": ("INT", {"default": 768, "min": 256, "max": 4096}),
                "flip_dimensions": ("BOOLEAN", {"default": True}),
                "divisible_by_64": ("BOOLEAN", {"default": False}),
                "seed_min": ("INT", {"default": 0, "min": 0, "max": 2**32 - 1}),
                "seed_max": ("INT", {"default": 8675309, "min": 0, "max": 2**32 - 1}),
                "seed": ("INT", {
                    "default": 0,
                    "min": 0,
                    "max": 2**32 - 1
                }),
            }
        }

    RETURN_TYPES = ("INT", "FLOAT", "FLOAT", "INT", "INT", "INT")
    RETURN_NAMES = ("steps", "cfg_scale", "cfg_guidance", "height", "width", "seed")
    FUNCTION = "randomize_with_flip"
    CATEGORY = "Endless 🌊✨/Randomizers"

    def randomize_with_flip(self, steps_min, steps_max, cfg_min, cfg_max, guidance_min, guidance_max, height_min, height_max, width_min, width_max, flip_dimensions, divisible_by_64, seed_min, seed_max, seed):
        # Use the seed to ensure reproducible randomness
        random.seed(seed)
        
        # Set divisibility requirement
        divisor = 64 if divisible_by_64 else 16
        
        # Min and max sanity checks

        steps_min, steps_max = ensure_order(steps_min, steps_max)
        cfg_min, cfg_max = ensure_order(cfg_min, cfg_max)
        guidance_min, guidance_max = ensure_order(guidance_min, guidance_max)
        height_min, height_max = ensure_order(height_min, height_max)
        width_min, width_max = ensure_order(width_min, width_max)
        seed_min, seed_max = ensure_order(seed_min, seed_max)


        # Ensure dimensions are divisible by divisor and at least 256
        height_min = max(256, (height_min // divisor) * divisor)
        height_max = max(256, (height_max // divisor) * divisor)
        width_min = max(256, (width_min // divisor) * divisor)
        width_max = max(256, (width_max // divisor) * divisor)

        # Random values
        steps = random.randint(steps_min, steps_max)
        cfg_scale = round(random.uniform(cfg_min, cfg_max), 2)
        cfg_guidance = round(random.uniform(guidance_min, guidance_max), 2)

        # Pick values based on user-defined intent
        height = random.randint(height_min // divisor, height_max // divisor) * divisor
        width = random.randint(width_min // divisor, width_max // divisor) * divisor

        # Flip output if requested
        if flip_dimensions and random.random() < 0.5:
            width, height = height, width
        
        output_seed = random.randint(seed_min, seed_max)
        return (steps, cfg_scale, cfg_guidance, height, width, output_seed)


class EndlessNode_Chaos:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "steps_min": ("INT", {"default": 20, "min": 1, "max": 150}),
                "steps_max": ("INT", {"default": 40, "min": 1, "max": 150}),
                "cfg_min": ("FLOAT", {"default": 6.0, "min": 1.0, "max": 20.0}),
                "cfg_max": ("FLOAT", {"default": 12.0, "min": 1.0, "max": 20.0}),
                "guidance_min": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 6.0}),
                "guidance_max": ("FLOAT", {"default": 3.5, "min": 0.0, "max": 6.0}),
                "dimension_min": ("INT", {"default": 512, "min": 256, "max": 4096}),
                "dimension_max": ("INT", {"default": 1024, "min": 256, "max": 4096}),
                "orientation": (["portrait", "landscape", "square", "random"], {"default": "random"}),
                "divisible_by_64": ("BOOLEAN", {"default": False}),
                "seed_min": ("INT", {"default": 0, "min": 0, "max": 2**32 - 1}),
                "seed_max": ("INT", {"default": 8675309, "min": 0, "max": 2**32 - 1}),
                "seed": ("INT", {
                    "default": 0,
                    "min": 0,
                    "max": 2**32 - 1
                }),
            }
        }

    RETURN_TYPES = ("INT", "FLOAT", "FLOAT", "INT", "INT", "INT")
    RETURN_NAMES = ("steps", "cfg_scale", "cfg_guidance", "height", "width", "seed")
    FUNCTION = "randomize_aspect_ratio_chaos"
    CATEGORY = "Endless 🌊✨/Randomizers"

    def randomize_aspect_ratio_chaos(self, steps_min, steps_max, cfg_min, cfg_max, guidance_min, guidance_max, dimension_min, dimension_max, orientation, divisible_by_64, seed_min, seed_max, seed):
        # Use the seed to ensure reproducible randomness
        random.seed(seed)
        
        # Set divisibility requirement
        divisor = 64 if divisible_by_64 else 16
        

        # Min and max sanity checks

        steps_min, steps_max = ensure_order(steps_min, steps_max)
        cfg_min, cfg_max = ensure_order(cfg_min, cfg_max)
        guidance_min, guidance_max = ensure_order(guidance_min, guidance_max)
        dimension_min, dimension_max = ensure_order(dimension_min, dimension_max)
        seed_min, seed_max = ensure_order(seed_min, seed_max)


        # Ensure min/max dimensions are properly divisible
        dimension_min = max(256, (dimension_min // divisor) * divisor)
        dimension_max = max(256, (dimension_max // divisor) * divisor)
        
        # Common aspect ratios (width:height)
        aspect_ratios = [
            (1, 1),      # 1:1 Square
            (4, 3),      # 4:3 Classic
            (3, 2),      # 3:2 Classic photo
            (16, 9),     # 16:9 Widescreen
            (5, 4),      # 5:4 
            (5, 3),      # 5:3
            (7, 5),      # 7:5
            (8, 5),      # 8:5
            (192, 100),  # 1.92:1 Instagram Stories
            (21, 9),     # 21:9 Ultrawide (2.33:1)
            (9, 21),     # 9:21 Ultra-tall mobile
            (22, 10),    # 2.2:1 70mm Cinema
            (28, 10),    # 2.8:1 Facebook Cover (approx)
            (185, 100),  # 1.85:1 Cinema
            (239, 100),  # 2.39:1 Anamorphic
        ]
        
        # Filter aspect ratios based on orientation
        if orientation == "square":
            filtered_ratios = [(1, 1)]
        elif orientation == "landscape":
            filtered_ratios = [(w, h) for w, h in aspect_ratios if w > h]
        elif orientation == "portrait":
            filtered_ratios = [(h, w) for w, h in aspect_ratios if w > h]  # Flip to portrait
            filtered_ratios.append((1, 1))  # Include square
        else:  # random
            # Include both orientations for non-square ratios
            all_ratios = []
            for w, h in aspect_ratios:
                all_ratios.append((w, h))  # Landscape
                if w != h:  # Don't duplicate square
                    all_ratios.append((h, w))  # Portrait
            filtered_ratios = all_ratios
        
        # Find valid dimensions for each aspect ratio
        valid_dimensions = []
        
        for aspect_w, aspect_h in filtered_ratios:
            # Try different scales to find dimensions within our bounds
            for scale in range(1, 100):  # Reasonable scale range
                width = (aspect_w * scale * divisor) // divisor * divisor
                height = (aspect_h * scale * divisor) // divisor * divisor
                
                # Check if dimensions are within bounds
                if (dimension_min <= width <= dimension_max and 
                    dimension_min <= height <= dimension_max):
                    valid_dimensions.append((width, height))
                
                # If we've exceeded max dimension, no point in larger scales
                if width > dimension_max or height > dimension_max:
                    break
        
        # Remove duplicates and ensure we have at least one option
        valid_dimensions = list(set(valid_dimensions))
        
        if not valid_dimensions:
            # Fallback to square if no valid aspect ratios found
            size = (dimension_min // divisor) * divisor
            valid_dimensions = [(size, size)]
        
        # Choose random dimensions
        width, height = random.choice(valid_dimensions)
        
        # Generate other random values
        steps = random.randint(steps_min, steps_max)
        cfg_scale = round(random.uniform(cfg_min, cfg_max), 2)
        output_seed = random.randint(seed_min, seed_max)
        cfg_guidance = round(random.uniform(guidance_min, guidance_max), 2)
        
        return (steps, cfg_scale, cfg_guidance, height, width, output_seed)


class EndlessNode_Pandemonium:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "divisible_by_64": ("BOOLEAN", {"default": False}),
                "seed": ("INT", {
                    "default": 0,
                    "min": 0,
                    "max": 2**32 - 1
                }),
            }
        }

    RETURN_TYPES = ("INT", "FLOAT", "FLOAT", "INT", "INT", "INT")
    RETURN_NAMES = ("steps", "cfg_scale", "cfg_guidance", "height", "width", "seed")
    FUNCTION = "randomize_aspect_ratio_mayhem"
    CATEGORY = "Endless 🌊✨/Randomizers"

    def randomize_aspect_ratio_mayhem(self, divisible_by_64, seed):
        import random
        random.seed(seed)

        # Fixed internal config
        dimension_min = 512
        dimension_max = 1536
        divisor = 64 if divisible_by_64 else 16

        dimension_min = max(256, (dimension_min // divisor) * divisor)
        dimension_max = max(256, (dimension_max // divisor) * divisor)

        # All aspect ratios (landscape + portrait + square)
        aspect_ratios = [
            (1, 1),
            (4, 3), (3, 2),
            (16, 9), (9, 16),
            (5, 4), (4, 5),
            (5, 3), (3, 5),
            (7, 5), (5, 7),
            (8, 5), (5, 8),
            (192, 100), (100, 192),
            (21, 9), (9, 21),
            (22, 10), (10, 22),
            (28, 10), (10, 28),
            (185, 100), (100, 185),
            (239, 100), (100, 239)
        ]

        valid_dimensions = []

        for aspect_w, aspect_h in aspect_ratios:
            for scale in range(1, 100):
                width = (aspect_w * scale * divisor) // divisor * divisor
                height = (aspect_h * scale * divisor) // divisor * divisor
                if dimension_min <= width <= dimension_max and dimension_min <= height <= dimension_max:
                    valid_dimensions.append((width, height))
                if width > dimension_max or height > dimension_max:
                    break

        if not valid_dimensions:
            fallback = (dimension_min // divisor) * divisor
            valid_dimensions = [(fallback, fallback)]

        width, height = random.choice(valid_dimensions)
        steps = random.randint(5, 60)
        cfg_scale = round(random.uniform(0, 15), 2)
        cfg_guidance = round(random.uniform(1, 4), 2)
        output_seed = random.randint(0, seed)

        return (steps, cfg_scale, cfg_guidance, height, width, output_seed)

