#!/usr/bin/env python3
# Captura a Home do WP (localhost:8090) para Buenas-New com fidelidade maxima.
# Agnostico a aspas simples/duplas (WP emite as duas).
import os, re, html, shutil
import requests
from urllib.parse import urljoin, urlparse

BASE = "http://localhost:8090/"
PROJ = "/Users/Bayerl/Documents/Projetos/Buenas-New"
ASSETS = os.path.join(PROJ, "public", "assets")
UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0 Safari/537.36"
S = requests.Session(); S.headers.update({"User-Agent": UA})
MAP = {}; FAIL = []
import hashlib

# limpa assets antigos
if os.path.isdir(ASSETS): shutil.rmtree(ASSETS)
os.makedirs(ASSETS, exist_ok=True)

def norm(u, base=BASE):
    if not u: return None
    u = u.strip()
    if u.startswith(("data:", "#", "mailto:", "tel:", "javascript:")): return None
    for d in ("https://www.buenascarnes.com.br","http://www.buenascarnes.com.br",
              "https://buenascarnes.com.br","http://buenascarnes.com.br"):
        u = u.replace(d, BASE.rstrip("/"))
    if u.startswith("//"): u = "https:" + u
    return urljoin(base, u)

def subdir_for(path, ctype=""):
    ext = os.path.splitext(urlparse(path).path)[1].lower()
    if ext == ".css" or "text/css" in ctype: return "css"
    if ext in (".woff",".woff2",".ttf",".otf",".eot"): return "fonts"
    if ext == ".js" or "javascript" in ctype: return "js"
    if ext in (".png",".jpg",".jpeg",".gif",".webp",".svg",".ico",".avif") or "image/" in ctype: return "img"
    return "misc"

def safe_name(url, sub):
    base = os.path.basename(urlparse(url).path) or "index"
    base = re.sub(r"[^A-Za-z0-9._-]", "_", base)
    root, ext = os.path.splitext(base)
    if sub == "css" and ext != ".css": ext = ".css"
    if sub == "fonts" and not ext: ext = ".woff2"
    h = hashlib.md5(url.encode()).hexdigest()[:8]
    return "%s.%s%s" % (root[:60] or "f", h, ext or "")

def download(url, process_css=True):
    n = norm(url)
    if not n: return url
    if n in MAP: return MAP[n]
    try:
        r = S.get(n, timeout=30)
        if r.status_code != 200: FAIL.append((r.status_code, n)); return url
    except Exception as e:
        FAIL.append((str(e)[:30], n)); return url
    ctype = r.headers.get("content-type", "")
    sub = subdir_for(n, ctype)
    name = safe_name(n, sub)
    local = "/assets/%s/%s" % (sub, name)
    MAP[n] = local
    outdir = os.path.join(ASSETS, sub); os.makedirs(outdir, exist_ok=True)
    data = r.content
    if sub == "css" and process_css:
        data = rewrite_css(r.text, n).encode("utf-8")
    with open(os.path.join(outdir, name), "wb") as f: f.write(data)
    return local

def rewrite_css(text, css_url):
    text = re.sub(r"url\(\s*['\"]?([^)'\"]+)['\"]?\s*\)",
                  lambda m: "url(%s)" % download(urljoin(css_url, m.group(1))), text)
    text = re.sub(r'@import\s+["\']([^"\']+)["\']',
                  lambda m: '@import "%s"' % download(urljoin(css_url, m.group(1))), text)
    return text

def rewrite_srcset(val, base=BASE):
    out = []
    for part in val.split(","):
        part = part.strip()
        if not part: continue
        bits = part.split()
        out.append(" ".join([download(urljoin(base, bits[0]))] + bits[1:]))
    return ", ".join(out)

Q = r'''=\s*(["'])(.*?)\1'''  # captura valor de atributo (grupo 2)

def get_attr(tag, name):
    m = re.search(name + Q, tag, re.I)
    return m.group(2) if m else None

def set_attr(tag, name, val):
    val = val.replace('"', '&quot;')
    m = re.search(name + Q, tag, re.I)
    if m:
        return tag[:m.start()] + '%s="%s"' % (name, val) + tag[m.end():]
    return re.sub(r"\s*/?>\s*$", ' %s="%s">' % (name, val), tag, count=1)

print("baixando home...")
r = S.get(BASE, timeout=30); r.raise_for_status()
doc = r.text

# 1) lazy-load Elementor/LiteSpeed: data-src -> src
def fix_lazy(m):
    tag = m.group(0)
    ds = get_attr(tag, "data-src") or get_attr(tag, "data-lazy-src")
    if ds: tag = set_attr(tag, "src", ds)
    dss = get_attr(tag, "data-srcset") or get_attr(tag, "data-lazysrcset")
    if dss: tag = set_attr(tag, "srcset", dss)
    return tag
doc = re.sub(r"<img\b[^>]*>", fix_lazy, doc, flags=re.I)
# revela elementos com animacao de entrada (opacity:0 sem JS)
doc = doc.replace("elementor-invisible", "")

# revela backgrounds lazy do Elementor: adiciona 'e-lazyloaded' nos containers e-con
# (e o que o JS faria no scroll; sem isso a regra :not(.e-lazyloaded){bg:none!important} esconde as imagens)
def add_lazyloaded(m):
    q, cls = m.group(1), m.group(2)
    if "e-con" in cls and "e-lazyloaded" not in cls:
        cls = cls + " e-lazyloaded"
    return "class=%s%s%s" % (q, cls, q)
doc = re.sub(r'class=(["\'])(.*?)\1', add_lazyloaded, doc, flags=re.I | re.S)

