# -*- 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))

text='中文😀 e\u0301 与 <script>window.injected=1</script> 字面内容。'
jseed('response-fixture.json',{'fixture_only':True,'offset_unit':'unicode_code_points_local_fixture','text':text,'annotations':[{'start':0,'end':3,'url':'https://developers.openai.com/api/docs/guides/tools-web-search','title':'OpenAI 官方联网引用说明'},{'start':2,'end':6,'url':'https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-a-element','title':'HTML 链接元素'},{'start':0,'end':1,'url':'javascript:alert(1)','title':'不允许的协议'},{'start':999,'end':1000,'url':'https://developers.openai.com/api/docs/guides/tools-web-search','title':'越界样例'}]});d=load('response-fixture.json')
script="""
const DATA=FIXTURE;
const text=document.getElementById('answer');text.textContent=DATA.text;
const status=document.getElementById('status');const list=document.getElementById('sources');const cp=Array.from(DATA.text);
window.renderResults=[];
DATA.annotations.forEach((a,i)=>{
 const li=document.createElement('li');const valid=Number.isInteger(a.start)&&Number.isInteger(a.end)&&a.start>=0&&a.end>a.start&&a.end<=cp.length;
 let url;try{url=new URL(a.url)}catch{}
 const safe=url&&['https:','http:'].includes(url.protocol);
 const label=valid?cp.slice(a.start,a.end).join(''):'范围越界';
 const record={valid,safe:Boolean(safe),label};window.renderResults.push(record);
 li.append(document.createTextNode(`[${i+1}] ${label} — `));
 if(safe){const link=document.createElement('a');link.href=url.href;link.textContent=a.title;link.rel='noopener noreferrer';li.append(link)}else{li.append(document.createTextNode('已拒绝危险URL方案'))}
 if(!valid)li.append(document.createTextNode('；请人工核对引用位置'));list.append(li);
});
status.textContent='引用采用本地 Unicode 码点约定。重叠来源分别保留；接真实API时须适配偏移，单位不明则保留全文及来源列表。';
"""
encoded=json.dumps(d,ensure_ascii=False).replace('<','\\u003c').replace('>','\\u003e').replace('&','\\u0026')
page('citation-viewer.html','可点击引用与原文','<p>原创离线响应夹具，未联网取正文。</p><pre id="answer"></pre><p id="status"></p><ol id="sources"></ol><script>'+script.replace('FIXTURE',encoded)+'</script>')
write('dom-check.py',"from pathlib import Path\nimport json\nfrom playwright.sync_api import sync_playwright\nwith sync_playwright() as p:\n    browser=p.chromium.launch()\n    page=browser.new_page()\n    page.goto(Path(__file__).with_name('citation-viewer.html').as_uri())\n    assert page.locator('#answer').inner_text().startswith('中文😀 e\u0301')\n    assert page.locator('#answer script').count()==0\n    assert page.evaluate('window.injected') is None\n    results=page.evaluate('window.renderResults')\n    assert results[0]['label']=='中文😀'\n    assert results[1]['label']=='😀 e\u0301'\n    assert results[2]['safe'] is False\n    assert results[3]['valid'] is False\n    assert page.locator('#sources a').count()==3\n    print(json.dumps(results, ensure_ascii=True))\n    browser.close()\n")
emit({'text':d['text'],'annotations':len(d['annotations']),'negative_examples':['javascript scheme rejected','out of range explicit','overlap retained']})
