from pathlib import Path
import json, math, shutil, tempfile, subprocess
from openpyxl import load_workbook
P=Path(__file__).resolve().parent;D=json.loads((P/'benchmark-data.json').read_text());checks=[]
def ck(n,x):
 checks.append({'check':n,'pass':bool(x)})
 if not x:raise AssertionError(n)
ck('120 unique vendor-criterion pairs',len(D['assessments'])==len({(a['vendor'],a['criterion']) for a in D['assessments']})==120)
ck('all sources resolve',all(s in {x['id'] for x in D['sources']} for a in D['assessments'] for s in a['sources']))
ck('all non-unknown assessments have a source',all(a['sources'] for a in D['assessments'] if a['status']!='U'))
ck('172 source functions exactly once',len(D['functions'])==len({f['id'] for f in D['functions']})==172)
ck('all mappings resolve',all(f['criterion'] in {c['id'] for c in D['criteria']} for f in D['functions']))
ck('no fabricated measured depth',all(a['depth'] is None for a in D['assessments']))
ck('overseas vendor excluded from default percentages',all(not a['comparable'] for a in D['assessments'] if a['vendor']=='zk'))
rec=json.loads((P/'recalculation.json').read_text());recalc=Path(rec['recalculated_file']);orig=P/'deliverables/五厂商量化对比与实测台账.xlsx'
b=load_workbook(orig,data_only=True);bf=load_workbook(orig,data_only=False);br=load_workbook(recalc,data_only=True)
errs=[];fc=0
for sh in br:
 for row in sh:
  for c in row:
   if c.data_type=='e':errs.append((sh.title,c.coordinate,c.value))
ck('LibreOffice zero formula errors',not errs)
for sh in bf:
 for row in sh:
  for c in row:
   if c.data_type!='f':continue
   fc+=1;x=b[sh.title][c.coordinate].value;y=br[sh.title][c.coordinate].value
   ck('recalculated '+sh.title+'!'+c.coordinate,math.isclose(x,y,rel_tol=1e-10) if isinstance(x,(float,int)) and isinstance(y,(float,int)) else x==y or (x in ('',None) and y in ('',None)))
# Verify formulas on non-empty and zero-denominator inputs with real office recalculation.
t=Path(tempfile.mkdtemp(prefix='cpt-formula-check-'));out=t/'out';out.mkdir();test=t/'test.xlsx';wb=load_workbook(orig);ws=wb['实测量化']
values={'C2':1000,'D2':995,'F2':1.2,'G2':92,'H2':8,'I2':8,'L2':4,'N2':100,'O2':90,'Q2':100000,'R2':30000,'S2':20000,'T2':10000,'U2':10000}
for c,v in values.items():ws[c]=v
for c in ['C3','D3','G3','H3','I3','L3','N3','O3','Q3','R3','S3','T3','U3']:ws[c]=0
wb['场景权重']['B2']=0;wb['场景权重']['C2']=0;wb['场景权重']['D2']=0;wb.save(test)
r=subprocess.run(['soffice','--headless','--convert-to','xlsx','--outdir',str(out),str(test)],capture_output=True,text=True,timeout=60)
ck('edge-case recalc completed',r.returncode==0 and (out/'test.xlsx').exists());tw=load_workbook(out/'test.xlsx',data_only=True);ts=tw['实测量化']
for c,v in {'E2':.995,'J2':.92,'K2':.92,'M2':2,'P2':.9,'W2':190000}.items():ck('numeric sample '+c,math.isclose(ts[c].value,v,rel_tol=1e-10))
for c in ['E3','J3','K3','M3','P3']:ck('zero denominator '+c,ts[c].value in ('',None))
ck('zero quote differs from missing quote',ts['W3'].value==0 and ts['W4'].value in ('',None))
ck('zero weight stays blank',tw['场景加权覆盖']['B2'].value in ('',None))
report={'checks':checks,'formula_count':fc,'sheets':bf.sheetnames,'recalc_errors':errs,'all_passed':all(x['pass'] for x in checks),'delivered_workbook':'Original xlsxwriter workbook retained after independently matching LibreOffice recalculation; preserves charts and formatting.'}
(P/'data-validation.json').write_text(json.dumps(report,ensure_ascii=False,indent=2));print(json.dumps({'passed':len(checks),'formulas':fc,'sheets':len(bf.sheetnames)},ensure_ascii=False))
