#!/usr/bin/env python3
"""Offline stdlib CSV reconstruction and frozen asset verification. No network."""
from pathlib import Path
from hashlib import sha256
from decimal import Decimal
import csv,io,itertools,json,math
MEMBERS=['BE','BG','CZ','DK','DE','EE','IE','EL','ES','FR','HR','IT','CY','LV','LT','LU','HU','MT','NL','AT','PL','PT','RO','SI','SK','FI','SE']
def csvbytes(rows):
 s=io.StringIO(newline='');w=csv.DictWriter(s,list(rows[0]),lineterminator='\n');w.writeheader();w.writerows(rows);return s.getvalue().encode()
def reconstruct(root):
 meta=json.loads((root/'metadata.json').read_text());slug=meta['id'];result={}
 if slug=='eu-home-leaving-gap':
  doc=json.loads((root/'raw/eurostat-yth_demo_030-2025.json').read_text(),parse_float=Decimal);ids=doc['id'];assert ids==['freq','unit','sex','geo','time']
  cats=[[k for k,v in sorted(doc['dimension'][d]['category']['index'].items(),key=lambda kv:kv[1])] for d in ids];decoded=[]
  for i,combo in enumerate(itertools.product(*cats)):
   v=doc['value'];status=doc.get('status',{});value=v[i] if isinstance(v,list) else v.get(str(i));flag=status[i] if isinstance(status,list) else status.get(str(i),'');decoded.append(dict(zip(ids,combo))|{'value':value,'status':flag})
  assert len(decoded)==math.prod(doc['size']) and '50 %' in doc['extension']['description']
  selected=[]
  for geo in ['EU27_2020']+MEMBERS:
   r={x['sex']:x for x in decoded if x['geo']==geo};assert all(r[s]['value'] is not None and not r[s]['status'] for s in ['F','M'])
   selected.append({'geo_code':geo,'country':'EU (27 countries)' if geo=='EU27_2020' else doc['dimension']['geo']['category']['label'][geo],'year':2025,'women_age_years':r['F']['value'],'men_age_years':r['M']['value'],'men_minus_women_years':r['M']['value']-r['F']['value'],'women_status':r['F']['status'],'men_status':r['M']['status']})
  countries=sorted(selected[1:],key=lambda x:(-x['men_minus_women_years'],x['country']));assert sum(x['men_minus_women_years']>0 for x in countries)==26 and [x['geo_code'] for x in countries if x['men_minus_women_years']<0]==['MT'];assert countries[0]['geo_code']=='RO' and countries[0]['men_minus_women_years']==Decimal('4.1')
  result['eu27-sex-gap-2025.csv']=csvbytes(selected[:1]+countries)
  result['source-all-geographies-2025.csv']=csvbytes([{'geo_code':r['geo'],'country':doc['dimension']['geo']['category']['label'][r['geo']],'frequency':r['freq'],'unit':r['unit'],'sex':r['sex'],'year':r['time'],'age_years':'' if r['value'] is None else r['value'],'status':r['status']} for r in decoded])
 else:
  assert slug=='australia-debit-card-takeover';doc=json.loads((root/'raw/rba-table1-web-extract.json').read_text());years=doc['years'];v=doc['rows'];assert years==[2007,2010,2013,2016,2019,2022,2025]
  assert v['Debit cards']==[15,22,24,30,44,51,49] and v['Cash']==[69,62,47,37,27,13,15] and v['Credit and charge cards']==[11,9,19,22,19,26,23]
  result['payment-shares-2007-2025.csv']=csvbytes([{'survey_year':y,'cash_share_of_number_pct':v['Cash'][i],'debit_share_of_number_pct':v['Debit cards'][i],'credit_charge_share_of_number_pct':v['Credit and charge cards'][i]} for i,y in enumerate(years)])
  result['table1-all-methods.csv']=csvbytes([{'method':m,'survey_year':y,'share_of_number_pct':'' if values[i] is None else values[i],'hierarchy':'aggregate; do not add to debit and credit' if m=='Card' else 'subcomponent of Card' if m in ['Debit cards','Credit and charge cards'] else 'other method','source_missing_symbol':'–' if values[i] is None else ''} for m,values in v.items() for i,y in enumerate(years)])
 return result
def main():
 root=Path(__file__).resolve().parent;count=0
 for line in (root/'SHA256SUMS.txt').read_text().splitlines():
  want,name=line.split('  ',1);p=Path(name);assert not p.is_absolute() and '..' not in p.parts;assert sha256((root/p).read_bytes()).hexdigest()==want,'Frozen asset changed: '+name;count+=1
 result=reconstruct(root);out=root/'reconstructed';out.mkdir(exist_ok=True)
 for name,raw in result.items():(out/name).write_bytes(raw);assert (root/name).read_bytes()==raw,'CSV differs: '+name
 print(json.dumps({'status':'PASS','csvReconstructedExactly':list(result),'frozenFilesVerified':count,'nativeBrowserUsed':False,'networkUsed':False,'rendering':'Exact frozen SVG/PNG exports verified, not rerendered','RBAcapture':'Structured official-table transcription, not original HTML; no direct-fetch retry'}))
if __name__=='__main__':main()
