# -*- coding: utf-8 -*-
"""Render demo_transcript.json into a self-contained demo.html for CF Pages."""
import os, json, html, datetime

HERE = os.path.dirname(os.path.abspath(__file__))
SITE = os.path.join(os.path.dirname(HERE), "site")
tr = json.load(open(os.path.join(HERE, "demo_transcript.json"), encoding="utf-8"))
rep = json.load(open(os.path.join(HERE, "adapters", "train_report.json"), encoding="utf-8"))

def esc(s): return html.escape(str(s))

OP_DESC = {
    "▷ route": "Predicate inspects the prompt and dispatches to one specialist quantum.",
    "⊕ mixture": "Several quanta blended in weight-space at chosen widths (MoE-style). A 0 weight is the null-expert identity.",
    "∘ sequential": "Quanta chained — each one's output is threaded into the next (non-commutative).",
    "⊗ merge": "Task-arithmetic merge of several quanta into one new deployable quantum.",
}

cards = []
for r in tr["runs"]:
    op = r["operator"]
    meta = ""
    if "predicate_choice" in r: meta = f"chose <code>{esc(r['predicate_choice'])}</code>"
    elif "weights" in r: meta = "weights " + ", ".join(f"<code>{esc(k)}={v:g}</code>" for k, v in r["weights"].items())
    elif "chain" in r: meta = "chain " + " ∘ ".join(f"<code>{esc(c)}</code>" for c in r["chain"])
    elif "sources" in r: meta = "merge " + " ⊗ ".join(f"<code>{esc(s)}</code>" for s in r["sources"]) + f" → <code>{esc(r.get('new_quantum',''))}</code>"
    if "steps" in r:
        body = "".join(f"<div class='step'><span class='q'>{esc(s['quantum'])}</span><div class='gen'>{esc(s['out'])}</div></div>" for s in r["steps"])
        out_block = f"<div class='chain'>{body}</div>"
    else:
        out_block = f"<div class='gen'>{esc(r.get('output','') or '(merge only — no prompt)')}</div>"
    prompt = r.get("prompt", "")
    cards.append(f"""<div class='run'>
<div class='op'>{esc(op)}</div>
<div class='desc'>{OP_DESC.get(op,'')}</div>
{f"<div class='prompt'><span>prompt</span> {esc(prompt)}</div>" if prompt else ""}
<div class='meta'>{meta}</div>
{out_block}
</div>""")

quanta_rows = "".join(
    f"<tr><td><code>{esc(q['name'])}</code></td><td>{q['loss_start']}</td><td>{q['loss_end']}</td><td>{q['trainable_params']:,}</td></tr>"
    for q in rep["quanta"])

