"""Mide una página en desktop, tablet y mobile con Playwright.

Por viewport: imágenes (formato servido, tamaño natural vs. pintado, si cae
arriba del fold, loading), peso total de imágenes, LCP, CLS y una captura de
pantalla completa. Se usa igual para la línea base y para la medición
posterior, así los números de resultado.md son comparables.

Uso (Python con Playwright; sirve el venv de claude-seo):
  .claude/skills/seo/.venv/bin/python -I seo-optimizations/_scripts/medir-pagina.py \
      https://www.patiospoolsanddriveways.com/ seo-optimizations/home/mediciones/20261030
"""

import json
import sys
from pathlib import Path

from playwright.sync_api import sync_playwright

VIEWPORTS = {
    "desktop": {"width": 1440, "height": 900, "dpr": 1},
    "tablet": {"width": 768, "height": 1024, "dpr": 2},
    "mobile": {"width": 390, "height": 844, "dpr": 2},
}

OBSERVERS = """
window.__lcp = null; window.__cls = 0;
new PerformanceObserver(l => { const e = l.getEntries().at(-1);
  window.__lcp = { ms: Math.round(e.startTime), file: (e.url || '').split('/').pop() }; })
  .observe({ type: 'largest-contentful-paint', buffered: true });
new PerformanceObserver(l => { for (const e of l.getEntries()) if (!e.hadRecentInput) window.__cls += e.value; })
  .observe({ type: 'layout-shift', buffered: true });
"""

IMAGES = """(vh) => [...document.images].map(i => {
  const r = i.getBoundingClientRect();
  const file = (i.currentSrc || i.src).split('/').pop();
  return {
    file,
    format: (file.match(/\\.(avif|webp|jpe?g|png|svg)/i) || [, '?'])[1].toLowerCase(),
    natural: [i.naturalWidth, i.naturalHeight],
    rendered: [Math.round(r.width), Math.round(r.height)],
    above_fold: r.top + scrollY < vh,
    loading: i.loading,
  };
})"""

IMAGE_BYTES = """() => performance.getEntriesByType('resource')
  .filter(r => r.initiatorType === 'img' || /\\.(avif|webp|jpe?g|png|svg)(\\?|$)/.test(r.name))
  .reduce((a, r) => a + (r.encodedBodySize || 0), 0)"""


def measure(browser, url, vp, shot):
    ctx = browser.new_context(
        viewport={"width": vp["width"], "height": vp["height"]}, device_scale_factor=vp["dpr"]
    )
    page = ctx.new_page()
    page.add_init_script(OBSERVERS)
    page.goto(url, wait_until="networkidle")
    lcp = page.evaluate("window.__lcp")
    # Bajar hasta el final para que carguen las lazy y entren en el peso total.
    page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
    page.wait_for_timeout(2500)
    page.evaluate("window.scrollTo(0, 0)")
    page.wait_for_timeout(500)
    result = {
        "lcp": lcp,
        "cls": round(page.evaluate("window.__cls"), 4),
        "image_kb": round(page.evaluate(IMAGE_BYTES) / 1024),
        "images": page.evaluate(IMAGES, vp["height"]),
    }
    # JPEG y no PNG: las capturas de página completa en PNG pesan ~5 MB cada una.
    page.screenshot(path=shot, full_page=True, type="jpeg", quality=70)
    ctx.close()
    return result


def main():
    url, out = sys.argv[1], Path(sys.argv[2])
    out.mkdir(parents=True, exist_ok=True)
    report = {"url": url}
    with sync_playwright() as p:
        browser = p.chromium.launch()
        for name, vp in VIEWPORTS.items():
            report[name] = measure(browser, url, vp, str(out / f"{name}.jpg"))
        browser.close()
    (out / "medicion.json").write_text(json.dumps(report, indent=1))
    for name in VIEWPORTS:
        r = report[name]
        print(f"{name:8s} imágenes {r['image_kb']:5d} KB  LCP {r['lcp']}  CLS {r['cls']}")


if __name__ == "__main__":
    main()
