"""Tests de comfyui_pixelize_image (offline, sin red ni GPU; PIL/numpy)."""

import os
import sys

import numpy as np
from PIL import Image

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from ml.comfyui_pixelize_image import comfyui_pixelize_image  # noqa: E402


def _noisy_png(path, w=256, h=256):
    """PNG ruidoso con cientos de colores (simula crudo borroso de IA)."""
    rng = np.random.default_rng(7)
    arr = rng.integers(0, 256, size=(h, w, 3), dtype=np.uint8)
    Image.fromarray(arr, "RGB").save(path)
    return path


def test_golden_downscale_quantize(tmp_path):
    src = _noisy_png(str(tmp_path / "raw.png"))
    dst = str(tmp_path / "pixel.png")
    res = comfyui_pixelize_image(src, dst, downscale=8, colors=16)
    assert res["ok"] is True, res["error"]
    assert os.path.isfile(dst)
    assert res["size"] == [256, 256]          # upscale_back=True conserva tamano
    assert res["n_colors_final"] <= 16        # cuantizado a <=16 colores


def test_no_upscale_back_keeps_small(tmp_path):
    src = _noisy_png(str(tmp_path / "raw.png"))
    dst = str(tmp_path / "small.png")
    res = comfyui_pixelize_image(src, dst, downscale=8, colors=16, upscale_back=False)
    assert res["ok"] is True
    assert res["size"] == [32, 32]            # 256//8


def test_edge_fixed_palette_game_boy(tmp_path):
    src = _noisy_png(str(tmp_path / "raw.png"))
    dst = str(tmp_path / "gb.png")
    res = comfyui_pixelize_image(src, dst, palette="game-boy")
    assert res["ok"] is True, res["error"]
    assert res["n_colors_final"] <= 4         # paleta Game Boy = 4 colores


def test_edge_palette_list_hex(tmp_path):
    src = _noisy_png(str(tmp_path / "raw.png"))
    dst = str(tmp_path / "pal.png")
    res = comfyui_pixelize_image(src, dst, palette=["#000000", "#ffffff", "#ff0000"])
    assert res["ok"] is True
    assert res["n_colors_final"] <= 3


def test_edge_downscale_1_only_quantizes(tmp_path):
    src = _noisy_png(str(tmp_path / "raw.png"))
    dst = str(tmp_path / "q.png")
    res = comfyui_pixelize_image(src, dst, downscale=1, colors=8)
    assert res["ok"] is True
    assert res["size"] == [256, 256]
    assert res["n_colors_final"] <= 8


def test_error_missing_src(tmp_path):
    res = comfyui_pixelize_image(str(tmp_path / "nope.png"), str(tmp_path / "o.png"))
    assert res["ok"] is False
    assert "no existe" in res["error"]


def test_error_downscale_zero(tmp_path):
    src = _noisy_png(str(tmp_path / "raw.png"))
    res = comfyui_pixelize_image(src, str(tmp_path / "o.png"), downscale=0)
    assert res["ok"] is False
    assert "downscale" in res["error"]


def test_error_bad_palette(tmp_path):
    src = _noisy_png(str(tmp_path / "raw.png"))
    res = comfyui_pixelize_image(src, str(tmp_path / "o.png"), palette="not-a-palette")
    assert res["ok"] is False
    assert "paleta" in res["error"].lower()


# --- alpha-aware (sprites con fondo transparente) ---

def _rgba_subject_png(path, canvas=256, box=120):
    """RGBA: sujeto opaco de colores variados centrado, fondo transparente."""
    rng = np.random.default_rng(3)
    arr = np.zeros((canvas, canvas, 4), dtype=np.uint8)
    o = (canvas - box) // 2
    arr[o:o + box, o:o + box, :3] = rng.integers(0, 256, size=(box, box, 3), dtype=np.uint8)
    arr[o:o + box, o:o + box, 3] = 255  # sujeto opaco
    Image.fromarray(arr, "RGBA").save(path)
    return path


def test_alpha_preserved_transparent_corners(tmp_path):
    """RGBA in -> RGBA out con esquinas transparentes y paleta limitada en lo opaco."""
    src = _rgba_subject_png(str(tmp_path / "sprite.png"))
    dst = str(tmp_path / "px.png")
    res = comfyui_pixelize_image(src, dst, downscale=4, colors=16, upscale_back=False)
    assert res["ok"] is True, res["error"]
    assert res["has_alpha"] is True
    out = Image.open(dst).convert("RGBA")
    a = np.asarray(out)[..., 3]
    w, h = out.size
    # Las 4 esquinas deben ser transparentes (alpha == 0).
    assert a[0, 0] == 0 and a[0, w - 1] == 0
    assert a[h - 1, 0] == 0 and a[h - 1, w - 1] == 0
    # Centro opaco.
    assert a[h // 2, w // 2] == 255
    # Colores limitados en la zona opaca.
    assert res["n_colors_final"] <= 16


def test_alpha_off_flattens_to_rgb(tmp_path):
    """keep_alpha=False sobre RGBA -> sale RGB (sin canal alpha)."""
    src = _rgba_subject_png(str(tmp_path / "sprite.png"))
    dst = str(tmp_path / "flat.png")
    res = comfyui_pixelize_image(src, dst, downscale=4, colors=16, keep_alpha=False)
    assert res["ok"] is True
    assert res["has_alpha"] is False
    assert Image.open(dst).mode != "RGBA"


def test_rgb_input_unaffected_by_keep_alpha(tmp_path):
    """Imagen RGB (sin alpha) con keep_alpha=True sigue saliendo RGB, sin romper."""
    src = _noisy_png(str(tmp_path / "raw.png"))
    dst = str(tmp_path / "rgb.png")
    res = comfyui_pixelize_image(src, dst, downscale=8, colors=16)  # keep_alpha default True
    assert res["ok"] is True
    assert res["has_alpha"] is False
    assert res["n_colors_final"] <= 16


def test_error_all_transparent_no_crash(tmp_path):
    """RGBA toda transparente (rembg sin sujeto): no crashea, 0 colores opacos."""
    arr = np.zeros((64, 64, 4), dtype=np.uint8)  # alpha 0 en todo
    src = str(tmp_path / "empty.png")
    Image.fromarray(arr, "RGBA").save(src)
    dst = str(tmp_path / "out.png")
    res = comfyui_pixelize_image(src, dst, downscale=1, colors=16)
    assert res["ok"] is True, res["error"]
    assert res["has_alpha"] is True
    assert res["n_colors_final"] == 0
    out = np.asarray(Image.open(dst).convert("RGBA"))
    assert out[..., 3].max() == 0  # sigue toda transparente
