#!/usr/bin/env python3
"""Erzeugt aus liste.xlsx pro Gäste-Zeile eine persönliche Karte in dist/<name>-<token>/ und links.csv.

Aufruf:  python3 build.py
Einstellungen (RSVP-Kontakt, Unterkünfte, Domain): config.json
"""
import csv, hashlib, hmac, json, re, secrets, shutil, unicodedata, zipfile
from pathlib import Path

ROOT = Path(__file__).resolve().parent
XLSX = ROOT.parent / "liste.xlsx"
DIST = ROOT / "dist"
SECRET_FILE = ROOT / ".secret"          # NICHT veröffentlichen – sorgt für stabile, nicht erratbare Links


def read_rows(path):
    z = zipfile.ZipFile(path)
    sst = []
    if "xl/sharedStrings.xml" in z.namelist():
        for si in re.findall(r"<si>(.*?)</si>", z.read("xl/sharedStrings.xml").decode("utf8"), re.S):
            sst.append(re.sub(r"<[^>]+>", "", si).replace("&amp;", "&").replace("&apos;", "'"))
    xml = z.read("xl/worksheets/sheet1.xml").decode("utf8")
    rows = []
    for r in re.findall(r"<row[^>]*>(.*?)</row>", xml, re.S):
        cells = {}
        for m in re.finditer(r'<c r="([A-Z]+)\d+"([^>]*?)(?:/>|>(.*?)</c>)', r, re.S):
            body = m.group(3) or ""
            v = re.search(r"<v>(.*?)</v>", body)
            if not v:
                continue
            cells[m.group(1)] = sst[int(v.group(1))] if 't="s"' in m.group(2) else v.group(1)
        if cells:
            rows.append(cells)
    return rows


def slugify(s):
    s = unicodedata.normalize("NFKD", s).encode("ascii", "ignore").decode()
    return re.sub(r"[^a-z0-9]+", "-", s.lower()).strip("-")


def secret():
    if not SECRET_FILE.exists():
        SECRET_FILE.write_text(secrets.token_hex(16))
    return SECRET_FILE.read_text().strip().encode()


def write_page(out, template, names, cfg, variant, preview=False, intro=""):
    data = {"guest": {"names": names}, "variant": variant, "preview": preview, "intro": intro,
            "config": {"rsvp": cfg.get("rsvp", {}), "accommodation": cfg.get("accommodation", []), "gift": cfg.get("gift", {})}}
    blob = "window.CARD=" + json.dumps(data, ensure_ascii=False).replace("</", "<\\/") + ";"
    out.mkdir()
    (out / "index.html").write_text(template.replace("/*__DATA__*/", blob), encoding="utf8")


COMPARE = """<!doctype html><html lang="de"><head><meta charset="utf-8"><meta name="robots" content="noindex">
<meta name="viewport" content="width=device-width,initial-scale=1"><title>Vergleich D / E</title>
<style>
body{margin:0;font-family:Georgia,serif;background:#e9e3d6;color:#3f3b36}
h1{font-weight:400;text-align:center;margin:22px 0 4px;font-size:24px}
p.sub{text-align:center;margin:0 0 18px;color:#6a6358;font-size:15px}
.row{display:flex;gap:36px;justify-content:center;flex-wrap:wrap;padding:0 16px 30px}
figure{margin:0;text-align:center}
figcaption{margin-bottom:10px;font-size:17px}
figcaption small{display:block;color:#6a6358;font-size:13px}
iframe{width:390px;height:780px;max-width:calc(100vw - 32px);border:0;border-radius:28px;background:#fff;
  box-shadow:0 0 0 10px #2b2a28,0 18px 40px rgba(0,0,0,.25)}
.btns{margin-top:18px;display:flex;gap:10px;justify-content:center}
a,button{font:inherit;font-size:14px;color:#566a4a;background:#fff;border:1px solid #cfc8b8;border-radius:999px;padding:6px 14px;cursor:pointer;text-decoration:none}
</style></head><body>
<h1>Mi &amp; Luke · Umschlag im Vergleich</h1>
<p class="sub">Links Entwurf 1 (gezeichneter Umschlag), rechts Entwurf 2 (Umschlag-Video). In jeden Umschlag tippen zum Öffnen.</p>
<div class="row">
<figure><figcaption>Entwurf 1 · D<small>gezeichneter Umschlag</small></figcaption>
<iframe id="a" src="../entwurf-d/" allow="autoplay"></iframe>
<div class="btns"><button onclick="a.src=a.src">Neu starten</button><a href="../entwurf-d/" target="_blank">Vollbild</a></div></figure>
<figure><figcaption>Entwurf 2 · E<small>Umschlag-Video</small></figcaption>
<iframe id="b" src="../entwurf-e/" allow="autoplay"></iframe>
<div class="btns"><button onclick="b.src=b.src">Neu starten</button><a href="../entwurf-e/" target="_blank">Vollbild</a></div></figure>
</div></body></html>"""


