#!/usr/bin/env python3
"""
aplicar_tema.py — aplica sistema de design Imperatriz a um .excalidraw existente.

Lê um arquivo .excalidraw, substitui cores pela paleta, troca fontes pela hierarquia
Imperatriz, ajusta roughness, espessura e arredondamento. NÃO move elementos.

Uso:
    python3 aplicar_tema.py --entrada diagrama.excalidraw --saida saida.excalidraw --paleta imperatriz
"""

import argparse
import json
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))

from helpers.paleta import (  # noqa: E402
    FONT_CASCADIA,
    FONT_HELVETICA,
    FONT_VIRGIL,
    ROUGHNESS,
    cor_sombra,
    get_paleta,
)


def mapear_cor(cor_original, paleta):
    """Mapeia cor antiga pra paleta nova (heuristica por luminosidade)."""
    if not cor_original or cor_original == "transparent":
        return cor_original
    cor = cor_original.lstrip("#")
    if len(cor) < 6:
        return paleta["traco"]
    try:
        r, g, b = int(cor[0:2], 16), int(cor[2:4], 16), int(cor[4:6], 16)
    except ValueError:
        return paleta["traco"]
    lum = (r * 299 + g * 587 + b * 114) / 1000

    # Cor preta/escura -> traco
    if lum < 40:
        return paleta["traco"]
    # Cor branca/clara -> background
    if lum > 230:
        return paleta["background"]
    # Cor quente (vermelha/laranja) -> destaque_quente
    if r > g and r > b and r > 150:
        return paleta["destaque_quente"]
    # Cor amarela/dourada -> accent
    if r > 150 and g > 150 and b < 120:
        return paleta["accent"]
    # Cor verde/azul -> dominante
    if g > r or b > r:
        return paleta["dominante"]
    # Fallback
    return paleta["neutro_medio"]


def aplicar_tema(cena, paleta, manter_layout=True):
    """Itera elementos e aplica tema. Mantem posicoes/tamanhos."""
    contador = {"shapes": 0, "textos": 0, "setas": 0}

    for el in cena.get("elements", []):
        tipo = el.get("type")

        # Roughness uniforme architect (premium B2B)
        if "roughness" in el:
            el["roughness"] = ROUGHNESS["architect"]

        if tipo in ("rectangle", "diamond", "ellipse"):
            # Stroke -> traco da paleta (ou mapeado)
            el["strokeColor"] = mapear_cor(el.get("strokeColor"), paleta)
            # Background -> mapeado
            if el.get("backgroundColor") and el["backgroundColor"] != "transparent":
                el["backgroundColor"] = mapear_cor(el["backgroundColor"], paleta)
            # Stroke width >= 2
            if el.get("strokeWidth", 1) < 2:
                el["strokeWidth"] = 2
            # Arredondado
            if tipo == "rectangle" and not el.get("roundness"):
                el["roundness"] = {"type": 3}
            contador["shapes"] += 1

        elif tipo == "text":
            # Fonte: detecta hierarquia pela tamanho atual
            tam = el.get("fontSize", 16)
            if tam >= 32:
                el["fontFamily"] = FONT_HELVETICA
            elif tam <= 14 and el.get("fontFamily") == 1:
                el["fontFamily"] = FONT_VIRGIL  # mantem annotation manuscrita
            elif el.get("fontFamily") == 3:
                el["fontFamily"] = FONT_CASCADIA  # mantem mono
            else:
                el["fontFamily"] = FONT_HELVETICA
            # Cor texto
            el["strokeColor"] = mapear_cor(el.get("strokeColor"), paleta)
            contador["textos"] += 1

        elif tipo in ("arrow", "line"):
            # Cor: cinza medio premium
            el["strokeColor"] = paleta["neutro_medio"]
            # Espessura 2px
            if el.get("strokeWidth", 1) < 2:
                el["strokeWidth"] = 2
            # Setas em elbow (B2B premium)
            if tipo == "arrow" and not el.get("elbowed"):
                el["elbowed"] = True
            contador["setas"] += 1

        elif tipo == "frame":
            el["strokeColor"] = paleta["neutro_medio"]
            el["strokeStyle"] = "dashed"

    # AppState: background + grid
    cena.setdefault("appState", {})
    cena["appState"]["viewBackgroundColor"] = paleta["background"]
    cena["appState"]["gridSize"] = 20
    cena["appState"]["gridStep"] = 5

    return cena, contador


def main():
    parser = argparse.ArgumentParser(description="Aplica tema Imperatriz a .excalidraw existente")
    parser.add_argument("--entrada", required=True, help="Arquivo .excalidraw de entrada")
    parser.add_argument("--saida", required=True, help="Arquivo .excalidraw de saida")
    parser.add_argument("--paleta", default="imperatriz",
                        choices=["imperatriz", "mckinsey", "dark_premium", "notion_minimal"])
    args = parser.parse_args()

    paleta = get_paleta(args.paleta)
    p_in = Path(args.entrada).expanduser()
    cena = json.loads(p_in.read_text(encoding="utf-8"))

    # Se vier do Obsidian (.excalidraw.md), extrair JSON do code fence
    if isinstance(cena, str) or not isinstance(cena, dict) or "elements" not in cena:
        raise ValueError(
            "Arquivo nao parece .excalidraw valido. "
            "Pra arquivo .excalidraw.md do Obsidian, extraia o JSON dentro do code fence primeiro."
        )

    cena, contador = aplicar_tema(cena, paleta)

    p_out = Path(args.saida).expanduser()
    p_out.parent.mkdir(parents=True, exist_ok=True)
    p_out.write_text(json.dumps(cena, ensure_ascii=False, indent=2), encoding="utf-8")

    print(f"✓ Tema '{paleta['nome']}' aplicado a {contador['shapes']} shapes, "
          f"{contador['textos']} textos, {contador['setas']} conectores.")
    print(f"✓ Salvo em: {p_out}")


if __name__ == "__main__":
    main()