# 2) <link> css/icon/preload
def repl_link(m):
    tag = m.group(0)
    href = get_attr(tag, "href")
    rel = (get_attr(tag, "rel") or "").lower()
    as_ = (get_attr(tag, "as") or "").lower()
    if not href: return tag
    if any(k in rel for k in ("stylesheet","icon","preload","apple-touch")) or as_ in ("style","font","image"):
        return set_attr(tag, "href", download(href))
    return tag
doc = re.sub(r"<link\b[^>]*?>", repl_link, doc, flags=re.I)

# 3) <style> inline
doc = re.sub(r"(<style\b[^>]*>)(.*?)(</style>)",
             lambda m: m.group(1) + rewrite_css(m.group(2), BASE) + m.group(3), doc, flags=re.I|re.S)

# 4) <img>
def repl_img(m):
    tag = m.group(0)
    src = get_attr(tag, "src")
    if src: tag = set_attr(tag, "src", download(src))
    ss = get_attr(tag, "srcset")
    if ss: tag = set_attr(tag, "srcset", rewrite_srcset(ss))
    return tag
doc = re.sub(r"<img\b[^>]*>", repl_img, doc, flags=re.I)

# 5) <source srcset>
def repl_source(m):
    tag = m.group(0); ss = get_attr(tag, "srcset")
    return set_attr(tag, "srcset", rewrite_srcset(ss)) if ss else tag
doc = re.sub(r"<source\b[^>]*>", repl_source, doc, flags=re.I)

# 6) inline style="...url()..."
def repl_inline_style(m):
    q, val = m.group(1), m.group(2)
    newv = re.sub(r"url\(\s*['\"]?([^)'\"]+)['\"]?\s*\)",
                  lambda mm: "url(%s)" % download(urljoin(BASE, mm.group(1))), val)
    return "style=%s%s%s" % (q, newv, q)
doc = re.sub(r"style" + r'''=(["'])((?:(?!\1).)*url\((?:(?!\1).)*)\1''', repl_inline_style, doc, flags=re.I)

# 6.5) Motion Effects: o bg vai num .elementor-motion-effects-layer criado por JS.
# Emitimos regra extra aplicando a mesma imagem no elemento base (existe no HTML estatico).
def fix_motion_bg(text):
    # escopa por .e-loop-item-NNN (cada card do loop compartilha o mesmo id de elemento)
    pat = re.compile(
        r'(\.e-loop-item-\d+\s+\.elementor-element\.elementor-element-[0-9a-fA-F]+)'
        r'(?::not\([^)]*\))?[^{}]*?\.elementor-motion-effects-layer\s*\{\s*'
        r'background-image\s*:\s*(url\([^)]*\))\s*;?\s*\}', re.I)
    def add(m):
        scoped, url = m.group(1), m.group(2)
        extra = ('%s{background-image:%s !important;background-size:cover !important;'
                 'background-position:center center !important;background-repeat:no-repeat !important;}' % (scoped, url))
        return m.group(0) + extra
    return pat.sub(add, text)
doc = fix_motion_bg(doc)

# 6.6) externaliza TODO CSS inline (<style>) num arquivo real — <style> em
# dangerouslySetInnerHTML nao e aplicado pelo browser. Preserva a ordem do documento.
inline_css = []
doc = re.sub(r"<style\b[^>]*>(.*?)</style>",
             lambda m: (inline_css.append(m.group(1)), "")[1], doc, flags=re.I|re.S)
os.makedirs(os.path.join(ASSETS, "css"), exist_ok=True)
with open(os.path.join(ASSETS, "css", "_inline_captured.css"), "w") as f:
    f.write("\n".join(inline_css))

# 7) head css + body
head_match = re.search(r"<head\b[^>]*>(.*?)</head>", doc, re.I|re.S)
head = head_match.group(1) if head_match else ""
head_bits = []
for m in re.finditer(r"<link\b[^>]*?>", head, re.I): head_bits.append(m.group(0))
# CSS inline capturado (head + body) carregado por ultimo p/ preservar override
head_bits.append('<link rel="stylesheet" href="/assets/css/_inline_captured.css" />')
tm = re.search(r"<title\b[^>]*>(.*?)</title>", head, re.I|re.S)
title = tm.group(1).strip() if tm else "Buenas"

bm = re.search(r"<body\b([^>]*)>(.*?)</body>", doc, re.I|re.S)
body_attrs = bm.group(1) if bm else ""
body_inner = bm.group(2) if bm else doc
bc = get_attr("<x " + body_attrs + ">", "class") or ""

# 8) escreve
os.makedirs(os.path.join(PROJ, "src"), exist_ok=True)
with open(os.path.join(PROJ, "src", "home-body.html"), "w") as f:
    f.write('<div class="%s">\n%s\n</div>' % (html.escape(bc, quote=True), body_inner))

with open(os.path.join(PROJ, "index.html"), "w") as f:
    f.write("""<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>%s</title>
%s
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
""" % (title, "\n".join(head_bits)))

print("OK - assets:", len(MAP), "| CSS:", sum(v.startswith("/assets/css") for v in MAP.values()),
      "IMG:", sum(v.startswith("/assets/img") for v in MAP.values()),
      "FONTS:", sum(v.startswith("/assets/fonts") for v in MAP.values()),
      "JS-ref:", sum(v.startswith("/assets/js") for v in MAP.values()))
if FAIL:
    print("FALHAS (%d):" % len(FAIL))
    for e, u in FAIL[:12]: print("  ", e, u[:85])