def main():
    cfg = json.loads((ROOT / "config.json").read_text(encoding="utf8"))
    template = (ROOT / "src" / "template.html").read_text(encoding="utf8")
    base = cfg.get("base_url", "").rstrip("/")
    key = secret()

    if DIST.exists():
        shutil.rmtree(DIST)
    DIST.mkdir()
    shutil.copytree(ROOT / "assets", DIST / "assets")
    (DIST / "robots.txt").write_text("User-agent: *\nDisallow: /\n")
    (DIST / "index.html").write_text(
        '<!doctype html><meta charset="utf-8"><meta name="robots" content="noindex">'
        '<meta name="viewport" content="width=device-width,initial-scale=1"><title>Mi &amp; Luke</title>'
        '<body style="font-family:Georgia,serif;text-align:center;padding:30vh 24px;background:#f6efe2;color:#3f3b36">'
        "<h1 style='font-weight:400'>Mi &amp; Luke</h1><p>15 · 05 · 2027</p>"
    )

    links, warnings, used = [], [], set()
    for row in read_rows(XLSX):
        label = row.get("A", "")
        if not label.lower().startswith("couple"):
            continue
        raw = [row[c].strip() for c in "BCDE" if row.get(c, "").strip()]
        names = []
        for n in raw:
            if n in names:
                warnings.append(f"{label}: '{n}' kommt doppelt vor – nur einmal verwendet")
                continue
            names.append(n)
        if not names:
            continue
        if any("tbd" in n.lower() for n in names):
            warnings.append(f"{label}: '{' / '.join(names)}' ist noch ein Platzhalter (TBD) – übersprungen")
            continue

        token = hmac.new(key, (label + "|" + "|".join(names)).encode(), hashlib.sha256).hexdigest()[:6]
        slug = "-".join(slugify(n) for n in names)[:36].strip("-") + "-" + token
        assert slug not in used
        used.add(slug)

        write_page(DIST / slug, template, names, cfg, cfg.get("variant", "d"), intro=cfg.get("intro", ""))
        if not links:   # Vorschau-Seiten mit dem ersten Gast: D (gezeichneter Umschlag) und E (Umschlag-Video)
            write_page(DIST / "entwurf-d", template, names, cfg, "d", preview=True)
            write_page(DIST / "entwurf-e", template, names, cfg, "d", preview=True, intro="video")
            (DIST / "vergleich").mkdir()
            (DIST / "vergleich" / "index.html").write_text(COMPARE, encoding="utf8")
        url = f"{base}/{slug}/"
        links.append([label, " & ".join(names), url, url + "?lang=en"])

    with open(ROOT / "links.csv", "w", newline="", encoding="utf-8-sig") as f:
        w = csv.writer(f, delimiter=";")
        w.writerow(["Zeile", "Gäste", "Link (DE/EN automatisch)", "Link (Englisch)"])
        w.writerows(links)

    print(f"{len(links)} Karten erzeugt in {DIST}")
    print(f"Linkliste: {ROOT / 'links.csv'}")
    r = cfg.get("rsvp", {})
    if not (r.get("endpoint") or r.get("whatsapp") or r.get("email")):
        warnings.append("config.json: kein Ziel für die Rückmeldung (endpoint, whatsapp oder email) eingetragen – siehe rsvp-google-sheet.md")
    if any("LINK-FUER" in v for v in cfg.get("gift", {}).values()):
        warnings.append("config.json: PayPal-Links unter \"gift\" sind noch Platzhalter")
    if "DEINE-DOMAIN" in base:
        warnings.append("config.json: base_url ist noch ein Platzhalter")
    for w_ in warnings:
        print("⚠️ ", w_)


if __name__ == "__main__":
    # Die Siegel-Sperre (Verschlüsselung der Karten) gibt es nur in build.js.
    # Damit nie versehentlich ungeschützte Karten entstehen, wird hier immer build.js ausgeführt.
    import subprocess, sys
    print("Hinweis: build.py leitet an build.js weiter (Siegel-Sperre).")
    sys.exit(subprocess.call(["node", str(ROOT / "build.js")]))
