# -*- coding: utf-8 -*-
"""Gera os dois catalog-fonts.ts (public e admin) a partir de uma lista unica.

Os dois apps sao projetos Next separados, entao cada um tem a sua copia.
Gerar os dois daqui garante que as CHAVES (que vao para o banco) nunca
divirjam entre o seletor e a vitrine.

Nenhum dos dois usa next/font/google: ele baixava as 65 familias do Google
durante o build, e o deploy caia quando o Google mudava o formato do
endereco. A vitrine e o seletor do admin pedem as fontes ao Google no
navegador, por <link>, que nao tem como derrubar o build.
"""
import io
import json

# (chave, ExportName, varName, rotulo, weight)
# weight None = fonte variavel (next/font dispensa o peso)
GRUPOS = [
    ("Serifadas e editoriais", [
        ("fraunces", "Fraunces", "fraunces", "Fraunces — serifada com personalidade", None),
        ("playfair-display", "Playfair_Display", "playfairDisplay", "Playfair Display — editorial clássica", None),
        ("instrument-serif", "Instrument_Serif", "instrumentSerif", "Instrument Serif — serifada enxuta", '"400"'),
        ("lora", "Lora", "lora", "Lora — leitura confortável", None),
        ("eb-garamond", "EB_Garamond", "ebGaramond", "EB Garamond — clássica atemporal", None),
        ("cormorant-garamond", "Cormorant_Garamond", "cormorantGaramond", "Cormorant Garamond — refinada e fina", '["300", "400", "500", "600", "700"]'),
        ("libre-baskerville", "Libre_Baskerville", "libreBaskerville", "Libre Baskerville — livro tradicional", '["400", "700"]'),
        ("crimson-pro", "Crimson_Pro", "crimsonPro", "Crimson Pro — humanista elegante", None),
        ("merriweather", "Merriweather", "merriweather", "Merriweather — robusta e legível", '["300", "400", "700", "900"]'),
        ("source-serif-4", "Source_Serif_4", "sourceSerif4", "Source Serif — técnica com serifa", None),
        ("spectral", "Spectral", "spectral", "Spectral — serifada contemporânea", '["200", "300", "400", "500", "600", "700", "800"]'),
        ("newsreader", "Newsreader", "newsreader", "Newsreader — jornalística", None),
        ("literata", "Literata", "literata", "Literata — editorial moderna", None),
        ("dm-serif-display", "DM_Serif_Display", "dmSerifDisplay", "DM Serif Display — títulos de alto contraste", '"400"'),
        ("bodoni-moda", "Bodoni_Moda", "bodoniModa", "Bodoni Moda — luxo e moda", None),
        ("marcellus", "Marcellus", "marcellus", "Marcellus — romana sofisticada", '"400"'),
    ]),
    ("Display e impacto", [
        ("syne", "Syne", "syne", "Syne — display marcante", None),
        ("unbounded", "Unbounded", "unbounded", "Unbounded — geométrica forte", None),
        ("anton", "Anton", "anton", "Anton — condensada de alto impacto", '"400"'),
        ("bebas-neue", "Bebas_Neue", "bebasNeue", "Bebas Neue — caixa alta esportiva", '"400"'),
        ("archivo-black", "Archivo_Black", "archivoBlack", "Archivo Black — peso máximo", '"400"'),
        ("abril-fatface", "Abril_Fatface", "abrilFatface", "Abril Fatface — cartaz clássico", '"400"'),
        ("alfa-slab-one", "Alfa_Slab_One", "alfaSlabOne", "Alfa Slab One — serifa pesada", '"400"'),
        ("righteous", "Righteous", "righteous", "Righteous — retrô arredondada", '"400"'),
        ("darker-grotesque", "Darker_Grotesque", "darkerGrotesque", "Darker Grotesque — condensada alternativa", None),
    ]),
    ("Sans modernas", [
        ("bricolage-grotesque", "Bricolage_Grotesque", "bricolageGrotesque", "Bricolage Grotesque — contemporânea", None),
        ("space-grotesk", "Space_Grotesk", "spaceGrotesk", "Space Grotesk — técnica", None),
        ("sora", "Sora", "sora", "Sora — moderna equilibrada", None),
        ("manrope", "Manrope", "manrope", "Manrope — limpa e arredondada", None),
        ("dm-sans", "DM_Sans", "dmSans", "DM Sans — neutra elegante", None),
        ("outfit", "Outfit", "outfit", "Outfit — geométrica leve", None),
        ("figtree", "Figtree", "figtree", "Figtree — amigável", None),
        ("epilogue", "Epilogue", "epilogue", "Epilogue — contrastada", None),
        ("work-sans", "Work_Sans", "workSans", "Work Sans — versátil", None),
        ("inter", "Inter", "inter", "Inter — padrão de interface", None),
        ("plus-jakarta-sans", "Plus_Jakarta_Sans", "plusJakartaSans", "Plus Jakarta Sans — sofisticada", None),
        ("urbanist", "Urbanist", "urbanist", "Urbanist — geométrica minimalista", None),
        ("jost", "Jost", "jost", "Jost — inspirada na Futura", None),
        ("lexend", "Lexend", "lexend", "Lexend — foco em legibilidade", None),
        ("albert-sans", "Albert_Sans", "albertSans", "Albert Sans — geométrica suave", None),
        ("onest", "Onest", "onest", "Onest — neutra atual", None),
        ("schibsted-grotesk", "Schibsted_Grotesk", "schibstedGrotesk", "Schibsted Grotesk — editorial digital", None),
        ("hanken-grotesk", "Hanken_Grotesk", "hankenGrotesk", "Hanken Grotesk — grotesca amigável", None),
        ("archivo", "Archivo", "archivo", "Archivo — grotesca de alto desempenho", None),
    ]),
    ("Sans clássicas", [
        ("poppins", "Poppins", "poppins", "Poppins — geométrica popular", '["300", "400", "500", "600", "700"]'),
        ("montserrat", "Montserrat", "montserrat", "Montserrat — urbana e conhecida", None),
        ("raleway", "Raleway", "raleway", "Raleway — elegante e fina", None),
        ("nunito", "Nunito", "nunito", "Nunito — arredondada acolhedora", None),
        ("nunito-sans", "Nunito_Sans", "nunitoSans", "Nunito Sans — versão sóbria", None),
        ("rubik", "Rubik", "rubik", "Rubik — cantos suaves", None),
        ("karla", "Karla", "karla", "Karla — grotesca compacta", None),
        ("lato", "Lato", "lato", "Lato — corporativa clássica", '["300", "400", "700", "900"]'),
        ("open-sans", "Open_Sans", "openSans", "Open Sans — neutra universal", None),
        ("roboto", "Roboto", "roboto", "Roboto — padrão do Android", None),
        ("source-sans-3", "Source_Sans_3", "sourceSans3", "Source Sans — limpa e técnica", None),
        ("quicksand", "Quicksand", "quicksand", "Quicksand — leve e arredondada", None),
    ]),
    ("Monoespaçadas", [
        ("jetbrains-mono", "JetBrains_Mono", "jetbrainsMono", "JetBrains Mono — monoespaçada moderna", None),
        ("ibm-plex-mono", "IBM_Plex_Mono", "ibmPlexMono", "IBM Plex Mono — técnica corporativa", '["300", "400", "500", "600", "700"]'),
        ("space-mono", "Space_Mono", "spaceMono", "Space Mono — retrô futurista", '["400", "700"]'),
        ("roboto-mono", "Roboto_Mono", "robotoMono", "Roboto Mono — monoespaçada neutra", None),
    ]),
    ("Manuscritas", [
        ("caveat", "Caveat", "caveat", "Caveat — manuscrita espontânea", None),
        ("dancing-script", "Dancing_Script", "dancingScript", "Dancing Script — caligráfica alegre", None),
        ("pacifico", "Pacifico", "pacifico", "Pacifico — script descontraída", '"400"'),
        ("sacramento", "Sacramento", "sacramento", "Sacramento — fina e delicada", '"400"'),
        ("great-vibes", "Great_Vibes", "greatVibes", "Great Vibes — caligrafia formal", '"400"'),
    ]),
]

