#!/usr/bin/env python3
# SSG do Buenas-New: gera HTML estático por página (mata o bundle 10MB),
# com meta/title/canonical/OG por página, JSON-LD Restaurant, imagens WebP,
# lazy-load, acessibilidade, robots.txt e sitemap.xml.
import json, os, re, shutil, html as _html
from PIL import Image

PROJ = os.environ.get("BUENAS_PROJ") or os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SRC = os.path.join(PROJ, "src")
OUT = os.path.join(PROJ, "dist")
ASSETS = os.path.join(OUT, "assets")
SITE = os.environ.get("SITE_URL", "https://buenascarnes.com.br").rstrip("/")

NAME = "Buenas Carnes & Pasta"
ADDR = "Av. Kennedy, 34 — Jardim do Mar, São Bernardo do Campo - SP"
PHONE = "+551125648400"

# ---------- 0) prepara saída ----------
# --- self-heal: nunca perca assets que existem no build anterior mas sumiram da fonte ---
# (ex.: /assets/img/ veio pronto no dist e não está em public/). Antes de apagar o dist,
# devolvemos p/ public/ qualquer arquivo presente no dist e ausente na fonte.
_PUB = os.path.join(PROJ, "public", "assets")
_PREV = os.path.join(OUT, "assets")
if os.path.isdir(_PREV):
    restored = 0
    for root, _, files in os.walk(_PREV):
        rel = os.path.relpath(root, _PREV)
        for f in files:
            if f.endswith(".webp"):
                continue  # webp são derivados; regeneramos
            dstdir = os.path.join(_PUB, rel)
            dst = os.path.join(dstdir, f)
            if not os.path.exists(dst):
                os.makedirs(dstdir, exist_ok=True)
                shutil.copy2(os.path.join(root, f), dst)
                restored += 1
    if restored:
        print("self-heal: %d asset(s) devolvidos p/ public/ antes do rebuild" % restored)

if os.path.isdir(OUT):
    shutil.rmtree(OUT)
os.makedirs(OUT)
shutil.copytree(os.path.join(PROJ, "public", "assets"), ASSETS)
shutil.copy(os.path.join(SRC, "article.css"), os.path.join(ASSETS, "css", "article.css"))

# ---------- 1) imagens -> WebP + mapa de reescrita ----------
def to_webp():
    converted = 0
    for root, _, files in os.walk(ASSETS):
        for f in files:
            ext = os.path.splitext(f)[1].lower()
            if ext not in (".jpg", ".jpeg", ".png"):
                continue
            if "favicon" in f.lower():
                continue
            src = os.path.join(root, f)
            dst = os.path.splitext(src)[0] + ".webp"
            try:
                im = Image.open(src)
                has_alpha = im.mode in ("P", "RGBA", "LA") or (im.mode == "P" and "transparency" in im.info)
                im = im.convert("RGBA") if has_alpha else im.convert("RGB")
                # limita a 1600px de largura (retina o suficiente) p/ "properly size images"
                if im.width > 1600:
                    im = im.resize((1600, round(im.height * 1600 / im.width)), Image.LANCZOS)
                im.save(dst, "WEBP", quality=72, method=6)
                os.remove(src)
                converted += 1
            except Exception:
                pass
    return converted

n_webp = to_webp()

def webpize(text):
    # troca extensão p/ .webp — só se o arquivo .webp realmente existir (conversão pode falhar)
    def rep(m):
        if "favicon" in m.group(0).lower():
            return m.group(0)
        webp = m.group(1) + ".webp"
        if os.path.exists(os.path.join(OUT, webp.lstrip("/"))):
            return webp
        return m.group(0)
    return re.sub(r'(/assets/(?:img|blog|misc|instagram)/[^"\'\s)]+?)\.(?:jpe?g|png)', rep, text, flags=re.I)

# reescreve refs de imagem dentro dos CSS (backgrounds)
for root, _, files in os.walk(os.path.join(ASSETS, "css")):
    for f in files:
        if f.endswith(".css"):
            p = os.path.join(root, f)
            s = open(p, encoding="utf-8", errors="ignore").read()
            open(p, "w", encoding="utf-8").write(webpize(s))

# ---------- 2) head compartilhado ----------
index_src = open(os.path.join(PROJ, "index.html"), encoding="utf-8").read()
css_links = re.findall(r'<link[^>]*rel=["\']stylesheet["\'][^>]*>', index_src, re.I)
icon_links = re.findall(r'<link[^>]*rel=["\'](?:icon|apple-touch-icon)["\'][^>]*>', index_src, re.I)
# CSS bloqueante (mais rápido no LCP com compressão do servidor; o defer atrasava o conteúdo principal)
CSS_BLOCK = "\n".join(css_links) + '\n<link rel="stylesheet" href="/assets/css/article.css" />'
ICON_BLOCK = "\n".join(icon_links)

