#!/usr/bin/env python3
"""
Arte Engine - Render pipeline.

Uso:
  python render.py --preset glass-live --ratio 9x16 --slide 1 --total 5 \
    --data '{"headline":"Campanha completa criada do zero.","subtext":"Tudo num só encontro.","kicker":"AO VIVO","cta":"100% GRATUITO"}' \
    --output ./out/glass-live-9x16-s01.png

Ratios:
  1x1  -> 1080x1080 (2160x2160 com scale=2)
  4x5  -> 1080x1350 (2160x2700 com scale=2)
  9x16 -> 1080x1920 (2160x3840 com scale=2)

Variáveis substituídas no template:
  {{HEADLINE}}, {{SUBTEXT}}, {{BG_IMAGE_URL}}, {{BG_IMAGE_DESC}},
  {{ACCENT}}, {{SLIDE_N}}, {{SLIDE_TOTAL}}, {{RATIO}},
  {{FOOTER_L}}, {{FOOTER_R}}, {{KICKER}}, {{CTA}}, {{BADGE}},
  {{SIDE_TEXT}}, {{SUBHEAD}}, {{META_TOP_L}}, {{META_TOP_R}}

Blocos condicionais:
  {{#IF_X}}...{{/IF_X}} — mantém o bloco se data["x"] for truthy

Dependencies:
  pip install playwright --break-system-packages
  playwright install chromium
"""
import argparse
import json
import re
import sys
from datetime import datetime
from pathlib import Path

try:
    from playwright.sync_api import sync_playwright
except ImportError:
    print("Erro: playwright nao instalado. Rode: pip install playwright --break-system-packages && playwright install chromium")
    sys.exit(1)

SCRIPT_DIR = Path(__file__).parent
SKILL_DIR = SCRIPT_DIR.parent
TEMPLATES_DIR = SKILL_DIR / "templates"
OUT_DIR = SKILL_DIR / "out"

RATIO_MAP = {
    "1x1": (1080, 1080),
    "4x5": (1080, 1350),
    "9x16": (1080, 1920),
}


def render_template(preset: str, ratio: str, slide_n: int, slide_total: int, data: dict) -> str:
    """Load HTML template for preset and substitute all variables."""
    tpl_path = TEMPLATES_DIR / f"{preset}.html"
    if not tpl_path.exists():
        print(f"Erro: template nao encontrado: {tpl_path}", file=sys.stderr)
        print(f"  Crie o arquivo HTML em: {tpl_path}", file=sys.stderr)
        sys.exit(1)

    html = tpl_path.read_text(encoding="utf-8")

    # Build substitution map
    substitutions = {
        "{{HEADLINE}}":      data.get("headline", ""),
        "{{SUBTEXT}}":       data.get("subtext", ""),
        "{{SUBHEAD}}":       data.get("subhead", data.get("subtext", "")),
        "{{BG_IMAGE_URL}}":  data.get("bg_image_url", ""),
        "{{BG_IMAGE_DESC}}": data.get("bg_image_desc", ""),
        "{{ACCENT}}":        data.get("accent", ""),
        "{{SLIDE_N}}":       str(slide_n),
        "{{SLIDE_TOTAL}}":   str(slide_total),
        "{{RATIO}}":         ratio,
        "{{FOOTER_L}}":      data.get("footer_l", ""),
        "{{FOOTER_R}}":      data.get("footer_r", ""),
        "{{KICKER}}":        data.get("kicker", ""),
        "{{CTA}}":           data.get("cta", ""),
        "{{BADGE}}":         data.get("badge", ""),
        "{{SIDE_TEXT}}":     data.get("side_text", ""),
        "{{META_TOP_L}}":    data.get("meta_top_l", data.get("meta_l", "")),
        "{{META_TOP_R}}":    data.get("meta_top_r", data.get("meta_r", "")),
        "{{HEADLINE_CLASS}}": data.get("headline_class", ""),
    }

    for key, val in substitutions.items():
        html = html.replace(key, str(val))

    # Conditional blocks: {{#IF_X}}...{{/IF_X}} — remove block if data key is falsy
    def resolve_conditional(match):
        flag = match.group(1)                     # e.g. "IF_IMAGE"
        body = match.group(2)
        # derive data key: IF_IMAGE -> image, IF_BG_IMAGE_URL -> bg_image_url
        key = flag[3:].lower()                    # strip "IF_", lowercase
        val = data.get(key) or data.get(flag.lower()) or ""
        return body if val else ""

    html = re.sub(
        r"\{\{#(IF_[A-Z_]+)\}\}(.*?)\{\{/\1\}\}",
        resolve_conditional,
        html,
        flags=re.DOTALL,
    )

    # Clean up any leftover unresolved conditionals (guard)
    html = re.sub(r"\{\{#IF_[A-Z_]+\}\}.*?\{\{/IF_[A-Z_]+\}\}", "", html, flags=re.DOTALL)

    return html


