# -*- coding: utf-8 -*-
"""原创离线合成样例；不连接模型、不读取账户。输出位于脚本旁。
已有输入保留，修改输入后可以重跑。失败边界由 --negative 单独演示。
"""
from pathlib import Path
import sys, json, csv, math, io, hashlib, html, base64, re
sys.stdout.reconfigure(encoding='utf-8')
sys.stderr.reconfigure(encoding='utf-8')
BASE = Path(__file__).resolve().parent
NEG = '--negative' in sys.argv
def write(name, value):
    p=BASE/name; p.parent.mkdir(parents=True,exist_ok=True)
    if isinstance(value,bytes): p.write_bytes(value)
    else: p.write_text(value,encoding='utf-8',newline='')
def seed(name,value):
    if not (BASE/name).exists(): write(name,value)
def jwrite(name,value): write(name,json.dumps(value,ensure_ascii=False,indent=2))
def jseed(name,value):
    if not (BASE/name).exists(): jwrite(name,value)
def load(name): return json.loads((BASE/name).read_text(encoding='utf-8'))
def table(name,heads,rows):
    f=io.StringIO(newline='');w=csv.writer(f);w.writerow(heads);w.writerows(rows);write(name,f.getvalue())
def page(name,title,body):
    write(name,'<!doctype html><html lang="zh-CN"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>'+html.escape(title)+'</title><style>body{max-width:1000px;margin:2rem auto;padding:0 1rem;font:17px/1.7 system-ui}table{border-collapse:collapse}td,th{border:1px solid #aaa;padding:.5rem}pre{white-space:pre-wrap;overflow-wrap:anywhere}img,svg{max-width:100%;height:auto}a{color:#125caa}</style><h1>'+html.escape(title)+'</h1>'+body+'</html>')
def emit(data): print(json.dumps(data,ensure_ascii=False))

import nbformat, tempfile, os
from nbclient import NotebookClient
from jupyter_client.kernelspec import KernelSpecManager
seed('orders.csv','order,amount\na,12\nb,18\nc,25\n')
metadata={'kernelspec':{'name':'python3','display_name':'Python 3','language':'python'}}
codes=["import csv\nfrom pathlib import Path\nrows=list(csv.DictReader(Path('orders.csv').open(encoding='utf-8')))","amounts=[int(r['amount']) for r in rows]","print(sum(amounts))"]
for name,seq in [('replayable.ipynb',codes),('broken.ipynb',[codes[2],codes[0],codes[1]])]:
    if not (BASE/name).exists():
        nb=nbformat.v4.new_notebook(cells=[nbformat.v4.new_code_cell(c) for c in seq],metadata=metadata);nbformat.write(nb,BASE/name)
# 内核配置只写临时目录，不安装到用户级 Jupyter。
with tempfile.TemporaryDirectory() as td:
    kp=Path(td)/'kernels'/'python3';kp.mkdir(parents=True)
    (kp/'kernel.json').write_text(json.dumps({'argv':[sys.executable,'-m','ipykernel_launcher','-f','{connection_file}'],'display_name':'local','language':'python'}))
    manager=KernelSpecManager(kernel_dirs=[str(Path(td)/'kernels')])
    nb=nbformat.read(BASE/('broken.ipynb' if NEG else 'replayable.ipynb'),as_version=4)
    from jupyter_client import AsyncKernelManager
    client=NotebookClient(nb,km=AsyncKernelManager(kernel_name='python3',kernel_spec_manager=manager),timeout=30,resources={'metadata':{'path':str(BASE)}},kernel_name='python3')
    client.execute()
    nbformat.write(nb,BASE/'executed.ipynb')
    emit({'stdout':nb.cells[-1].outputs[0]['text'].strip()})