OG_IMG = SITE + "/assets/img/cropped-favicon-3-192x192.41069208.png"

RESTAURANT_LD = {
    "@context": "https://schema.org",
    "@type": "Restaurant",
    "name": NAME,
    "description": "Restaurante de carnes nobres na parrilla e massas artesanais em São Bernardo do Campo.",
    "servesCuisine": ["Argentina", "Italiana", "Churrasco", "Carnes", "Massas artesanais"],
    "priceRange": "$$$",
    "url": SITE + "/",
    "telephone": PHONE,
    "acceptsReservations": True,
    "address": {
        "@type": "PostalAddress", "streetAddress": "Av. Kennedy, 34",
        "addressLocality": "São Bernardo do Campo", "addressRegion": "SP",
        "postalCode": "09750-651", "addressCountry": "BR",
    },
    "areaServed": "São Bernardo do Campo",
    "aggregateRating": {"@type": "AggregateRating", "ratingValue": "4.8", "reviewCount": "150"},
}

def head(title, desc, path, og_image=OG_IMG, extra_ld=None, og_type="website"):
    canonical = SITE + path
    lds = [RESTAURANT_LD] + (extra_ld or [])
    ld_tags = "\n".join(
        '<script type="application/ld+json">%s</script>' % json.dumps(x, ensure_ascii=False) for x in lds)
    return """<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{title}</title>
<meta name="description" content="{desc}" />
<link rel="canonical" href="{canonical}" />
<meta name="robots" content="index, follow" />
<meta property="og:type" content="{og_type}" />
<meta property="og:site_name" content="{name}" />
<meta property="og:title" content="{title}" />
<meta property="og:description" content="{desc}" />
<meta property="og:url" content="{canonical}" />
<meta property="og:image" content="{og_image}" />
<meta property="og:locale" content="pt_BR" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="{title}" />
<meta name="twitter:description" content="{desc}" />
<meta name="twitter:image" content="{og_image}" />
{icons}
{css}
{ld}
</head>
<body>
<a class="bn-skip" href="#conteudo">Pular para o conteúdo</a>
""".format(title=_html.escape(title), desc=_html.escape(desc), canonical=canonical,
           og_type=og_type, name=NAME, og_image=og_image, icons=ICON_BLOCK, css=CSS_BLOCK, ld=ld_tags)

FOOT = "\n</body>\n</html>\n"

# ---------- 3) acessibilidade + lazy no conteúdo ----------
def a11y(htmlstr):
    # remove scripts do WordPress/Elementor (site estático não precisa; evitam 404 e erros de console)
    htmlstr = re.sub(r"<script\b[^>]*>.*?</script>", "", htmlstr, flags=re.I | re.S)
    htmlstr = re.sub(r"<script\b[^>]*/?>", "", htmlstr, flags=re.I)
    # lazy + async decoding em todas as <img>
    htmlstr = re.sub(r"<img\b(?![^>]*loading=)", '<img loading="lazy" decoding="async"', htmlstr, flags=re.I)
    # remove o skip-link do WP (não focável) — temos o nosso .bn-skip
    htmlstr = re.sub(r'<a class="skip-link[^"]*"[^>]*>.*?</a>', "", htmlstr, flags=re.I | re.S)
    # remove aria-controls inválido (aponta p/ id inexistente) do burger
    htmlstr = re.sub(r'\s*aria-controls="[^"]*"', "", htmlstr, flags=re.I)
    return htmlstr

WRAP_OPEN = '<div class="elementor-kit-6 elementor-page elementor-default"><main id="conteudo">'
WRAP_CLOSE = '</main></div>'

HEADER = a11y(open(os.path.join(SRC, "_header.html"), encoding="utf-8").read())
FOOTER = a11y(open(os.path.join(SRC, "_footer.html"), encoding="utf-8").read())

def page_shell(inner, active=None):
    header = HEADER
    if active:
        # marca o item de menu da página atual como ativo (todas as cópias do nav)
        header = re.sub(
            r'(<a href="%s"[^>]*class="elementor-item)(")' % re.escape(active),
            r'\1 elementor-item-active\2', header)
    return webpize(WRAP_OPEN + header + inner + FOOTER + WRAP_CLOSE)