TODAS = [f for _, fontes in GRUPOS for f in fontes]

GENERICO_DO_GRUPO = {
    "Serifadas e editoriais": "serif",
    "Display e impacto": "sans-serif",
    "Sans modernas": "sans-serif",
    "Sans clássicas": "sans-serif",
    "Monoespaçadas": "monospace",
    "Manuscritas": "cursive",
}


def pesos_para_o_google(w):
    """Os pesos que a vitrine pede ao Google Fonts.

    Fonte variavel (None) recebe os quatro pesos que a loja usa; todas as
    variaveis da lista cobrem 400..700. Fonte estatica recebe os pesos que ela
    tem."""
    if w is None:
        return ["400", "500", "600", "700"]
    valor = json.loads(w)
    return [valor] if isinstance(valor, str) else list(valor)


def gerar_public():
    """O lado da vitrine nao instancia fonte nenhuma.

    Com next/font, as 65 familias viravam 112 KB de CSS bloqueante em toda loja
    (365 @font-face) para usar duas. Aqui vai so a tabela: a loja pede ao
    Google Fonts a dupla escolhida, por <link>, e quem nao escolheu fonte nao
    pede nada. As chaves continuam as mesmas do admin."""
    linhas = []
    for grupo, fontes in GRUPOS:
        generico = GENERICO_DO_GRUPO[grupo]
        for chave, export, _var, _rotulo, w in fontes:
            familia = export.replace("_", " ")
            pesos = ", ".join('"%s"' % p for p in pesos_para_o_google(w))
            linhas.append('  "%s": { familia: "%s", pesos: [%s], generico: "%s" },' % (chave, familia, pesos, generico))
    tabela = "\n".join(linhas)
    return """/**
 * Fontes que o lojista pode escolher na personalizacao avancada.
 *
 * ESTE ARQUIVO E GERADO por scripts/gen_fonts.py, junto com o do admin, para
 * as CHAVES (gravadas em `catalog_font_heading` e `catalog_font_body`) nunca
 * divergirem entre o seletor e a vitrine. Nunca renomeie uma chave sem migrar
 * os dados: a loja que a usava cairia no padrao silenciosamente.
 *
 * A vitrine nao instancia fonte com next/font: isso punha 112 KB de CSS
 * bloqueante em toda loja para usar duas familias. Aqui vai so a tabela, e a
 * loja pede ao Google Fonts a dupla escolhida (ver components/FontesDaLoja).
 */

type FonteDaLoja = { familia: string; pesos: string[]; generico: string };

const FONTES: Record<string, FonteDaLoja> = {
%s
};

function fonte(chave: string | null | undefined) {
  const limpa = String(chave || "").trim();
  if (!limpa) return null;
  return FONTES[limpa] || null;
}

export function resolveCatalogFontFamily(chave: string | null | undefined) {
  const f = fonte(chave);
  if (!f) return null;
  return `"${f.familia}", ${f.generico}`;
}

/** A URL do CSS do Google Fonts com as familias pedidas, ou nulo se nenhuma. */
export function hrefDasFontes(chaves: Array<string | null | undefined>) {
  const vistas = new Set<string>();
  const partes: string[] = [];
  for (const chave of chaves) {
    const f = fonte(chave);
    if (!f || vistas.has(f.familia)) continue;
    vistas.add(f.familia);
    partes.push(`family=${encodeURIComponent(f.familia).replace(/%%20/g, "+")}:wght@${f.pesos.join(";")}`);
  }
  if (!partes.length) return null;
  return `https://fonts.googleapis.com/css2?${partes.join("&")}&display=swap`;
}

/**
 * Resolve o par de fontes da loja.
 *
 * Quando a personalizacao avancada esta desligada devolve nulo nos dois, e a
 * vitrine nao aplica nenhum estilo de fonte — a loja fica exatamente como era.
 */
export function resolveCatalogFonts(params: {
  enabled: boolean;
  heading?: string | null;
  body?: string | null;
}) {
  if (!params.enabled) return { heading: null, body: null };
  return {
    heading: resolveCatalogFontFamily(params.heading),
    body: resolveCatalogFontFamily(params.body),
  };
}
""" % tabela


