"""Genera un icono .ico multi-resolucion para apps C++ del registry.

Rasteriza un icono Phosphor SVG sobre un fondo redondeado del color accent
y exporta un .ico con multiples resoluciones (16, 24, 32, 48, 64, 128, 256).

Funciones publicas reutilizables:
    _find_registry_root  — localiza la raiz del registry
    _render_glyph_white  — rasteriza un SVG Phosphor como glyph blanco RGBA
    _make_icon_image     — compone fondo redondeado + glyph en un PIL.Image
"""
import io
import os
from pathlib import Path

from PIL import Image, ImageDraw

# cairosvg se importa de forma perezosa dentro de los renderers que lo usan
# (ver _render_glyph_*). Asi el modulo (y el paquete infra que lo reexporta)
# se importa sin requerir cairosvg instalado; solo rasterizar SVGs lo exige.


DEFAULT_SIZES = [16, 24, 32, 48, 64, 128, 256]


def _find_registry_root(registry_root: str | None = None) -> Path:
    """Descubre la raiz del registry.

    Prioridad: argumento explicito > FN_REGISTRY_ROOT env > caminar hacia arriba
    desde el archivo hasta encontrar registry.db.

    Args:
        registry_root: Ruta absoluta opcional. Si se pasa, se devuelve directamente.

    Returns:
        Path resuelto a la raiz del registry.

    Raises:
        FileNotFoundError: Si no se puede localizar registry.db.
    """
    if registry_root is not None:
        return Path(registry_root).resolve()
    env_root = os.environ.get("FN_REGISTRY_ROOT")
    if env_root:
        return Path(env_root).resolve()
    current = Path(__file__).resolve()
    for parent in current.parents:
        if (parent / "registry.db").exists():
            return parent
    raise FileNotFoundError(
        "No se encontro registry.db caminando desde el archivo hasta la raiz. "
        "Define FN_REGISTRY_ROOT en el entorno."
    )


def _hex_to_rgb(h: str) -> tuple[int, int, int]:
    h = h.lstrip("#")
    return tuple(int(h[i: i + 2], 16) for i in (0, 2, 4))


def _luminance(accent_hex: str) -> float:
    """Luminancia percibida Rec.601 normalizada [0,1]."""
    r, g, b = _hex_to_rgb(accent_hex)
    return (0.299 * r + 0.587 * g + 0.114 * b) / 255.0


def _render_glyph_colored(svg_path: Path, size: int, fill: str) -> Image.Image:
    """Renderiza un SVG Phosphor reemplazando currentColor por `fill`."""
    import cairosvg

    svg = svg_path.read_text(encoding="utf-8")
    svg = svg.replace('fill="currentColor"', f'fill="{fill}"')
    png_bytes = cairosvg.svg2png(
        bytestring=svg.encode("utf-8"),
        output_width=size,
        output_height=size,
    )
    return Image.open(io.BytesIO(png_bytes)).convert("RGBA")


def _render_glyph_white(svg_path: Path, size: int) -> Image.Image:
    """Renderiza el SVG Phosphor como glyph blanco sobre fondo transparente.

    Args:
        svg_path: Ruta al archivo .svg de Phosphor.
        size: Ancho/alto en pixels del output.

    Returns:
        Imagen RGBA con el glyph en blanco sobre fondo transparente.
    """
    import cairosvg

    svg = svg_path.read_text(encoding="utf-8")
    # Phosphor usa fill="currentColor" — forzar blanco.
    svg = svg.replace('fill="currentColor"', 'fill="#ffffff"')
    png_bytes = cairosvg.svg2png(
        bytestring=svg.encode("utf-8"),
        output_width=size,
        output_height=size,
    )
    return Image.open(io.BytesIO(png_bytes)).convert("RGBA")


