#!/usr/bin/env python3
"""
auditar.py — auditor visual de diagramas .excalidraw contra sistema Imperatriz.

Avalia 12 criterios e gera nota 0-100 + recomendacoes pra subir o nivel.

Uso:
    python3 auditar.py --entrada diagrama.excalidraw
    python3 auditar.py --entrada diagrama.excalidraw --paleta imperatriz --json
"""

import argparse
import json
import sys
from collections import Counter
from pathlib import Path

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

from helpers.paleta import get_paleta  # noqa: E402


CRITERIOS = [
    "paleta_limitada",       # max 5 cores
    "tipografia_consistente",  # max 3 fontes
    "roughness_uniforme",    # uma roughness so
    "stroke_uniforme",       # espessura padronizada
    "alinhamento_grid",      # coords multiplos de 20
    "espacamento_minimo",    # 40px+ entre blocos
    "tem_titulo",            # texto grande (48px+) presente
    "background_estilizado",  # nao branco puro
    "frames_organizam",      # ha pelo menos 1 frame
    "conectores_premium",    # elbow ou curved, nao reto
    "shapes_arredondados",   # roundness configurado
    "annotations_humanas",   # Virgil presente em algum lugar
]


def cor_hex_clean(cor):
    if not cor or cor == "transparent":
        return None
    return cor.upper().split(":")[0][:7]