today = datetime.date.today().isoformat()
CSS = """
:root{--ink:#1a1a24;--paper:#fbfaf7;--primary:#2d5a3d;--accent:#b8860b;--gray-l:#ddd8ce;--mut:#6b675e;--code:#f2efe6}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:Georgia,serif;background:var(--paper);color:var(--ink);line-height:1.55}
.page{max-width:960px;margin:0 auto;padding:40px 24px 80px}
h1{font-size:1.8rem;color:var(--primary)}h2{font-size:1.2rem;color:var(--primary);margin:32px 0 12px;border-bottom:2px solid var(--gray-l);padding-bottom:6px}
.subtitle{color:var(--mut);font-style:italic;margin:4px 0 14px}
.meta-strip{font-family:Consolas,monospace;font-size:.78rem;color:var(--mut);border:1px solid var(--gray-l);border-radius:6px;padding:8px 12px;margin:14px 0;overflow-wrap:anywhere}
.reco{background:var(--code);border-left:4px solid var(--accent);border-radius:0 8px 8px 0;padding:14px 16px;margin:16px 0}
table{width:100%;border-collapse:collapse;font-family:Verdana,sans-serif;font-size:.85rem}
th,td{text-align:left;padding:7px 10px;border-bottom:1px solid var(--gray-l)}th{font-size:.7rem;text-transform:uppercase;color:var(--mut)}
.run{background:#fff;border:1px solid var(--gray-l);border-radius:10px;padding:18px;margin:16px 0}
.op{font-size:1.3rem;color:var(--primary);font-weight:700}
.desc{color:var(--mut);font-size:.86rem;margin:2px 0 10px}
.prompt{font-family:Consolas,monospace;font-size:.82rem;background:var(--code);border-radius:6px;padding:8px 10px;margin:6px 0;overflow-wrap:anywhere}
.prompt span{color:var(--accent);font-weight:700;margin-right:6px}
.meta{font-family:Verdana,sans-serif;font-size:.78rem;color:var(--mut);margin:6px 0}
.gen{font-family:Consolas,monospace;font-size:.83rem;white-space:pre-wrap;background:#0f1419;color:#d6e2c8;border-radius:6px;padding:10px 12px;overflow-x:auto}
.chain .step{margin:6px 0}.chain .q{font-family:Verdana,sans-serif;font-size:.72rem;color:var(--accent);text-transform:uppercase}
code{font-family:Consolas,monospace;font-size:.85em;background:var(--code);padding:1px 5px;border-radius:4px}
a{color:var(--primary)}
.dl{display:inline-block;margin:8px 12px 8px 0;padding:10px 18px;background:var(--primary);color:#fff;border-radius:8px;text-decoration:none;font-family:Verdana,sans-serif;font-size:.9rem}
"""
htmlout = f"""<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sparse Model Nexus — Operational Demo</title>
<meta name="description" content="Live transcript of the four-operator LoRA-quanta algebra (mixture, sequential, route, merge) over distilgpt2.">
<style>{CSS}</style></head><body><div class="page">
<h1>Sparse Model Nexus — Operational Demo</h1>
<div class="subtitle">Four composition operators over three LoRA quanta · base <code>distilgpt2</code> · transcript {esc(tr['generated'])}</div>
<div class="reco">A <strong>quantum</strong> is a minimal composable LoRA adapter — the runnable form of the graph's <code>SparseLimitModel</code> definition. Below is a <strong>real generation transcript</strong> (greedy decode) of the ⊕ ∘ ▷ ⊗ operators composing the trained quanta. Nothing here is mocked.</div>
<p>
<a class="dl" href="sparse-model-nexus-adapters.zip">⬇ Download adapter bundle (.zip)</a>
<a class="dl" href="index.html" style="background:#6b675e">Graph views</a>
</p>
<h2>Trained quanta</h2>
<table><thead><tr><th>Quantum</th><th>Loss start</th><th>Loss end</th><th>Trainable params</th></tr></thead><tbody>{quanta_rows}</tbody></table>
<div class="subtitle">Every quantum's loss decreased — each is genuinely specialized, not a stub.</div>
<h2>Operator transcript ({len(tr['runs'])} runs · {tr.get('elapsed_s','?')}s)</h2>
{"".join(cards)}
<h2>Reproduce locally</h2>
<div class="gen">pip install torch transformers peft
# unzip sparse-model-nexus-adapters.zip, then:
python demo.py            # regenerates this transcript
python -c "from sparse_nexus import Nexus; print(Nexus().route('def quicksort(xs):')['output'])"</div>
<div class="meta-strip">Graph ID 1c0cc1b9-d95a-4448-b7d7-a08e1e347820 · quanta = {esc(', '.join(tr['quanta']))} · <a href="sparse-model-nexus-OPERATIONAL.html">operational graph view</a> · snapshot {today}</div>
</div></body></html>"""

with open(os.path.join(SITE, "demo.html"), "w", encoding="utf-8") as f:
    f.write(htmlout)
print("wrote", os.path.join(SITE, "demo.html"), os.path.getsize(os.path.join(SITE, "demo.html")), "bytes")