def _make_icon_image(svg_path: Path, accent_hex: str, size: int) -> Image.Image:
    """Compone fondo redondeado con color accent + glyph blanco centrado al 70%.

    Args:
        svg_path: Ruta al archivo .svg de Phosphor.
        accent_hex: Color de fondo en formato "#RRGGBB".
        size: Ancho/alto en pixels del canvas de salida.

    Returns:
        Imagen RGBA lista para guardar como PNG o ICO.
    """
    bg_color = _hex_to_rgb(accent_hex) + (255,)
    canvas = Image.new("RGBA", (size, size), (0, 0, 0, 0))
    draw = ImageDraw.Draw(canvas)
    radius = max(2, size // 6)  # ~16% de radio redondeado
    draw.rounded_rectangle(
        [(0, 0), (size - 1, size - 1)],
        radius=radius,
        fill=bg_color,
    )
    # El glyph ocupa ~70% del canvas (padding ~15% en cada lado).
    glyph_size = int(size * 0.7)
    if glyph_size < 8:
        glyph_size = max(8, size - 2)
    glyph = _render_glyph_white(svg_path, glyph_size)
    off = ((size - glyph_size) // 2, (size - glyph_size) // 2)
    canvas.alpha_composite(glyph, dest=off)
    return canvas


ADAPTIVE_LUMINANCE_THRESHOLD = 0.5
ADAPTIVE_DARK_GLYPH = "#0f0f12"
ADAPTIVE_LIGHT_GLYPH = "#f4f4f5"
WHITE_DUOTONE_GLYPH = "#ffffff"


def _make_icon_image_duotone(
    phosphor_icon_name: str,
    accent_hex: str,
    size: int,
    phosphor_assets: Path,
    glyph_color: str,
) -> Image.Image:
    """Compone bg accent + glyph duotone Phosphor con `glyph_color` como fill.

    Usa la variante `duotone` de Phosphor cuando exista (el path con `opacity=0.2`
    crea automaticamente el segundo tono atenuado al mezclarse con el accent).
    Fallback a `fill` si no hay duotone.
    """
    bg_color = _hex_to_rgb(accent_hex) + (255,)
    canvas = Image.new("RGBA", (size, size), (0, 0, 0, 0))
    draw = ImageDraw.Draw(canvas)
    radius = max(2, size // 6)
    draw.rounded_rectangle([(0, 0), (size - 1, size - 1)], radius=radius, fill=bg_color)

    glyph_size = int(size * 0.7)
    if glyph_size < 8:
        glyph_size = max(8, size - 2)

    svg_duo = phosphor_assets / "duotone" / f"{phosphor_icon_name}-duotone.svg"
    svg_fill = phosphor_assets / "fill" / f"{phosphor_icon_name}-fill.svg"
    svg_src = svg_duo if svg_duo.exists() else svg_fill
    if not svg_src.exists():
        raise FileNotFoundError(f"Icono Phosphor no encontrado: {svg_src}")

    glyph = _render_glyph_colored(svg_src, glyph_size, glyph_color)
    off = ((size - glyph_size) // 2, (size - glyph_size) // 2)
    canvas.alpha_composite(glyph, dest=off)
    return canvas


def _make_icon_image_adaptive(
    phosphor_icon_name: str,
    accent_hex: str,
    size: int,
    phosphor_assets: Path,
    threshold: float = ADAPTIVE_LUMINANCE_THRESHOLD,
) -> Image.Image:
    """Adaptive duotone: tono claro/oscuro segun luminancia del accent."""
    glyph_color = ADAPTIVE_DARK_GLYPH if _luminance(accent_hex) >= threshold else ADAPTIVE_LIGHT_GLYPH
    return _make_icon_image_duotone(phosphor_icon_name, accent_hex, size, phosphor_assets, glyph_color)


def generate_app_icon(
    phosphor_icon_name: str,
    accent_hex: str,
    out_ico_path: str,
    *,
    weight: str = "fill",
    sizes: list[int] = None,
    phosphor_root: str = None,
    style: str = "fill_white",
) -> str:
    """Genera un icono .ico multi-resolucion a partir de un SVG Phosphor.

    Rasteriza el icono Phosphor indicado sobre un fondo redondeado del color
    accent y exporta un .ico con multiples resoluciones ordenadas de mayor a
    menor para maxima compatibilidad con Windows.

    Args:
        phosphor_icon_name: Nombre del icono Phosphor sin sufijo de weight
            (ej. "chart-bar", "tree-structure", "gauge").
        accent_hex: Color de fondo en formato hexadecimal "#RRGGBB"
            (ej. "#0ea5e9", "#7c3aed").
        out_ico_path: Ruta de salida para el archivo .ico. Puede ser absoluta
            o relativa al directorio de trabajo actual. El directorio padre
            se crea si no existe.
        weight: Variante del icono Phosphor. Default "fill". Otros valores
            validos segun el repositorio: "regular", "bold", "light",
            "thin", "duotone".
        sizes: Lista de resoluciones a incluir en el .ico. Default
            [16, 24, 32, 48, 64, 128, 256]. El orden no importa; se
            renderiza cada tamano individualmente para maxima crispness.
        phosphor_root: Ruta a la carpeta raiz de assets de phosphor-core
            (la que contiene subdirectorios "fill", "regular", etc.).
            Default: <registry_root>/sources/phosphor-core/assets.
        style: Estilo de render. Valores:
            - "fill_white" (default, backwards-compat): fondo accent + glyph
              blanco solido usando `weight`.
            - "adaptive_duotone": fondo accent + glyph duotone con tono claro/
              oscuro elegido por luminancia del accent. Glyph oscuro
              `#0f0f12` si L>=0.5; glyph claro `#f4f4f5` si L<0.5. Usa
              variante `duotone` de Phosphor (el path con opacity=0.2 da el
              segundo tono). Fallback a `fill` si no hay duotone.
            - "white_duotone": fondo accent + glyph duotone Phosphor con fill
              `#ffffff`. Path bg al opacity=0.2 deja translucir el accent y
              produce un segundo tono mas suave. Legible en cualquier accent
              (claro u oscuro). Fallback a `fill` si no hay duotone.

    Returns:
        Ruta absoluta del archivo .ico generado.

    Raises:
        FileNotFoundError: Si el SVG del icono no existe en phosphor_root.
        ValueError: Si accent_hex no tiene el formato "#RRGGBB" o style invalido.
    """
    if sizes is None:
        sizes = DEFAULT_SIZES

    if style not in ("fill_white", "adaptive_duotone", "white_duotone"):
        raise ValueError(f"style invalido: {style!r}. Valores: fill_white | adaptive_duotone | white_duotone")

    # Resolver raiz de phosphor
    if phosphor_root is None:
        _root = _find_registry_root()
        phosphor_assets = _root / "sources" / "phosphor-core" / "assets"
    else:
        phosphor_assets = Path(phosphor_root)

    # Validar formato del color (antes de tocar disco)
    h = accent_hex.lstrip("#")
    if len(h) != 6:
        raise ValueError(f"accent_hex debe tener formato #RRGGBB, recibido: {accent_hex!r}")

    if style in ("adaptive_duotone", "white_duotone"):
        svg_duo = phosphor_assets / "duotone" / f"{phosphor_icon_name}-duotone.svg"
        svg_fill = phosphor_assets / "fill" / f"{phosphor_icon_name}-fill.svg"
        if not svg_duo.exists() and not svg_fill.exists():
            raise FileNotFoundError(
                f"Icono Phosphor no encontrado: {svg_duo} ni {svg_fill}\n"
                f"Asegurate de que sources/phosphor-core/ existe."
            )
        if style == "adaptive_duotone":
            images = {
                s: _make_icon_image_adaptive(phosphor_icon_name, accent_hex, s, phosphor_assets)
                for s in sorted(sizes, reverse=True)
            }
        else:  # white_duotone
            images = {
                s: _make_icon_image_duotone(phosphor_icon_name, accent_hex, s, phosphor_assets, WHITE_DUOTONE_GLYPH)
                for s in sorted(sizes, reverse=True)
            }
    else:
        svg_file = phosphor_assets / weight / f"{phosphor_icon_name}-{weight}.svg"
        if not svg_file.exists():
            raise FileNotFoundError(
                f"Icono Phosphor no encontrado: {svg_file}\n"
                f"Asegurate de que sources/phosphor-core/ existe. Si no:\n"
                f"  git clone --depth=1 https://github.com/phosphor-icons/core.git "
                f"sources/phosphor-core"
            )
        images = {s: _make_icon_image(svg_file, accent_hex, s) for s in sorted(sizes, reverse=True)}

    out = Path(out_ico_path)
    if not out.is_absolute():
        out = Path.cwd() / out
    out.parent.mkdir(parents=True, exist_ok=True)

    sorted_sizes = sorted(sizes, reverse=True)
    biggest = images[sorted_sizes[0]]
    others = [images[s] for s in sorted_sizes[1:]]
    biggest.save(
        out,
        format="ICO",
        sizes=[(s, s) for s in sorted_sizes],
        append_images=others,
    )

    return str(out.resolve())
