import copy
import datetime
import json
from pathlib import Path
from zoneinfo import ZoneInfo

ROOT=Path(__file__).resolve().parent
TZ=ZoneInfo('Asia/Shanghai')

def equal(a,b):
    if type(a) is not type(b):return False
    if isinstance(a,dict):return a.keys()==b.keys() and all(equal(a[k],b[k]) for k in a)
    if isinstance(a,list):return len(a)==len(b) and all(equal(x,y) for x,y in zip(a,b))
    return a==b

def stamp(epoch):
    return datetime.datetime.fromtimestamp(int(epoch),TZ).strftime('%Y-%m-%d %H:%M:%S')

def source_row(row,e,module,stage):
    r=copy.deepcopy(row)
    assert type(r['page_id']) is int,'Original numeric page ID type'
    ids=e['owned'][module];assert r['page_id'] in ids,'Run owned original ID only'
    original=next(x for x in e['independent_final_original_rows'] if int(x['page_id'])==r['page_id'])
    assert type(r['create_time']) is str and r['create_time']==stamp(original['create_time']),'Original create epoch projection'
    if stage=='CREATED':epoch=int(original['create_time'])
    else:
        writer='EDIT' if stage=='EDITED' else 'SET_HOME_SECOND'
        key='PILOT_PG_'+module.upper()+'_'+writer
        assert key in e['actual_SQL_update_epochs'],'Recorded exact actual write epoch required'
        epoch=e['actual_SQL_update_epochs'][key]
        c=next(c for c in e['cases'] if c['scenario']==key)
        assert c['window'][0]-2<=epoch<=c['window'][1]+2,'Actual write epoch request window'
    assert type(r['update_time']) is str and r['update_time']==stamp(epoch),'Exact actual SQL timestamp projection, original PDO positive-time proof paired'
    r['page_id']='OWN_'+module+'_'+str(ids.index(row['page_id']))
    r['create_time']='ORIGINAL_CREATE_EPOCH_VERIFIED'
    r['update_time']='ACTUAL_WRITE_EPOCH_VERIFIED'
    return r

def normalize(case,e):
    body=copy.deepcopy(case['body']);scenario=case['scenario'];module=case['module']
    assert body['trace_id']=='VALID_DYNAMIC_TRACE'
    assert type(body['status']) is int and case['http_status']==200 and case['native_cli_exit']==0
    if scenario.endswith('DETAIL_CREATED') or scenario.endswith('DETAIL_EDITED'):
        stage='CREATED' if scenario.endswith('DETAIL_CREATED') else 'EDITED'
        body['data']['detail']=source_row(body['data']['detail'],e,module,stage)
    elif scenario.endswith('FILTER_LIST') or scenario.endswith('LIST_AFTER_DELETE'):
        stage='EDITED' if scenario.endswith('FILTER_LIST') else 'HOME_SECOND'
        body['data']['list']['data']=[source_row(r,e,module,stage) for r in body['data']['list']['data']]
    return body

def verify_final(p,e):
    assert p['native_exit']==0 and len(p['cases'])==4
    assert p['fresh_original_rows_unchanged'] and p['natural_sha_unchanged'] and p['tokens_reclaimed']
    out={}
    for c in p['cases']:
        assert c['complete_original_equal'] and equal(c['body']['data'],c['original_oracle']['complete_expected_data'])
        b=copy.deepcopy(c['body']);r=b['data']['detail'];oracle=c['original_oracle']['original_row']
        module='store' if '_STORE_' in c['scenario'] else 'terminal'
        assert type(r['page_id']) is int and r['page_id'] in e['owned'][module]
        assert r['create_time']==stamp(oracle['create_time']) and r['update_time']==stamp(oracle['update_time'])
        r['page_id']='OWN_'+module+'_'+str(e['owned'][module].index(r['page_id']))
        r['create_time']='ORIGINAL_EPOCH_VERIFIED';r['update_time']='ORIGINAL_EPOCH_VERIFIED'
        out[c['scenario']]=b
    for report in p['cli_reports']:
        assert report['stats']['requests']['total']==4 and report['stats']['assertions']['total']==16 and report['stats']['assertions']['failed']==0 and report['failures']==0
    return out

def main():
    lanes={k:json.loads((ROOT/('evidence-'+k+'.json')).read_text()) for k in ['B','C']}
    norm={};no_db=[]
    for lane,e in lanes.items():
        assert e['errors']==[] and len(e['cases'])==30 and len(e['proofs'])==16
        assert e['natural_unchanged'] and e['final_natural_unchanged'] and e['before']==e['after']
        assert e['resources']['tokens_absent'] and e['resources']['cache_files']==0
        assert e['native_cli_totals']=={'requests':30,'assertions':150,'failures':0}
        assert len(e['write_audit'])==16 and all(x['only_page_table'] and x['own_tenant_predicate'] for x in e['write_audit'])
        assert all(x['own_tenant_in_actual_framework_SQL'] for x in e['dry_setHome'].values())
        assert e['metadata']['foreign_keys']==e['metadata']['triggers']==e['metadata']['own_collision']==0
        for module in ['store','terminal']:
            s='PILOT_PG_'+module.upper()+'_DEFAULT_DATA'
            ends=[v for v in e['events'] if v.get('scenario')==s and v.get('kind')=='request_end']
            # Model construction calls getFields: no business-row read, but real metadata SQL exists.
            assert len(ends)==1 and ends[0]['observed_queries']==(1 if lane=='B' else 3)
            queries=[v for v in e['events'] if v.get('scenario')==s and v.get('kind')=='query_observed']
            expected={'357b0160ccd7ca645df12d6e7d464c8c45a169d2478f7ed1072e87da1e3b59c1'} if lane=='B' else {'2a4d84558495d73d7809ba6d9f57ad56443c9429ac83f881b661f7334874b78f','2a2aae4e127669b3ba48830d9be8ba24b3d9f37739bfcbf87ebbf64d5a4e74bc','0e0ff06ada018c16c9501ac0c101f53d5eed7076e0be5ef7cb238cd0a19a103e'}
            assert {v['fingerprint'] for v in queries}==expected
            no_db.append(lane+':'+module)
        norm[lane]={c['scenario']:normalize(c,e) for c in e['cases']}
    assert norm['B'].keys()==norm['C'].keys()
    differences=[s for s in norm['B'] if not equal(norm['B'][s],norm['C'][s])]
    finals={k:verify_final(json.loads((ROOT/('final-read-'+k+'.json')).read_text()),lanes[k]) for k in lanes}
    differences += ['final:'+s for s in finals['B'] if not equal(finals['B'][s],finals['C'][s])]
    result={'status':'PASS' if not differences else 'FAIL','flow_HTTP_pairs':30,'final_exact_original_projection_pairs':4,'actual_native_HTTP_total':68,'named_native_assertions_total':332,'original_full_JSON_state_checks':32,'actual_DML_audited_total':32,'defaultData_metadata_only_no_business_row':no_db,'differences':differences,'timestamp_evidence':'Flow create_time final original epoch; flow update_time actual SQL epoch plus same write independent PDO state proof; final four details exact fresh original create/update epochs before-after unchanged','scope':'Partial business scenarios for twelve DB methods; two defaultData methods metadata-only. Not full system acceptance.'}
    (ROOT/'comparison.json').write_text(json.dumps(result,ensure_ascii=False,indent=2)+'\n')
    print(json.dumps(result,ensure_ascii=False));assert not differences

if __name__=='__main__':main()