def write(path, content):
    d = os.path.join(OUT, path.lstrip("/"))
    os.makedirs(d, exist_ok=True)
    open(os.path.join(d, "index.html"), "w", encoding="utf-8").write(content)

# ---------- 4) páginas capturadas ----------
def body(slug):
    return a11y(open(os.path.join(SRC, "%s-body.html" % slug), encoding="utf-8").read())

PAGES = {
    "/": ("Buenas — Restaurante de Carnes e Massas em São Bernardo do Campo",
          "Carnes nobres na parrilla argentina e massas artesanais italianas em São Bernardo do Campo. Reserve sua mesa na Buenas Carnes & Pasta.",
          "home"),
    "/sobre-nos/": ("Sobre a Buenas — Parrilla Argentina e Massas Italianas | São Bernardo",
          "Conheça a Buenas: a fusão da parrilla argentina com a tradição italiana das massas artesanais, em São Bernardo do Campo.",
          "sobre-nos"),
    "/contato/": ("Contato e Reservas — Buenas Carnes & Pasta | São Bernardo do Campo",
          "Endereço, telefone, WhatsApp e reservas da Buenas Carnes & Pasta em São Bernardo do Campo. Venha nos visitar.",
          "contato"),
}
for path, (title, desc, slug) in PAGES.items():
    # a página capturada JÁ tem header+footer+wrapper elementor-kit-6; só embrulha p/ o skip-link
    inner = '<div id="conteudo">' + webpize(body(slug)) + '</div>'
    doc = head(title, desc, path) + inner + FOOT
    write(path, doc)

# ---------- 5) blog: listagem + artigos ----------
articles = json.load(open(os.path.join(SRC, "blog-data.json"), encoding="utf-8"))

def esc(s): return _html.escape(s or "", quote=True)

def blog_list_html():
    cards = []
    for a in articles:
        cards.append(
            '<a class="bn-card" href="/{slug}/"><div class="bn-card-img" style="background-image:url(&quot;{img}&quot;)"></div>'
            '<div class="bn-card-body"><span class="bn-card-date">{date}</span>'
            '<h2 class="bn-card-title">{title}</h2><p class="bn-card-excerpt">{exc}</p>'
            '<span class="bn-card-link">Ler artigo →</span></div></a>'.format(
                slug=a["slug"], img=a["image"], date=esc(a["date"]), title=esc(a["title"]), exc=esc(a["excerpt"])))
    return ('<section class="bn-hero"><h1>Blog</h1><p>Histórias, cortes e sabores da Buenas Carnes &amp; Pasta</p></section>'
            '<div class="bn-wrap"><div class="bn-grid">' + "".join(cards) + '</div></div>')

write("/blog/", head("Blog — Buenas Carnes & Pasta",
      "Dicas de cortes, harmonizações, massas artesanais e experiências gastronômicas no blog da Buenas Carnes & Pasta.",
      "/blog/") + page_shell(blog_list_html(), active="/blog/") + FOOT)

# ---------- Eventos ----------
eventos_html = webpize(open(os.path.join(SRC, "eventos-content.html"), encoding="utf-8").read())
write("/eventos/", head("Eventos — Buenas Carnes & Pasta",
      "Espaço para eventos, confraternizações, celebrações e eventos corporativos em São Bernardo do Campo. Cardápio ítalo-portenho sob medida no Buenas Carnes & Pasta.",
      "/eventos/") + page_shell(eventos_html, active="/eventos/") + FOOT)

def related(idx, n=4):
    out = []
    for k in range(1, len(articles)):
        if len(out) >= n: break
        out.append(articles[(idx + k) % len(articles)])
    return out

