#!/usr/bin/env python3
"""POC generator: Brentwood Laravel Homepage JSON -> WordPress Gutenberg post_content.

Maps each Laravel content-element to a block:
  photo-block       -> wp:brentwood/media-grid  (custom block; static HTML fallback renders pre-registration)
  text-block        -> core paragraph/heading blocks
  embed-video       -> media-grid 'hero' variant (poster + overlay text)  [mp4 wiring = TODO]
  publication-list  -> placeholder note (production: WP Query Loop over posts)
  social-media-feed -> placeholder note (production: Instagram embed)

Image URLs are hotlinked from the public live site for the POC.
"""
import json, html, re, sys

IMG_BASE = "https://www.brentwood.ca"   # public, serves /photos/... at 200
SRC = "/srv/apps/brentwood/tmp/poc/home.json"

def esc(s): return html.escape(s or "", quote=True)
def img_url(p):
    u = p.get("large") or p.get("medium") or p.get("small") or ""
    return (IMG_BASE + u) if u.startswith("/") else u

def focal(p):
    # Laravel offsetX/offsetY are percentages -> CSS object-position
    return f'{p.get("offsetX",50)}% {p.get("offsetY",50)}%'

# --- text body HTML -> core blocks (simplified port of the blog importer's converter) ---
def text_to_blocks(body):
    if not body: return ""
    # split into paragraphs on </p> or <br><br>
    body = body.replace("\r", "")
    parts = re.split(r"</p\s*>|<br\s*/?>\s*<br\s*/?>", body, flags=re.I)
    out = []
    for part in parts:
        t = re.sub(r"<p[^>]*>", "", part, flags=re.I).strip()
        if not t: continue
        # keep simple inline tags; strip wrapper class attrs
        t = re.sub(r'\sclass="[^"]*"', "", t)
        out.append(f"<!-- wp:paragraph -->\n<p>{t}</p>\n<!-- /wp:paragraph -->")
    return "\n\n".join(out)

def media_grid(el, variant="grid"):
    c = el["content"]
    # Source renders photos sorted by sort_order, not DB/array order — match it.
    photos = sorted(el["photos"], key=lambda p: (p.get("sort_order") or 0))
    cols = int(c.get("columns") or 1)
    height = int(c.get("height") or 66)
    layout = c.get("layout") or variant
    text_style = c.get("text_style") or ""
    attrs = {
        "columns": cols, "height": height, "layout": layout,
        "header": c.get("header") or "", "body": c.get("body") or "",
        "showText": bool(c.get("show_text")), "textStyle": text_style,
        "textOrder": c.get("text_order"), "textSpan": c.get("text_span") or 1,
        "variant": variant,
        "photos": [{
            "url": img_url(p), "alt": p.get("alt") or "", "span": p.get("span") or 1,
            "vspan": p.get("vertical_span") or 1, "focal": focal(p),
            "title": p.get("title"), "subtitle": p.get("subtitle"), "link": p.get("link"),
        } for p in photos],
    }
    # ---- static fallback HTML (renders even if block type is unregistered) ----
    cls = f"bw-mg bw-mg--{layout} bw-mg--cols{cols}" + (f" bw-mg--{text_style}" if text_style else "")
    cells = []
    # optional text panel placed by text_order
    text_cell = ""
    if attrs["showText"] and (attrs["header"] or attrs["body"]):
        ts = int(attrs["textSpan"] or 1)
        text_cell = (f'<div class="bw-mg__text" style="--span:{ts}">'
                     + (f'<h2>{esc(attrs["header"])}</h2>' if attrs["header"] else "")
                     + (attrs["body"] or "") + '</div>')
    for p in attrs["photos"]:
        pb = max(25, min(200, round(height / max(1, int(p["span"])))))
        inner = (f'<span class="bw-mg__cap"><strong>{esc(p["title"])}</strong>'
                 f'<em>{esc(p["subtitle"])}</em></span>') if (p["title"] or p["subtitle"]) else ""
        media = (f'<div class="bw-mg__cell" style="--span:{p["span"]};--vspan:{p["vspan"]};--pb:{pb}%">'
                 f'<img loading="lazy" src="{esc(p["url"])}" alt="{esc(p["alt"])}" '
                 f'style="object-position:{p["focal"]}">{inner}</div>')
        if p["link"]:
            media = f'<a class="bw-mg__link" href="{IMG_BASE}/{esc(str(p["link"]))}">{media}</a>'
        cells.append(media)
    # insert text panel by order (1-based); default append
    order = attrs.get("textOrder")
    if text_cell:
        if isinstance(order, int) and 1 <= order <= len(cells)+1:
            cells.insert(order-1, text_cell)
        else:
            cells.insert(0, text_cell)
    header_bar = f'<div class="bw-mg__head"><h3>{esc(attrs["header"])}</h3></div>' if (layout=="links" and attrs["header"]) else ""
    static = f'<div class="{cls}">{header_bar}<div class="bw-mg__grid">{"".join(cells)}</div></div>'
    a = json.dumps(attrs, ensure_ascii=False)
    return f"<!-- wp:brentwood/media-grid {a} -->\n{static}\n<!-- /wp:brentwood/media-grid -->"

