"""Compara dos PIL Images lado a lado con diff opcional y metricas de similitud."""

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    import PIL.Image


def image_compare_side_by_side(
    a: "PIL.Image.Image",
    b: "PIL.Image.Image",
    label_a: str = "A",
    label_b: str = "B",
    gap_px: int = 16,
    show_diff: bool = True,
    show_phash: bool = True,
) -> dict:
    """Crea una imagen comparativa lado a lado con metricas opcionales.

    Construye una imagen compuesta A | [diff] | B con gap configurable.
    Calcula MSE pixel-wise y opcionalmente perceptual hash (imagehash).

    Args:
        a: Primera imagen PIL (imagen de referencia).
        b: Segunda imagen PIL (imagen a comparar).
        label_a: Etiqueta de texto para la imagen A (default "A").
        label_b: Etiqueta de texto para la imagen B (default "B").
        gap_px: Espacio en pixeles entre paneles (default 16).
        show_diff: Si True, inserta panel de diferencia autocontrastada entre A y B.
        show_phash: Si True, calcula perceptual hash con imagehash si disponible.

    Returns:
        dict con:
          - "grid": PIL.Image.Image — imagen compuesta lado a lado.
          - "phash_a": str | None — 16 hex chars de perceptual hash de A (None si imagehash no disponible).
          - "phash_b": str | None — 16 hex chars de perceptual hash de B.
          - "phash_distance": int | None — Distancia de Hamming entre phash_a y phash_b.
          - "pixel_mse": float — MSE pixel-wise sobre canales RGB.

    Raises:
        ImportError: si Pillow no esta instalado.
    """
    try:
        from PIL import Image, ImageChops, ImageDraw, ImageFont, ImageOps
    except ImportError as exc:
        raise ImportError(
            "Pillow no esta instalado. Instalar con: pip install Pillow"
        ) from exc

    # Normalizar a RGB
    img_a = a.convert("RGB")
    img_b = b.convert("RGB")

    # Asegurar mismo tamano para comparacion (resize b a size de a si difieren)
    if img_a.size != img_b.size:
        img_b = img_b.resize(img_a.size, Image.LANCZOS)

    w, h = img_a.size

    # --- Construir panels ---
    panels = [img_a]
    if show_diff:
        diff = ImageChops.difference(img_a, img_b)
        diff_contrast = ImageOps.autocontrast(diff)
        panels.append(diff_contrast)
    panels.append(img_b)

    n = len(panels)
    canvas_w = n * w + (n + 1) * gap_px
    canvas_h = h + 2 * gap_px
    canvas = Image.new("RGB", (canvas_w, canvas_h), color=(20, 20, 20))

    # Pegar panels
    labels_map = {0: label_a, n - 1: label_b}
    if show_diff:
        labels_map[1] = "diff"

    try:
        draw = ImageDraw.Draw(canvas)
        font = ImageFont.load_default()
    except Exception:
        draw = None
        font = None

    for i, panel in enumerate(panels):
        x = gap_px + i * (w + gap_px)
        y = gap_px
        canvas.paste(panel, (x, y))
        if draw and i in labels_map:
            draw.text((x + 4, y + 4), labels_map[i], fill=(255, 255, 255), font=font)

    # --- MSE pixel-wise ---
    pixel_mse = _compute_mse(img_a, img_b)

    # --- Perceptual hash ---
    phash_a: str | None = None
    phash_b: str | None = None
    phash_distance: int | None = None

    if show_phash:
        try:
            import imagehash  # type: ignore[import]
            h_a = imagehash.phash(img_a)
            h_b = imagehash.phash(img_b)
            phash_a = str(h_a)
            phash_b = str(h_b)
            phash_distance = int(h_a - h_b)
        except ImportError:
            pass  # imagehash not installed — leave None

    return {
        "grid": canvas,
        "phash_a": phash_a,
        "phash_b": phash_b,
        "phash_distance": phash_distance,
        "pixel_mse": pixel_mse,
    }


def _compute_mse(img_a: "PIL.Image.Image", img_b: "PIL.Image.Image") -> float:
    """Calcula MSE pixel-wise sobre canales RGB."""
    try:
        import numpy as np
        arr_a = np.asarray(img_a, dtype=np.float64)
        arr_b = np.asarray(img_b, dtype=np.float64)
        return float(np.mean((arr_a - arr_b) ** 2))
    except ImportError:
        pass

    # Fallback puro stdlib (lento para imagenes grandes)
    pixels_a = list(img_a.getdata())
    pixels_b = list(img_b.getdata())
    n_pixels = len(pixels_a)
    if n_pixels == 0:
        return 0.0

    total = 0.0
    for pa, pb in zip(pixels_a, pixels_b):
        # Each pixel is (R, G, B)
        for ca, cb in zip(pa, pb):
            diff = float(ca) - float(cb)
            total += diff * diff

    channels = 3
    return total / (n_pixels * channels)
