#!/usr/bin/env python3
"""Verify private natural evidence; never outputs its field values."""
import copy,json,pathlib,re,sys,collections
def need(ok,label):
    if not ok:raise AssertionError(label)
def strict(a,b,path='$'):
    need(type(a) is type(b),'type '+path)
    if isinstance(a,dict):
        need(a.keys()==b.keys(),'fields '+path)
        for k in a:strict(a[k],b[k],path+'.'+k)
    elif isinstance(a,list):
        need(len(a)==len(b),'length '+path)
        for i,(x,y) in enumerate(zip(a,b)):strict(x,y,path+'['+str(i)+']')
    else:need(a==b,'value '+path)
def trace_mapped(body):
    body=copy.deepcopy(body);v=body.get('trace_id');need(type(v) is str and re.fullmatch('[0-9a-f]{13}',v),'trace');body['trace_id']='<TRACE>';return body
def load(p):
    p=pathlib.Path(p);return {k:json.loads((p/f).read_text()) for k,f in [('report','verified-report.json'),('rawreport','report.json'),('responses','responses-private.json'),('source','verified-source-private.json')]}
CASES=[m+'_'+n for m in ('S','T') for n in ('MENU_COLD','MENU_REPEAT','ROLE_ALL','ROLE_MATCH','ROLE_QUOTE','ROLE_MISSING')]
def verify_lane(lane,x):
    r=x['report'];need(r['lane']==lane and r['unexpected'] is None,'lane/clean');need([c['case'] for c in r['cases']]==CASES,'cases')
    need(r['before']==r['after'] and r['natural_unchanged'] and r['token_cleaned'] and r['external_attempts']==0 and r['dml']==0 and r['ddl']==0,'boundary')
    frozen=dict((line.split()[1],line.split()[0]) for line in pathlib.Path(__file__).with_name('SOURCE_SHA256.txt').read_text().splitlines())
    need(r['source_sha256']==frozen,'physical frozen source SHA')
    need(all(v is True for v in r['boundary'].values()),'private boundary')
    corr=r['projection_tool_correction'];need(corr['offline_saved_original_wire_and_source_only'] and corr['new_http']==corr['new_database_queries']==0,'offline correction')
    ends=[e for e in r['events'] if e['kind']=='request_end'];queries=[e for e in r['events'] if e['kind']=='query_observed']
    need(len(ends)==12 and all(e['http_status']==200 and not e['fatal'] for e in ends),'actual HTTP')
    need(all(e['operation'] not in ('insert','update','delete','replace','merge') and e['callsites'] and not e['fingerprint_lexical_warning'] for e in queries),'query safety')
    need(r['events']==x['rawreport']['events'],'original events not overwritten')
    for c in r['cases']:
        name=c['case'];resp=x['responses'][name];need(c['source_pass'] and c['difference'] is None,'source verdict')
        need(resp['method']=='GET' and resp['http_status']==200 and resp['body']['status']==200 and resp['path']==c['path'],'real method/status')
        expected=x['source'][name]['expected'];strict(expected,resp['body']['data']['list'],name+'.original-table');trace_mapped(resp['body'])
        if name.endswith(('MENU_COLD','ROLE_ALL','ROLE_MATCH')):need(type(expected) is list and len(expected)>0,'nonempty positive')
        if name.endswith(('ROLE_QUOTE','ROLE_MISSING')):need(expected==[] and type(expected) is list,'typed empty')
    for m in ('S','T'):
        strict(trace_mapped(x['responses'][m+'_MENU_COLD']['body']),trace_mapped(x['responses'][m+'_MENU_REPEAT']['body']),'same-lane repeat')
    for case in ('MENU_COLD','ROLE_ALL','ROLE_MATCH','ROLE_QUOTE','ROLE_MISSING'):
        strict(trace_mapped(x['responses']['S_'+case]['body']),trace_mapped(x['responses']['T_'+case]['body']),'same-lane store/terminal')
    return {'cases':12,'source_pass':12,'distinct_methods':4,'nonempty_methods':4,'natural_before':r['before'],'natural_unchanged':True,'dml':0,'ddl':0,'external_attempts':0,'token_cleaned':True,'raw_query_operations':dict(collections.Counter(e['operation'] for e in queries)),'original_projection_tool_red':corr['original_source_failures'],'source_sha256':r['source_sha256']}
def verify_loaded(b,c):
    out={'status':'pass','coverage':'partial-own-source-natural-snapshots-differ','full_cross_engine_response_equal':False,'methods':[{'method':'GET','path':'/'+m+'/store.'+n+'/list'} for m in ('store','terminal') for n in ('Role','Menu')],'lanes':{l:verify_lane(l,x) for l,x in [('B',b),('C',c)]}}
    for name in [m+'_'+n for m in ('S','T') for n in ('ROLE_QUOTE','ROLE_MISSING')]:strict(trace_mapped(b['responses'][name]['body']),trace_mapped(c['responses'][name]['body']),'cross-engine typed empty')
    out['cross_engine_empty_pairs']=4;out['same_lane_repeat_and_module_pairs']=14
    return out
if __name__=='__main__':
    try:
        need(len(sys.argv)==3,'two private runtime paths required');print(json.dumps(verify_loaded(load(sys.argv[1]),load(sys.argv[2])),ensure_ascii=False,indent=2))
    except Exception as e:print(json.dumps({'status':'fail','class':type(e).__name__,'reason':str(e)}));raise SystemExit(1)