# POC: self-hosted video URLs keyed by EmbedVideo file_upload_id.
# Production: the extractor must emit EmbedVideo->url / ->url_hls (storage path) per element.
VIDEOS = {
    "11088": IMG_BASE + "/storage/videos/11088/hero-broll-web-banner-2-minutes-(web-1080p-compressed-medium).mp4",
}

def hero(el):
    c = el["content"]; ph = el["photos"][0] if el["photos"] else {}
    poster = img_url(ph)
    video = VIDEOS.get(str(c.get("file_upload_id") or ""))
    attrs = {"poster": poster, "video": video or "", "fileUploadId": c.get("file_upload_id")}
    # Editable card content = real core blocks (InnerBlocks): heading + paragraphs.
    inner = []
    if c.get("header"):
        inner.append('<!-- wp:heading {"level":1} -->\n'
                     f'<h1 class="wp-block-heading">{esc(c["header"])}</h1>\n<!-- /wp:heading -->')
    inner.append(text_to_blocks(c.get("body")))
    inner_html = "\n\n".join(b for b in inner if b)
    return (f"<!-- wp:brentwood/video-hero {json.dumps(attrs, ensure_ascii=False)} -->\n"
            f"{inner_html}\n<!-- /wp:brentwood/video-hero -->")

def placeholder(label, detail):
    return ('<!-- wp:html -->\n'
            f'<div class="bw-dyn"><strong>{esc(label)}</strong><span>{esc(detail)}</span></div>\n'
            '<!-- /wp:html -->')

CSS = """
<!-- wp:html -->
<style>
.bw-mg{margin:0 auto 2rem;max-width:72rem}
.bw-mg--full{max-width:100%}
.bw-mg__grid{display:grid;gap:.5rem;grid-template-columns:repeat(var(--cols,3),1fr)}
.bw-mg--cols2 .bw-mg__grid{--cols:2}.bw-mg--cols3 .bw-mg__grid{--cols:3}
.bw-mg--cols4 .bw-mg__grid{--cols:4}.bw-mg--cols5 .bw-mg__grid{--cols:5}.bw-mg--cols6 .bw-mg__grid{--cols:6}
.bw-mg__cell{position:relative;grid-column:span var(--span,1);grid-row:span var(--vspan,1);overflow:hidden;border-radius:.5rem}
.bw-mg__cell>img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.bw-mg__cell::before{content:"";display:block;padding-bottom:var(--pb,66%)}
.bw-mg__link{grid-column:span var(--span,1);display:block;transition:transform .3s}
.bw-mg__link:hover{transform:scale(1.02)}
.bw-mg__link .bw-mg__cell{grid-column:span 1}
.bw-mg__text{grid-column:span var(--span,1);display:flex;flex-direction:column;justify-content:center;padding:1.5rem}
.bw-mg__text h2{margin:0 0 .5rem}
.bw-mg--white .bw-mg__text{color:#fff;background:#0a3d2e}
.bw-mg__cap{position:absolute;left:0;right:0;bottom:0;padding:.75rem;background:linear-gradient(transparent,rgba(0,0,0,.7));color:#fff;display:flex;flex-direction:column}
.bw-mg__head h3{margin:.5rem 0;text-align:center}
.bw-hero{position:relative;min-height:70vh;background-size:cover;background-position:center;display:flex;align-items:center;justify-content:center;border-radius:.5rem;overflow:hidden;margin-bottom:2rem}
.bw-hero__overlay{background:rgba(7,40,30,.55);color:#fff;padding:3rem;text-align:center;max-width:48rem}
.bw-hero__overlay h1{font-size:2.5rem;margin:0 0 1rem}
.bw-hero__note{opacity:.6;font-size:.8rem;margin-top:1rem}
.bw-dyn{display:flex;flex-direction:column;gap:.25rem;border:2px dashed #1e7a5a;border-radius:.5rem;padding:1.25rem;margin:0 auto 2rem;max-width:72rem;color:#1e7a5a}
@media(max-width:768px){.bw-mg__grid{grid-template-columns:repeat(2,1fr)!important}.bw-mg--cols2 .bw-mg__grid,.bw-mg--cols3 .bw-mg__grid{grid-template-columns:1fr!important}}
</style>
<!-- /wp:html -->
"""

def main():
    d = json.load(open(SRC))
    # CSS is now enqueued by the child-theme block; only inline it if explicitly asked.
    blocks = [CSS.strip()] if "--inline-css" in sys.argv else []
    for el in d["elements"]:
        b = el["base"]
        if b == "EmbedVideo":      blocks.append(hero(el))
        elif b == "PhotoBlock":    blocks.append(media_grid(el, el["content"].get("layout") or "grid"))
        elif b == "TextBlock":     blocks.append(text_to_blocks(el["content"].get("body")))
        elif b == "PublicationList": blocks.append(placeholder("Latest News / Publications", "production: WP Query Loop over posts (newest N)"))
        elif b == "SocialMediaFeed": blocks.append(placeholder("Social Media Feed", "production: Instagram/embed block"))
        else: blocks.append(placeholder(b, "not yet mapped"))
    out = "\n\n".join(blocks)
    open("/srv/apps/brentwood/tmp/poc/home-content.html", "w").write(out)
    print(f"wrote home-content.html ({len(out)} bytes), {len(d['elements'])} elements -> {len(blocks)} block groups")

main()
