"""export_hub_icons — rasteriza iconos PNG para todas las apps C++/imgui del registry.

Para cada app cpp/imgui registrada en registry.db lee el bloque `icon:` de su
app.md (phosphor + accent), rasteriza un PNG cuadrado usando la misma logica
visual que generate_app_icon y lo escribe en out_dir/<name>.png.

El directorio de salida lo consume app_hub_launcher via fn::gl_texture_load.
"""

from __future__ import annotations

import os
import sqlite3
import sys
from pathlib import Path
from typing import Any

from infra.generate_app_icon import (
    _find_registry_root,
    _make_icon_image,
    _make_icon_image_adaptive,
    _make_icon_image_duotone,
    WHITE_DUOTONE_GLYPH,
)

DEFAULT_PHOSPHOR = "app-window"
DEFAULT_ACCENT = "#64748b"
DEFAULT_SIZE = 64
DEFAULT_STYLE = "fill_white"
VALID_STYLES = ("fill_white", "adaptive_duotone", "white_duotone")


def _read_frontmatter(md_path: Path) -> dict[str, Any]:
    """Parse YAML frontmatter from a .md file. Returns {} on any error."""
    try:
        import yaml

        text = md_path.read_text(encoding="utf-8")
        if not text.startswith("---"):
            return {}
        end = text.find("\n---", 3)
        if end == -1:
            return {}
        yaml_block = text[3:end].strip()
        data = yaml.safe_load(yaml_block)
        return data if isinstance(data, dict) else {}
    except Exception as exc:
        print(f"[export_hub_icons] WARN: could not parse {md_path}: {exc}", file=sys.stderr)
        return {}


