#!/usr/bin/env python3
import json
from pathlib import Path
def exact(a,b,path='$'):
    if type(a) is not type(b):raise AssertionError('type '+path)
    if isinstance(a,dict):
        if set(a)!=set(b):raise AssertionError('keys '+path)
        for k in a:exact(a[k],b[k],path+'.'+k)
    elif isinstance(a,list):
        if len(a)!=len(b):raise AssertionError('length '+path)
        for i,(x,y) in enumerate(zip(a,b)):exact(x,y,f'{path}[{i}]')
    elif a!=b:raise AssertionError('value '+path)
def validate(x):
    assert not x['errors'] and not x['findings'] and len(x['cases'])==20
    for c in x['cases']:
        assert c['method']=='POST' and c['http_status']==200 and c['trace_format_valid'] is True
        assert c['body']['status']==(500 if c['scenario']=='PILOT_CF_HEALTH_INVALID_99_REFUSAL' else 200)
        if 'complete_mysql_original_projection_equal' in c:assert c['complete_mysql_original_projection_equal'] is True
        if 'original_response_id' in c:assert type(c['original_response_id']) is int and c['original_response_id']==0 and c['body']['data']['id']==0
    assert len([c for c in x['cases'] if c['scenario'].startswith('PILOT_CF_GREEN_CONFIG_DETAIL_')])==2
    assert len(x['original_proofs'])==7
    assert x['retention']=={'mode':'retained-owned','fresh_counts':{'user':1,'staff':1,'athlete_master':2,'athlete_pal':10,'health':1},'natural_seven_table_sha_unchanged':True,'exact_owned_pks_only':True}
    assert x['resources']=={'token_absent':True,'cache_files':0}
    assert x['read_only_green']=={'new_http_requests':2,'new_DML':0,'no_successful_writes_replayed':True,'raw_wire_independent_original_projection_equal':True}
    assert x['oracle_object_member_order_only_corrected'] is True
    assert x['independent_final_original']=={'fresh_counts':{'user':1,'staff':1,'athlete_master':2,'athlete_pal':10,'health':1},'exact_all_PK_and_user_staff_master_pal_health_ownership_verified':True,'all_business_original_columns_and_clob_values_verified':True,'business_datetime_HTTP_windows_verified':True,'native_DB_clock_offset_within_three_seconds':True,'config_response_id_zero_preserved':True,'actual_health_old_relation_physical_delete_verified':True,'user_money_zero_and_staff_demographics_unchanged':True,'read_only_green_DML_zero':True}
    assert len(x['baseline_oracle_reverification'])==2
    for c in x['baseline_oracle_reverification']:assert c['original_mysql_projection_equal'] is (x['lane']=='B') and c['strict_object_keys_scalar_types_array_order_verified'] is True
    assert len(x['write_audit'])==19
    for e in x['write_audit']:assert e['owned_scope_verified'] is True and e['table'] in ('yoshop_athlete_master','yoshop_athlete_pal','yoshop_health')
    assert sum(e['operation']=='DELETE' for e in x['write_audit'])==2
    assert not any(e.get('fingerprint_lexical_warning') for e in x['events'])
    assert x['loaded_boundary']['config_service_sha']=='6ad093c02710e3646e11b68abce705beb9ad15dab6bc7f6aa85f7a6fac615237'
    assert x['old_loaded_boundary']['config_service_sha']=='7d40ac160b1eee2cd1ca82b47b63a531ed57c7fcaf6eb22237e2becb7f5ab85c'
def compare(b,c):
    validate(b);validate(c)
    for key in ('cases','original_proofs','retention','resources','independent_final_original','read_only_green','loaded_boundary','old_loaded_boundary'):exact(b[key],c[key],key)
    assert len(b['mysql_before_after_unchanged'])==2 and not c['mysql_before_after_unchanged']
    assert all(x['complete_wire_value_type_container_equal'] is True for x in b['mysql_before_after_unchanged'])
    project=lambda x:[{k:e[k] for k in ('operation','table','owned_scope_verified')} for e in x['write_audit']]
    exact(project(b),project(c),'all DML')
    hashes=lambda x:{k:v for e in x['events'] for k,v in e.get('source_hashes',{}).items()}
    bh,ch=hashes(b),hashes(c)
    for k in set(bh)&set(ch):exact(bh[k],ch[k],'actual source '+k)
    methods=sorted({x['method']+' '+x['path'] for x in b['cases']});assert len(methods)==6
    return {'actual_http_pairs_including_preserved_red':22,'final_strict_scenario_pairs':20,'final_positive_pairs':19,'invalid_health_refusal_pairs':1,'new_positive_methods':methods,'strict_wire_type_value_array_order_differences':0,'actual_common_source_hashes':len(set(bh)&set(ch)),'only_post_fix_replays':'two read-only Config.detail; DML0','retained_each_engine':{'user':1,'staff':1,'athlete_master':2,'athlete_pal':10,'health':1},'original_response_id_zero_preserved':True,'MySQL_original_detail_wire_unchanged_cases':2}
if __name__=='__main__':
    p=Path(__file__).parent;print(json.dumps(compare(json.loads((p/'green-evidence-B.json').read_text()),json.loads((p/'green-evidence-C.json').read_text())),indent=2))