def render_to_png(html: str, ratio: str, output: Path, scale: int = 2) -> None:
    """Render HTML string to PNG using Playwright Chromium."""
    width, height = RATIO_MAP[ratio]
    output.parent.mkdir(parents=True, exist_ok=True)

    # Write debug HTML alongside the PNG
    debug_html = output.with_suffix(".html")
    debug_html.write_text(html, encoding="utf-8")

    with sync_playwright() as p:
        browser = p.chromium.launch()
        context = browser.new_context(
            viewport={"width": width, "height": height},
            device_scale_factor=scale,
        )
        page = context.new_page()
        page.set_content(html, wait_until="networkidle")
        page.wait_for_timeout(500)  # allow fonts and animations to settle
        canvas = page.locator("#canvas")
        canvas.screenshot(path=str(output), omit_background=False)
        browser.close()

    print(str(output))


def build_default_output(preset: str, ratio: str) -> Path:
    """Return default output path when --output is not given."""
    ts = datetime.now().strftime("%Y%m%d-%H%M%S")
    OUT_DIR.mkdir(parents=True, exist_ok=True)
    return OUT_DIR / f"{preset}-{ratio}-{ts}.png"


def main():
    parser = argparse.ArgumentParser(
        description="Arte Engine — render preset HTML template to PNG",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Exemplos:
  # Preset glass-live, story 9x16, slide 1 de 5
  python render.py --preset glass-live --ratio 9x16 --slide 1 --total 5 \\
    --data '{"headline":"Campanha completa.","kicker":"AO VIVO","cta":"100% GRATUITO"}' \\
    --output ./out/glass-live-s01.png

  # Preset squad-editorial, feed 4x5, único slide
  python render.py --preset squad-editorial --ratio 4x5 \\
    --data '{"headline":"NÃO FAZEMOS VÍDEOS. CONTAMOS HISTÓRIAS.","subhead":"CINEMA DE PERFORMANCE."}'
        """,
    )
    parser.add_argument(
        "--preset", required=True,
        help="Código do preset (ex: glass-live, squad-editorial, editorial-carousel-ai, ...)",
    )
    parser.add_argument(
        "--ratio", required=True, choices=list(RATIO_MAP.keys()),
        help="1x1 | 4x5 | 9x16",
    )
    parser.add_argument(
        "--slide", type=int, default=1,
        help="Número do slide atual (padrão: 1)",
    )
    parser.add_argument(
        "--total", type=int, default=1,
        help="Total de slides do carrossel (padrão: 1)",
    )
    parser.add_argument(
        "--data", default="{}",
        help='JSON inline com variáveis (ex: \'{"headline":"Texto aqui"}\')',
    )
    parser.add_argument(
        "--output", default=None,
        help="Caminho do PNG de saída (opcional — usa out/<preset>-<ratio>-<ts>.png se omitido)",
    )
    parser.add_argument(
        "--scale", type=int, default=2,
        help="device_scale_factor do Playwright (padrão: 2 → PNG 2x)",
    )
    args = parser.parse_args()

    # Validate ratio
    if args.ratio not in RATIO_MAP:
        print(f"Erro: ratio invalido '{args.ratio}'. Use: {list(RATIO_MAP.keys())}", file=sys.stderr)
        sys.exit(1)

    # Parse data JSON
    try:
        data = json.loads(args.data)
    except json.JSONDecodeError as exc:
        print(f"Erro: JSON invalido em --data: {exc}", file=sys.stderr)
        sys.exit(1)

    # Resolve output path
    if args.output:
        output = Path(args.output)
    else:
        output = build_default_output(args.preset, args.ratio)

    # Render
    html = render_template(args.preset, args.ratio, args.slide, args.total, data)
    render_to_png(html, args.ratio, output, scale=args.scale)


if __name__ == "__main__":
    main()
