#!/usr/bin/env python3
"""Writes the web site's pages from i18n.py: site/index.html (English), site/<code>/index.html, sitemap.xml, robots.txt.

    python3 pages.py <site dir>

Absolute URLs are https://{{DOMAIN}}/... (bin/jumphp deploy / the playground replace {{DOMAIN}}).
"""
import html
import json
import os
import sys

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from i18n import T, ORDER  # noqa: E402
from i18n_more import M  # noqa: E402

for _code in ORDER:
    T[_code].update(M[_code])
    T[_code]['caps'] = {**T[_code]['caps'], **T[_code].pop('caps2')}
    T[_code]['faq'] = T[_code]['faq'] + T[_code].pop('faq2')

site = sys.argv[1]


def french_spaces(value):
    """French typography: a no-break space before : ; ? ! and inside « »."""
    if isinstance(value, str):
        for mark in (' :', ' ;', ' ?', ' !'):
            value = value.replace(mark, '\u202f' + mark[1] if mark != ' :' else '\u00a0:')
        return value.replace('« ', '«\u00a0').replace(' »', '\u00a0»')
    if isinstance(value, list):
        return [french_spaces(v) for v in value]
    if isinstance(value, tuple):
        return tuple(french_spaces(v) for v in value)
    if isinstance(value, dict):
        return {k: french_spaces(v) for k, v in value.items()}
    return value


T['fr'] = french_spaces(T['fr'])
MAIN = ORDER[0]
BASE = 'https://{{DOMAIN}}/'
SHOTS = ['general', 'console', 'performance', 'search', 'logs', 'snapshots', 'new-vm', 'menu']
MORE_SHOTS = ['dashboard', 'updates', 'new-pool', 'cross-pool', 'new-sr-nfs', 'new-network', 'folders', 'alerts-dialog']
GROUP_ICONS = ['&#9638;', '&#9707;', '&#8635;', '&#9873;']
ICONS = ['&#9776;', '&#9638;', '&#9000;', '&#8776;', '&#9654;', '&#9719;', '&#9881;', '&#8635;']
CONNECT_ICONS = ['&#8644;', '&gt;_', '&#9673;', '&#8962;']


def e(text):
    return html.escape(text, quote=True)


def hreflang(code):
    return 'zh-Hans' if code == 'zh' else code


def url(code):
    return BASE if code == MAIN else f'{BASE}{code}/'