def pesos_do_seletor(w):
    """Os pesos que o seletor do admin pede: so os que a lista desenha.

    A opcao aberta e font-medium (500) e o botao fechado e 400. Fonte sem o
    500 (Merriweather, Lato...) cai no 400, que e o que o navegador ja
    escolhia quando o next/font carregava todos os pesos."""
    return [p for p in pesos_para_o_google(w) if p in ("400", "500")] or pesos_para_o_google(w)[:1]


def gerar_admin():
    partes = []
    familias = []
    for grupo, fontes in GRUPOS:
        generico = GENERICO_DO_GRUPO[grupo]
        partes.append('  { tipo: "grupo", label: "%s" },' % grupo)
        for chave, export, _var, rotulo, w in fontes:
            familia = export.replace("_", " ")
            partes.append(
                "  { tipo: \"fonte\", key: \"%s\", label: \"%s\", fontFamily: '\"%s\", %s' },"
                % (chave, rotulo, familia, generico)
            )
            familias.append("family=%s:wght@%s" % (familia.replace(" ", "+"), ";".join(pesos_do_seletor(w))))
    lista = "\n".join(partes)
    href = "https://fonts.googleapis.com/css2?" + "&".join(familias) + "&display=swap"
    return """/**
 * Fontes que o lojista pode escolher na personalizacao avancada.
 *
 * A CHAVE de cada fonte e gravada no banco (`catalog_font_heading` e
 * `catalog_font_body`). Nunca renomeie uma chave sem migrar os dados: a loja
 * que a usava cairia no padrao silenciosamente.
 *
 * ESTE ARQUIVO E GERADO. A lista vive em scripts/gen_fonts.py e escreve os
 * dois lados de uma vez (public e admin), justamente para as chaves nao
 * divergirem entre o seletor do admin e a vitrine que aplica a fonte.
 *
 * Nada de next/font/google aqui: ele baixava as 65 familias do Google durante
 * o build, e o deploy caia quando o Google mudava o formato do endereco. A
 * previa do seletor pede as fontes ao Google no navegador, pela folha
 * HREF_DAS_FONTES_DO_SELETOR, igual a vitrine faz com a dupla da loja. A
 * folha so declara as fontes; o navegador baixa o arquivo de cada uma apenas
 * quando a lista aberta desenha aquele nome.
 */

/**
 * Cabecalho de grupo (nao selecionavel) ou uma fonte de verdade.
 *
 * O campo `tipo` e obrigatorio nos dois lados de proposito: e ele que deixa o
 * TypeScript estreitar a uniao. Com um campo opcional o compilador nao
 * consegue separar os dois casos e o `label` vira erro.
 */
export type CatalogFontOption =
  | { tipo: "grupo"; label: string }
  | { tipo: "fonte"; key: string; label: string; fontFamily: string };

/** A folha do Google com as 65 familias, nos pesos que o seletor desenha. */
export const HREF_DAS_FONTES_DO_SELETOR =
  "%s";

export const CATALOG_FONT_OPTIONS: CatalogFontOption[] = [
%s
];
""" % (href, lista)


BASE = "C:/Users/Natan/Downloads/ktalogu-main/"

io.open(BASE + "apps/public/src/lib/catalog-fonts.ts", "w", encoding="utf-8", newline="\n").write(gerar_public())
io.open(BASE + "apps/admin/src/lib/catalog-fonts.ts", "w", encoding="utf-8", newline="\n").write(gerar_admin())

print("fontes:", len(TODAS))
print("grupos:", len(GRUPOS))
chaves = [f[0] for f in TODAS]
assert len(chaves) == len(set(chaves)), "chave duplicada"
print("ok, chaves unicas")
