# -*- 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))

seed('probabilities.csv','id,p_positive,y\n1,.1,0\n2,.2,0\n3,.6,1\n4,.7,0\n5,.8,1\n6,.9,0\n')
rows=list(csv.DictReader((BASE/'probabilities.csv').open()));bins=[[] for _ in range(5)]
if NEG: rows[0]['p_positive']='1.2'
for r in rows:
    p=float(r['p_positive']);y=int(r['y'])
    if not math.isfinite(p) or not 0<=p<=1 or y not in (0,1): raise ValueError('概率或二值标签越界')
    bins[min(int(p*5),4)].append((p,y))
out=[];circles=[]
for i,b in enumerate(bins):
    mp=sum(p for p,y in b)/len(b) if b else None;freq=sum(y for p,y in b)/len(b) if b else None;out.append([i/5,(i+1)/5,len(b),mp,freq])
    if b: circles.append(f'<circle cx="{40+mp*300}" cy="{340-freq*300}" r="7" fill="#1268a0"/><text x="{45+mp*300}" y="{340-freq*300}">n={len(b)}</text>')
table('bin-counts.csv',['left','right','n','mean_p_positive','positive_fraction'],out)
write('reliability.svg','<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 390"><rect width="400" height="390" fill="white"/><path d="M40 40V340H340M40 340L340 40" stroke="#999" fill="none"/>'+''.join(circles)+'<text x="40" y="380">mean positive probability / positive fraction</text></svg>');emit({'bins':out})