CSS = """
:root{--dark:#1b335a;--light:#2f5d94;--accent:#2f6fd6;--grey:#eef1f5;--line:#dbe1ea;--text:#1d2633;--muted:#5b6778}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;font:16px/1.6 system-ui,-apple-system,"Segoe UI",Roboto,"Noto Sans",Ubuntu,sans-serif;color:var(--text);background:var(--grey)}
img{max-width:100%;height:auto;display:block}
a{color:var(--accent)}
.wrap{max-width:1120px;margin:0 auto;padding:0 16px}
header.top{background:var(--dark);color:#fff;position:relative;z-index:5;box-shadow:0 1px 0 rgba(255,255,255,.08)}
header.top .wrap{display:flex;align-items:center;gap:16px;height:56px}
.brand{display:flex;align-items:center;gap:10px;color:#fff;text-decoration:none;font-weight:700;white-space:nowrap}
.brand img{width:28px;height:28px;border-radius:6px}
nav.links{display:flex;gap:18px;margin-left:auto}
nav.links a{color:#d6e2f3;text-decoration:none;font-size:15px}
nav.links a:hover{color:#fff}
details.langs{position:relative;margin-left:8px}
details.langs summary{list-style:none;cursor:pointer;color:#fff;border:1px solid rgba(255,255,255,.35);border-radius:8px;padding:4px 10px;font-size:14px;white-space:nowrap}
details.langs summary::-webkit-details-marker{display:none}
details.langs ul{position:absolute;right:0;top:36px;margin:0;padding:6px;list-style:none;background:#fff;border:1px solid var(--line);border-radius:10px;box-shadow:0 10px 30px rgba(20,40,80,.2);min-width:170px;columns:2;column-gap:4px}
details.langs li a{display:block;padding:5px 10px;border-radius:6px;color:var(--text);text-decoration:none;font-size:14px}
details.langs li a:hover,details.langs li a[aria-current]{background:var(--grey);color:var(--accent)}
.hero{background:linear-gradient(120deg,var(--dark),var(--light));color:#fff;padding:56px 0 0;overflow:hidden}
.tag{display:inline-block;background:rgba(255,255,255,.14);border:1px solid rgba(255,255,255,.25);border-radius:999px;padding:3px 12px;font-size:14px;color:#e3ecf8}
.hero h1{font-size:44px;line-height:1.15;margin:16px 0 14px;max-width:820px;overflow-wrap:break-word}
.hero p.lead{font-size:19px;color:#d9e4f3;max-width:760px;margin:0 0 26px}
.cta{display:inline-block;background:var(--accent);color:#fff;text-decoration:none;font-weight:700;font-size:19px;padding:14px 30px;border-radius:10px;box-shadow:0 6px 18px rgba(10,30,70,.35)}
.cta:hover{background:#255fbe}
.hero .cta{background:#fff;color:var(--dark)}
.hero .cta:hover{background:#e8effa}
.hint{font-size:15px;color:#c6d5ea;margin:12px 0 0}
.shot-frame{margin:40px auto 0;max-width:1000px;background:#fff;border-radius:12px 12px 0 0;padding:8px 8px 0;box-shadow:0 -4px 40px rgba(0,0,0,.25)}
.shot-frame img{border-radius:6px 6px 0 0}
section{padding:64px 0}
section.white{background:#fff}
h2{font-size:30px;line-height:1.25;margin:0 0 8px;color:var(--dark)}
.sub{color:var(--muted);margin:0 0 28px}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:16px}
.card{background:#fff;border:1px solid var(--line);border-radius:12px;padding:20px}
section.white .card{background:var(--grey)}
.card h3{margin:0 0 6px;font-size:18px;color:var(--dark);display:flex;align-items:center;gap:10px}
.card h3 span{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:8px;background:var(--accent);color:#fff;font-size:16px;flex:none}
.card p{margin:0;color:var(--muted);font-size:15px}
.gallery{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}
figure{margin:0;background:#fff;border:1px solid var(--line);border-radius:12px;overflow:hidden}
figcaption{padding:12px 16px;font-size:15px;color:var(--text)}
.demo{background:linear-gradient(120deg,var(--dark),var(--light));color:#fff;text-align:center}
.demo h2{color:#fff}
.demo p{max-width:760px;margin:0 auto 26px;color:#d9e4f3}
.demo .cta{background:#fff;color:var(--dark)}
.group{margin:0 0 28px}.group .grid{grid-template-columns:repeat(auto-fill,minmax(300px,1fr))}.gh{font-size:21px;color:var(--dark);margin:0 0 12px;display:flex;align-items:center;gap:10px}.gh span{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:9px;background:var(--dark);color:#fff;font-size:17px}.card h4{margin:0 0 6px;font-size:17px;color:var(--dark)}
.faq details{background:#fff;border:1px solid var(--line);border-radius:10px;margin:0 0 10px;padding:0 18px}
.faq summary{cursor:pointer;font-weight:600;padding:14px 0;color:var(--dark)}
.faq details p{margin:0 0 16px;color:var(--muted)}
.again{text-align:center;margin-top:32px}
footer{background:var(--dark);color:#c6d5ea;padding:32px 0;font-size:14px}
footer p{margin:0 0 10px}
footer .langlist{display:flex;flex-wrap:wrap;gap:6px 14px}
footer .langlist a{color:#e3ecf8;text-decoration:none}
@media (max-width:820px){nav.links{display:none}details.langs{margin-left:auto}.gallery{grid-template-columns:1fr}.hero h1{font-size:32px}.hero p.lead{font-size:17px}h2{font-size:25px}section{padding:44px 0}.hero{padding-top:36px}}
@media (max-width:420px){.brand span{font-size:15px}.cta{font-size:17px;padding:13px 22px;display:block;text-align:center}.hero h1{font-size:28px}}
"""