def auditar_cena(cena, paleta):
    """Roda 12 verificacoes, retorna dict com resultados + nota."""
    elementos = cena.get("elements", [])
    app = cena.get("appState", {})

    resultados = {}
    recomendacoes = []

    # 1. Paleta limitada (max 5 cores)
    cores = set()
    for el in elementos:
        for k in ("strokeColor", "backgroundColor"):
            c = cor_hex_clean(el.get(k))
            if c:
                cores.add(c)
    n_cores = len(cores)
    resultados["paleta_limitada"] = n_cores <= 6
    if n_cores > 6:
        recomendacoes.append(f"Reduzir paleta — diagrama tem {n_cores} cores distintas (alvo: ≤5). "
                             "Roda aplicar_tema.py pra normalizar.")

    # 2. Tipografia consistente
    fontes = set()
    for el in elementos:
        if el.get("type") == "text":
            fontes.add(el.get("fontFamily"))
    n_fontes = len(fontes)
    resultados["tipografia_consistente"] = n_fontes <= 3
    if n_fontes > 3:
        recomendacoes.append(f"Reduzir fontes — tem {n_fontes} familias (alvo: ≤3). "
                             "Helvetica + Virgil + Cascadia bastam.")

    # 3. Roughness uniforme
    roughnesses = set()
    for el in elementos:
        if "roughness" in el:
            roughnesses.add(el.get("roughness"))
    resultados["roughness_uniforme"] = len(roughnesses) <= 1
    if len(roughnesses) > 1:
        recomendacoes.append("Roughness misturada — defina UMA roughness (0=architect, 1=artist, 2=cartoonist) "
                             "pra todo o diagrama.")

    # 4. Stroke uniforme
    espessuras = Counter()
    for el in elementos:
        if "strokeWidth" in el:
            espessuras[el["strokeWidth"]] += 1
    n_espessuras = len(espessuras)
    resultados["stroke_uniforme"] = n_espessuras <= 2
    if n_espessuras > 2:
        recomendacoes.append(f"Espessura de traco variada ({n_espessuras} valores). "
                             "Padronize: 2px pra tudo, 3px so pra destaque.")

    # 5. Alinhamento ao grid (coords / 20)
    fora_grid = 0
    total_check = 0
    for el in elementos:
        if "x" in el and "y" in el:
            total_check += 1
            if el["x"] % 20 != 0 or el["y"] % 20 != 0:
                fora_grid += 1
    pct_grid = 1 - (fora_grid / total_check if total_check else 0)
    resultados["alinhamento_grid"] = pct_grid >= 0.7
    if pct_grid < 0.7:
        recomendacoes.append(f"Snap to grid — {fora_grid}/{total_check} elementos fora do grid de 20px. "
                             "Ative View > Snap to grid + Snap to objects no Excalidraw.")

    # 6. Espacamento minimo entre blocos (heuristica simples)
    blocos = [el for el in elementos if el.get("type") in ("rectangle", "diamond", "ellipse")]
    colisoes = 0
    for i, a in enumerate(blocos):
        for b in blocos[i+1:]:
            if a["x"] + a["width"] + 20 > b["x"] and b["x"] + b["width"] + 20 > a["x"]:
                if abs(a["y"] - b["y"]) < 20 and a["y"] != b["y"]:
                    colisoes += 1
    resultados["espacamento_minimo"] = colisoes < max(1, len(blocos) // 5)
    if colisoes >= max(1, len(blocos) // 5):
        recomendacoes.append("Espacamento apertado — aumente gap entre blocos (alvo: 40px+).")

    # 7. Tem titulo (texto >=40px)
    tem_titulo = any(
        el.get("type") == "text" and el.get("fontSize", 0) >= 40
        for el in elementos
    )
    resultados["tem_titulo"] = tem_titulo
    if not tem_titulo:
        recomendacoes.append("Falta titulo — adicione texto grande (48-72px) em CAPS Helvetica no topo.")

    # 8. Background estilizado (nao branco puro)
    bg = (app.get("viewBackgroundColor") or "#FFFFFF").upper()
    resultados["background_estilizado"] = bg not in ("#FFFFFF", "#FFF", "WHITE")
    if not resultados["background_estilizado"]:
        recomendacoes.append("Background branco puro — use marfim #FAF8F3 ou carvao #1A1A1A "
                             "(via appState.viewBackgroundColor).")

    # 9. Frames organizam
    tem_frame = any(el.get("type") == "frame" for el in elementos)
    resultados["frames_organizam"] = tem_frame
    if not tem_frame and len(elementos) > 15:
        recomendacoes.append("Diagrama complexo sem frames — agrupe blocos relacionados em Frames (F) nomeados.")

    # 10. Conectores premium (elbow ou curved, nao reto diagonal)
    setas = [el for el in elementos if el.get("type") == "arrow"]
    setas_premium = sum(
        1 for s in setas
        if s.get("elbowed") or len(s.get("points", [])) > 2
    )
    resultados["conectores_premium"] = (
        not setas or setas_premium / len(setas) >= 0.5
    )
    if setas and setas_premium / len(setas) < 0.5:
        recomendacoes.append(f"Conectores retos demais — converta setas pra elbow (B2B) ou curved. "
                             f"({setas_premium}/{len(setas)} sao premium hoje)")

    # 11. Shapes arredondados
    rects = [el for el in elementos if el.get("type") == "rectangle"]
    rects_round = sum(1 for r in rects if r.get("roundness"))
    resultados["shapes_arredondados"] = (
        not rects or rects_round / len(rects) >= 0.7
    )
    if rects and rects_round / len(rects) < 0.7:
        recomendacoes.append(f"Cantos quadrados demais — arredonde retangulos ({rects_round}/{len(rects)} arredondados).")

    # 12. Annotations humanas (Virgil em algum texto)
    tem_virgil = any(
        el.get("type") == "text" and el.get("fontFamily") == 1
        for el in elementos
    )
    resultados["annotations_humanas"] = tem_virgil
    if not tem_virgil:
        recomendacoes.append("Faltam annotations humanas — adicione 2-3 textos em Virgil (fontFamily=1) "
                             "como '← cuidado aqui' ou 'obs:' pra dar voz manuscrita.")

    # Nota final
    pontos = sum(1 for v in resultados.values() if v)
    nota = int(pontos / len(CRITERIOS) * 100)

    return {
        "nota": nota,
        "criterios": resultados,
        "recomendacoes": recomendacoes,
        "stats": {
            "total_elementos": len(elementos),
            "shapes": len([e for e in elementos if e.get("type") in ("rectangle", "diamond", "ellipse")]),
            "textos": len([e for e in elementos if e.get("type") == "text"]),
            "setas": len([e for e in elementos if e.get("type") == "arrow"]),
            "frames": len([e for e in elementos if e.get("type") == "frame"]),
            "cores_distintas": len(cores),
            "fontes_distintas": len(fontes),
        }
    }


def imprimir_relatorio(resultado):
    print("=" * 60)
    print(f"AUDITORIA DIAGRAMA EXCALIDRAW IMPERATRIZ")
    print("=" * 60)
    print()

    nota = resultado["nota"]
    if nota >= 90:
        nivel = "PREMIUM — pronto pra apresentar a cliente"
    elif nota >= 75:
        nivel = "BOM — ajustes pequenos resolvem"
    elif nota >= 60:
        nivel = "MEDIO — precisa de polimento"
    else:
        nivel = "RASCUNHO — aplicar tema antes de usar"

    print(f"NOTA: {nota}/100 — {nivel}")
    print()

    print("CRITERIOS:")
    for c, ok in resultado["criterios"].items():
        marca = "✓" if ok else "✗"
        print(f"  {marca}  {c.replace('_', ' ')}")
    print()

    if resultado["recomendacoes"]:
        print("RECOMENDACOES:")
        for i, r in enumerate(resultado["recomendacoes"], 1):
            print(f"  {i}. {r}")
        print()

    print("STATS:")
    for k, v in resultado["stats"].items():
        print(f"  {k.replace('_', ' '):>22}: {v}")
    print()


def main():
    parser = argparse.ArgumentParser(description="Audita .excalidraw contra sistema Imperatriz")
    parser.add_argument("--entrada", required=True, help="Arquivo .excalidraw")
    parser.add_argument("--paleta", default="imperatriz")
    parser.add_argument("--json", action="store_true", help="Output em JSON em vez de texto")
    args = parser.parse_args()

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

    if args.json:
        print(json.dumps(resultado, indent=2, ensure_ascii=False))
    else:
        imprimir_relatorio(resultado)


if __name__ == "__main__":
    main()