for i, a in enumerate(articles):
    rel = related(i)
    rel_html = "".join(
        '<a class="bn-related-item" href="/{slug}/"><span class="bn-related-thumb" style="background-image:url(&quot;{img}&quot;)"></span>'
        '<span class="bn-related-info"><span class="bn-related-date">{date}</span>'
        '<span class="bn-related-title">{title}</span></span></a>'.format(
            slug=r["slug"], img=r["image"], date=esc(r["date"]), title=esc(r["title"])) for r in rel)
    content = a11y(a["content"])
    # FAQ visível (renderizado como HTML semântico após o corpo do artigo)
    faq = a.get("faq") or []
    faq_html = ""
    if faq:
        items = "".join(
            '<details class="bn-faq-item"{op}>'
            '<summary><h3 class="bn-faq-q">{q}</h3><span class="bn-faq-ico" aria-hidden="true"></span></summary>'
            '<div class="bn-faq-a"><p>{ans}</p></div></details>'.format(
                q=esc(f["q"]), ans=esc(f["a"]), op=(" open" if i == 0 else ""))
            for i, f in enumerate(faq))
        faq_html = ('<section class="bn-faq" aria-labelledby="faq-h">'
                    '<span class="bn-faq-eyebrow">FAQ</span>'
                    '<h2 id="faq-h">Perguntas frequentes</h2>'
                    '<div class="bn-faq-list">' + items + '</div></section>')
    inner = (
        '<section class="bn-article-hero" style="background-image:linear-gradient(rgba(0,0,0,.42),rgba(0,0,0,.66)),url(&quot;{img}&quot;)">'
        '<div class="bn-article-hero-inner"><a class="bn-back" href="/blog/">← Voltar ao blog</a>'
        '<span class="bn-article-date">{date}</span><h1 class="bn-article-title">{title}</h1></div></section>'
        '<div class="bn-article-wrap"><div class="bn-article-main"><article class="bn-article article-content">{content}</article>{faq}</div>'
        '<aside class="bn-sidebar"><div class="bn-related"><h2 class="bn-related-h">Artigos relacionados</h2>{rel}</div></aside></div>'
        '<section class="bn-cta"><div class="bn-cta-inner"><h2>Viva a experiência Buenas</h2>'
        '<p>Carnes nobres na parrilla e massas artesanais, num só lugar. Reserve sua mesa e venha provar.</p>'
        '<div class="bn-cta-btns"><a class="bn-btn bn-btn-primary" href="/contato/">Fazer uma reserva</a>'
        '<a class="bn-btn bn-btn-ghost" href="/blog/">Ver mais artigos</a></div></div></section>'
    ).format(img=a["image"], date=esc(a["date"]), title=esc(a["title"]), content=content, faq=faq_html, rel=rel_html)

    article_ld = {
        "@context": "https://schema.org", "@type": "Article",
        "headline": a["title"], "image": SITE + a["image"],
        "datePublished": a["date"], "author": {"@type": "Organization", "name": NAME},
        "publisher": {"@type": "Organization", "name": NAME},
        "mainEntityOfPage": SITE + "/" + a["slug"] + "/",
    }
    lds = [article_ld]
    # FAQPage schema (rich snippet de perguntas no Google) — só quando há FAQ
    if faq:
        lds.append({
            "@context": "https://schema.org", "@type": "FAQPage",
            "mainEntity": [{
                "@type": "Question", "name": f["q"],
                "acceptedAnswer": {"@type": "Answer", "text": f["a"]},
            } for f in faq],
        })
    doc = head(a["title"] + " — Blog Buenas", a["excerpt"], "/" + a["slug"] + "/",
               og_image=SITE + a["image"], extra_ld=lds, og_type="article") \
          + page_shell(inner) + FOOT
    write("/" + a["slug"] + "/", doc)

# ---------- 6) skip link CSS (injeta no article.css já copiado) ----------
skip_css = ("\n.bn-skip{position:absolute;left:-9999px;top:0;z-index:9999;background:#c05b52;color:#fff;"
            "padding:10px 16px;border-radius:0 0 8px 0}.bn-skip:focus{left:0}\n")
with open(os.path.join(ASSETS, "css", "article.css"), "a", encoding="utf-8") as f:
    f.write(skip_css)

# ---------- 7) robots.txt + sitemap.xml ----------
open(os.path.join(OUT, "robots.txt"), "w").write(
    "User-agent: *\nAllow: /\nSitemap: %s/sitemap.xml\n" % SITE)

urls = ["/", "/sobre-nos/", "/eventos/", "/contato/", "/blog/"] + ["/" + a["slug"] + "/" for a in articles]
sm = ['<?xml version="1.0" encoding="UTF-8"?>',
      '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">']
for u in urls:
    pr = "1.0" if u == "/" else ("0.8" if u in ("/sobre-nos/", "/eventos/", "/contato/", "/blog/") else "0.6")
    sm.append("<url><loc>%s%s</loc><priority>%s</priority></url>" % (SITE, u, pr))
sm.append("</urlset>")
open(os.path.join(OUT, "sitemap.xml"), "w", encoding="utf-8").write("\n".join(sm))

print("SSG OK -> %s" % OUT)
print("  webp convertidas:", n_webp)
print("  páginas:", 4 + len(articles), "(home/sobre/contato/blog +", len(articles), "artigos)")
print("  robots.txt + sitemap.xml (%d urls)" % len(urls))