def page(code):
    t = T[code]
    up = '' if code == MAIN else '../'
    play = f'{up}play/'
    img = f'{up}images/'
    alternates = '\n'.join(f'<link rel="alternate" hreflang="{hreflang(c)}" href="{url(c)}">' for c in ORDER)
    alternates += f'\n<link rel="alternate" hreflang="x-default" href="{BASE}">'
    lang_items = '\n'.join(
        f'<li><a href="{up}{"" if c == MAIN else c + "/"}" hreflang="{hreflang(c)}" lang="{hreflang(c)}"'
        + (' aria-current="page"' if c == code else '') + f'>{e(T[c]["name"])}</a></li>'
        for c in ORDER)
    foot_langs = ' '.join(
        f'<a href="{up}{"" if c == MAIN else c + "/"}" hreflang="{hreflang(c)}" lang="{hreflang(c)}">{e(T[c]["name"])}</a>' for c in ORDER)
    app_ld = {
        '@context': 'https://schema.org', '@type': 'SoftwareApplication',
        'name': 'XenCenter (xenadmin) XCP - Jumphp', 'alternateName': 'XenCenter for JumPHP',
        'description': t['desc'], 'url': url(code), 'inLanguage': hreflang(code),
        'applicationCategory': 'UtilitiesApplication', 'applicationSubCategory': 'Virtualization management',
        'operatingSystem': 'Linux, Windows, macOS, Web', 'isAccessibleForFree': True,
        'offers': {'@type': 'Offer', 'price': '0', 'priceCurrency': 'USD'},
        'image': f'{BASE}images/og.png',
        'screenshot': [f'{BASE}images/{s}.jpg' for s in SHOTS + MORE_SHOTS],
        'featureList': [f[0] for f in t['features']] + [i[0] for _, items in t['groups'] for i in items],
        'potentialAction': {'@type': 'UseAction', 'target': f'{BASE}play/'},
    }
    faq_ld = {
        '@context': 'https://schema.org', '@type': 'FAQPage', 'inLanguage': hreflang(code),
        'mainEntity': [{'@type': 'Question', 'name': q, 'acceptedAnswer': {'@type': 'Answer', 'text': a}} for q, a in t['faq']],
    }
    ld = lambda d: json.dumps(d, ensure_ascii=False, indent=1).replace('</', '<\\/')
    features = '\n'.join(
        f'<div class="card"><h3><span aria-hidden="true">{ICONS[i]}</span>{e(ti)}</h3><p>{e(d)}</p></div>'
        for i, (ti, d) in enumerate(t['features']))
    connect = '\n'.join(
        f'<div class="card"><h3><span aria-hidden="true">{CONNECT_ICONS[i]}</span>{e(ti)}</h3><p>{e(d)}</p></div>'
        for i, (ti, d) in enumerate(t['connect']))
    gallery = '\n'.join(
        f'<figure><img src="{img}{s}.jpg" width="1280" height="800" decoding="async" alt="{e(t["caps"][s])}">'
        f'<figcaption>{e(t["caps"][s])}</figcaption></figure>' for s in SHOTS)
    groups = '\n'.join(
        f'<div class="group"><h3 class="gh"><span aria-hidden="true">{GROUP_ICONS[g]}</span>{e(title)}</h3><div class="grid">'
        + ''.join(f'<div class="card"><h4>{e(ti)}</h4><p>{e(d)}</p></div>' for ti, d in items) + '</div></div>'
        for g, (title, items) in enumerate(t['groups']))
    gallery2 = '\n'.join(
        f'<figure><img src="{img}{s}.jpg" width="1280" height="800" decoding="async" alt="{e(t["caps"][s])}">'
        f'<figcaption>{e(t["caps"][s])}</figcaption></figure>' for s in MORE_SHOTS)
    faq = '\n'.join(f'<details><summary>{e(q)}</summary><p>{e(a)}</p></details>' for q, a in t['faq'])
    og_alt = '\n'.join(f'<meta property="og:locale:alternate" content="{T[c]["locale"]}">' for c in ORDER if c != code)
    return f"""<!doctype html>
<html lang="{hreflang(code)}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{e(t['title'])}</title>
<meta name="description" content="{e(t['desc'])}">
<link rel="canonical" href="{url(code)}">
{alternates}
<link rel="icon" type="image/png" href="{img}favicon.png">
<link rel="apple-touch-icon" href="{img}icon-192.png">
<meta name="theme-color" content="#1b335a">
<meta property="og:type" content="website">
<meta property="og:site_name" content="XenCenter for JumPHP">
<meta property="og:title" content="{e(t['h1'])}">
<meta property="og:description" content="{e(t['desc'])}">
<meta property="og:url" content="{url(code)}">
<meta property="og:image" content="{BASE}images/og.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:locale" content="{t['locale']}">
{og_alt}
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{e(t['h1'])}">
<meta name="twitter:description" content="{e(t['desc'])}">
<meta name="twitter:image" content="{BASE}images/og.png">
<script type="application/ld+json">
{ld(app_ld)}
</script>
<script type="application/ld+json">
{ld(faq_ld)}
</script>
<style>{CSS}</style>
</head>
<body>
<header class="top"><div class="wrap">
<a class="brand" href="{up or './'}"><img src="{img}favicon.png" width="28" height="28" alt=""><span>XenCenter · JumPHP</span></a>
<nav class="links" aria-label="Sections"><a href="#features">{e(t['nav_features'])}</a><a href="#screenshots">{e(t['nav_shots'])}</a><a href="#connect">{e(t['nav_connect'])}</a><a href="#faq">{e(t['nav_faq'])}</a></nav>
<details class="langs"><summary aria-label="{e(t['lang_label'])}">{e(t['name'])} &#9662;</summary><ul>
{lang_items}
</ul></details>
</div></header>
<main>
<div class="hero"><div class="wrap">
<span class="tag">{e(t['tag'])}</span>
<h1>{e(t['h1'])}</h1>
<p class="lead">{e(t['lead'])}</p>
<a class="cta" href="{play}">{e(t['cta'])} &#8594;</a>
<p class="hint">{e(t['hint'])}</p>
<div class="shot-frame"><img src="{img}general.jpg" width="1280" height="800" alt="{e(t['alt_hero'])}"></div>
</div></div>

<section id="features"><div class="wrap">
<h2>{e(t['features_h'])}</h2>
<p class="sub">{e(t['lead'])}</p>
<div class="grid">
{features}
</div>
</div></section>

<section id="more" class="white"><div class="wrap">
<h2>{e(t['more_h'])}</h2>
<p class="sub">{e(t['more_lead'])}</p>
{groups}
</div></section>

<section id="screenshots"><div class="wrap">
<h2>{e(t['shots_h'])}</h2>
<p class="sub">{e(t['shots_lead'])}</p>
<div class="gallery">
{gallery}
{gallery2}
</div>
</div></section>

<section id="connect" class="white"><div class="wrap">
<h2>{e(t['connect_h'])}</h2>
<p class="sub">{e(t['connect_lead'])}</p>
<div class="grid">
{connect}
</div>
</div></section>

<section class="demo"><div class="wrap">
<h2>{e(t['demo_h'])}</h2>
<p>{e(t['demo_p'])}</p>
<a class="cta" href="{play}">{e(t['cta2'])} &#8594;</a>
</div></section>

<section id="faq" class="faq"><div class="wrap">
<h2>{e(t['faq_h'])}</h2>
{faq}
<p class="again"><a class="cta" href="{play}">{e(t['cta'])} &#8594;</a></p>
</div></section>
</main>
<footer><div class="wrap">
<p>{e(t['credit'])}</p>
<p>{e(t['made'])}</p>
<nav class="langlist" aria-label="{e(t['lang_label'])}">{foot_langs}</nav>
</div></footer>
</body>
</html>
"""


for code in ORDER:
    folder = site if code == MAIN else os.path.join(site, code)
    os.makedirs(folder, exist_ok=True)
    with open(os.path.join(folder, 'index.html'), 'w', encoding='utf-8') as f:
        f.write(page(code))

links = '\n'.join(f'    <xhtml:link rel="alternate" hreflang="{hreflang(c)}" href="{url(c)}"/>' for c in ORDER)
links += f'\n    <xhtml:link rel="alternate" hreflang="x-default" href="{BASE}"/>'
urls = '\n'.join(f'  <url>\n    <loc>{url(c)}</loc>\n{links}\n  </url>' for c in ORDER)
with open(os.path.join(site, 'sitemap.xml'), 'w', encoding='utf-8') as f:
    f.write('<?xml version="1.0" encoding="UTF-8"?>\n'
            '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">\n'
            f'{urls}\n</urlset>\n')
with open(os.path.join(site, 'robots.txt'), 'w', encoding='utf-8') as f:
    f.write(f'User-agent: *\nAllow: /\n\nSitemap: {BASE}sitemap.xml\n')
print('pages written:', ', '.join(ORDER))
