"""Meaningful evidence checks: conservation, raw truth, prefix truncation and preservation."""
import hashlib
import csv
from analyze import *

def main():
    tests=[]
    def ok(name,detail):tests.append(dict(check=name,status='PASS',detail=detail))
    qs=read(ROOT/'analysis/archive-inventory-coverage.json');es=lines(ROOT/'analysis/events.jsonl')
    ocs=read(ROOT/'analysis/entry-occurrences.json');cps=read(ROOT/'analysis/temporal-checkpoints.json')
    assert len(qs)==10 and len(es)==23279 and sum(q['decision_points'] for q in qs)==23971
    assert len(ocs)==1967 and len(cps)==160
    for q in qs:
        assert sum(q['source_'+lev] for lev in ['EXACT','PARTIAL','INFERRED','MISSING'])==q['confirmed_source_visits']
        assert sum(q['decision_'+lev] for lev in ['EXACT','PARTIAL','INFERRED','MISSING'])==q['decision_points']
        assert sum(q['arrival_'+lev] for lev in ['EXACT','PARTIAL','INFERRED','MISSING'])==q['confirmed_arrivals']
        assert q['complete_valid_Concrete_inventory_EXACT']==0 and q['exact_remaining_executable_concrete'] is None
    ok('ten_run_and_evidence_denominator_conservation',dict(executions=len(es),selections=23971,occurrences=len(ocs),checkpoints=len(cps)))
    # Original transition tuple/input/receipt and input/selection chronology.
    for e in es:
        assert e['raw_event']['input_confirmed'] is True
        r=read(e['receipt_path']);inp=read(e['input_path'])['input']
        assert (e['state'],e['target_state'],e['model_action'],e['version'])==(r['state_id'],e['raw_event']['target_state'],r['model_action_id'],r['abstraction_version'])
        assert e['input_ns']<=e['monotonic_ns'] and e['input_version']==inp['abstraction_version']
        if e['latest_selection']:
            s=e['latest_selection'];assert s['monotonic_ns']<=e['input_ns']
            assert (s['step'],s['abstraction_version'],s['state_id'],s['model_action_id'])==(e['input_step'],e['input_version'],e['state'],e['input_model_action'])
        assert e['exact_remaining_executable_concrete'] is None
    ok('all_23279_input_receipt_transition_equalities',True)
    # Recompute first-seen facts using only chronological prefixes.
    for q in qs:
        ee=[e for e in es if e['app']+'-'+e['rep']==q['run']];seen={k:set() for k in ['concrete_key','activity_concrete_key','model_transition_key','ID_only_model_transition_key']}
        for e in ee:
            for f,flag in [('concrete_key','first_local_concrete'),('activity_concrete_key','first_activity_concrete'),('model_transition_key','first_model_transition'),('ID_only_model_transition_key','first_ID_only_model_transition')]:
                assert e[flag]==bool(e[f] and e[f] not in seen[f])
                if e[f]:seen[f].add(e[f])
    ok('first_seen_flags_replayed_without_final_model',23279)
    # Physically drop all future rows and target-Activity observations, not merely filter their counters.
    count=0
    for meta in read(ROOT/'evidence/archive-manifest.json'):
        rid=meta['app']+'-'+meta['rep'];ee=[e for e in es if e['app']+'-'+e['rep']==rid]
        raw=lines(Path(meta['graph_path']).parent/'jev-events.jsonl')
        sels=[dict(e,event_line=n) for n,e in enumerate(raw,1) if e['event']=='SELECTION_DECISION']
        for c in [c for c in cps if c['run']==rid]:
            cut=c['cutoff_ns'];short_es=[]
            for original in ee:
                if original['monotonic_ns']>cut:break
                e=copy.deepcopy(original)
                for forbidden in ['final_exact','edge_id','target_activity']:e.pop(forbidden,None)
                e['target_activity_evidence']=[a for a in e['target_activity_evidence'] if a['available_ns']<=cut]
                short_es.append(e)
            prefix=dict(id=rid,app=meta['app'],rep=meta['rep'],events=short_es,byid={e['transition_id']:e for e in short_es},
                selections=[s for s in sels if s['monotonic_ns']<=cut])
            past=[o for o in ocs if o['run']==rid and o['function']==c['function'] and o['monotonic_ns']<=cut]
            segs=[segment(prefix,prefix['byid'][o['entry']],'B',cut) for o in past]
            ids={t for s in segs for t in s['ids']}
            assert ids==set(c['history_ids']),c['id']
            assert metric(prefix,sorted(ids,key=lambda t:prefix['byid'][t]['order']))==c['history'],c['id']
            inv=inventory_at(prefix,{prefix['byid'][t]['source_key'] for t in ids},cut)
            assert inv==c['inventory'],c['id']
            assert all(r['available_ns']<=cut for r in inv['snapshots'])
            assert c['future_validation']['events']==len({t for o in ocs if o['run']==rid and o['function']==c['function'] and o['monotonic_ns']>cut for t in o['segments']['B']['ids']})
            count+=1
    ok('physical_future_truncation_keeps_all_history_features_identical',count)
    # No stitched paths: each adjacent step matches State, GUI identity, timestamp, version and native event order.
    by={(e['app']+'-'+e['rep'],e['transition_id']):e for e in es}
    for o in ocs:
        for seg in o['segments'].values():
            for a,b in zip(seg['ids'],seg['ids'][1:]):assert not continuity(by[(o['run'],a)],by[(o['run'],b)])
    ok('all_occurrence_windows_are_contiguous_confirmed_execution',1967)
    for path in (ROOT/'analysis').glob('*.csv'):
        obj=path.with_suffix('.json')
        if obj.is_file():
            rows=read(obj)
            if isinstance(rows,list):
                csv.field_size_limit(256*1024*1024)
                with path.open() as f:assert sum(1 for _ in csv.DictReader(f))==len(rows),path.name
    ok('CSV_JSON_row_conservation','all paired generated tables')
    schema=read(ROOT/'analysis/shadow-capture.schema.json');example=read(ROOT/'analysis/shadow-capture.example.json')
    def structure(s):
        if isinstance(s,dict):
            if '$ref' in s:
                assert s['$ref'].startswith('#/$defs/') and s['$ref'].split('/')[-1] in schema['$defs']
            if 'required' in s:assert set(s['required'])<=set(s.get('properties',{}))
            for v in s.values():structure(v)
        elif isinstance(s,list):
            for v in s:structure(v)
    structure(schema)
    assert set(schema['required'])<=set(example)
    assert example['proposal_only'] is True and example['event_kind'] in schema['properties']['event_kind']['enum']
    assert set(schema['properties']['coverage']['required'])<=set(example['coverage'])
    assert not example['coverage']['model_actions_complete'] and not example['coverage']['all_members_native_validated']
    ok('proposal_schema_local_refs_required_fields_and_non_data_example','structural checks; no external full Draft202012 validator installed')
    protected=read(ROOT/'evidence/protected-files-before.json')
    for path,h in protected.items():assert hashlib.sha256((REPO/path).read_bytes()).hexdigest()==h,path
    ok('previous_five_studies_native_and_user_modified_files_preserved',len(protected))
    # All externally read archives remain byte-identical.
    archive_hashes=read(ROOT/'evidence/read-source-sha256.json')
    for path,h in archive_hashes.items():
        p=Path(path)
        if not p.resolve().is_relative_to(ROOT):assert hashlib.sha256(p.read_bytes()).hexdigest()==h,path
    ok('all_archive_read_source_hashes_unchanged',sum(not Path(p).resolve().is_relative_to(ROOT) for p in archive_hashes))
    if (ROOT/'evidence/image-index.json').exists():
        assets=read(ROOT/'evidence/image-index.json')
        for name,a in assets.items():
            assert hashlib.sha256(Path(a['original_path']).read_bytes()).hexdigest()==a['original_sha256']
            assert hashlib.sha256((ROOT/'visuals/assets'/name).read_bytes()).hexdigest()==a['copy_sha256']
        ok('real_archived_pixel_provenance_and_copy_hashes',len(assets))
    required=['README.md','ape-action-inventory-audit.md','archive-inventory-coverage.md','functional-opportunity-ledger.md','cross-entry-sharing.md',
        'multi-app-analysis.md','temporal-validation.md','shadow-capture-design.md','representative-cases.md','conclusion.md','visuals/index.html']
    for name in required:assert (ROOT/name).stat().st_size>300
    ok('all_required_deliverables_present',len(required))
    write('evidence/verification.json',dict(status='PASS',tests=tests,online_changes=False,new_experiments=False))
    print(json.dumps(tests,ensure_ascii=False,indent=2))

if __name__=='__main__':main()