def export_hub_icons(
    out_dir: str,
    *,
    size: int = DEFAULT_SIZE,
    registry_root: str | None = None,
    style: str = DEFAULT_STYLE,
) -> dict:
    """Rasteriza iconos PNG para todas las apps C++/imgui del registry.

    Consulta registry.db buscando apps con lang='cpp' AND framework='imgui',
    lee el bloque icon: de cada app.md (phosphor + accent), y escribe un PNG
    cuadrado de size x size pixels en out_dir/<name>.png usando el mismo estilo
    visual de generate_app_icon: fondo redondeado del color accent con glyph
    Phosphor blanco centrado al 70%.

    Si una app.md no existe, no tiene icono declarado o el SVG Phosphor no se
    encuentra, el app se omite (skipped) y el proceso continua con las demas.

    Args:
        out_dir: Directorio de salida. Se crea si no existe. Los PNGs existentes
            se sobreescriben.
        size: Lado del cuadrado del PNG en pixels. Default 64.
        registry_root: Ruta a la raiz del fn_registry. Si es None, usa la variable
            de entorno FN_REGISTRY_ROOT o la raiz del repo derivada de la ubicacion del archivo.

    Returns:
        {
            "ok": True,
            "count": N,          # PNGs escritos correctamente
            "out_dir": "<abs>",  # ruta absoluta del directorio de salida
            "skipped": [         # apps omitidas con razon
                {"name": "...", "reason": "..."},
                ...
            ]
        }
    """
    if style not in VALID_STYLES:
        raise ValueError(f"style invalido: {style!r}. Valores: {' | '.join(VALID_STYLES)}")

    root = _find_registry_root(registry_root)

    db_path = root / "registry.db"
    if not db_path.exists():
        raise FileNotFoundError(f"registry.db not found at {db_path}")

    con = sqlite3.connect(str(db_path))
    con.row_factory = sqlite3.Row
    try:
        rows = con.execute(
            "SELECT id, name, dir_path FROM apps"
            " WHERE lang='cpp' AND framework='imgui'"
            " ORDER BY name"
        ).fetchall()
    finally:
        con.close()

    phosphor_assets = root / "sources" / "phosphor-core" / "assets"

    out_path = Path(out_dir).resolve()
    out_path.mkdir(parents=True, exist_ok=True)

    count = 0
    skipped: list[dict[str, str]] = []

    for row in rows:
        app_name: str = row["name"]
        dir_path: str = row["dir_path"]

        # --- Leer app.md ---
        md_path = root / dir_path / "app.md"
        if not md_path.exists():
            msg = f"app.md missing at {md_path}"
            print(f"[export_hub_icons] SKIP {app_name}: {msg}", file=sys.stderr)
            skipped.append({"name": app_name, "reason": msg})
            continue

        fm = _read_frontmatter(md_path)
        if not fm:
            msg = "app.md missing or malformed frontmatter"
            print(f"[export_hub_icons] SKIP {app_name}: {msg}", file=sys.stderr)
            skipped.append({"name": app_name, "reason": msg})
            continue

        icon_block = fm.get("icon")
        if isinstance(icon_block, dict):
            phosphor_name = icon_block.get("phosphor") or DEFAULT_PHOSPHOR
            accent_hex = icon_block.get("accent") or DEFAULT_ACCENT
        else:
            phosphor_name = DEFAULT_PHOSPHOR
            accent_hex = DEFAULT_ACCENT

        # --- Localizar SVG Phosphor (duotone preferido si style lo necesita) ---
        svg_fill = phosphor_assets / "fill" / f"{phosphor_name}-fill.svg"
        svg_duo = phosphor_assets / "duotone" / f"{phosphor_name}-duotone.svg"
        if style == "fill_white":
            if not svg_fill.exists():
                msg = f"Phosphor SVG not found: {svg_fill}"
                print(f"[export_hub_icons] SKIP {app_name}: {msg}", file=sys.stderr)
                skipped.append({"name": app_name, "reason": msg})
                continue
        else:
            if not svg_duo.exists() and not svg_fill.exists():
                msg = f"Phosphor SVG not found: {svg_duo} ni {svg_fill}"
                print(f"[export_hub_icons] SKIP {app_name}: {msg}", file=sys.stderr)
                skipped.append({"name": app_name, "reason": msg})
                continue

        # --- Rasterizar y guardar PNG ---
        try:
            if style == "fill_white":
                img = _make_icon_image(svg_fill, accent_hex, size)
            elif style == "adaptive_duotone":
                img = _make_icon_image_adaptive(phosphor_name, accent_hex, size, phosphor_assets)
            else:  # white_duotone
                img = _make_icon_image_duotone(phosphor_name, accent_hex, size, phosphor_assets, WHITE_DUOTONE_GLYPH)
            png_out = out_path / f"{app_name}.png"
            img.save(str(png_out), format="PNG")
            count += 1
        except Exception as exc:
            msg = f"render error: {exc}"
            print(f"[export_hub_icons] SKIP {app_name}: {msg}", file=sys.stderr)
            skipped.append({"name": app_name, "reason": msg})
            continue

    return {
        "ok": True,
        "count": count,
        "out_dir": str(out_path),
        "skipped": skipped,
    }


if __name__ == "__main__":
    import argparse
    import json

    parser = argparse.ArgumentParser(
        description="Export PNG icons for all cpp/imgui apps to a directory."
    )
    parser.add_argument("out_dir", help="Destination directory for .png files")
    parser.add_argument(
        "--size",
        type=int,
        default=DEFAULT_SIZE,
        help=f"Icon side in pixels (default {DEFAULT_SIZE})",
    )
    parser.add_argument(
        "--registry-root",
        default=None,
        help="Path to fn_registry root (default: FN_REGISTRY_ROOT env or repo root derived from file location)",
    )
    parser.add_argument(
        "--style",
        default=DEFAULT_STYLE,
        choices=VALID_STYLES,
        help=f"Estilo del icono (default {DEFAULT_STYLE})",
    )
    args = parser.parse_args()

    result = export_hub_icons(
        args.out_dir,
        size=args.size,
        registry_root=args.registry_root,
        style=args.style,
    )
    print(json.dumps(result, indent=2))
