#!/usr/bin/env python3
import hashlib, json, sys

def digest(event_without_hash):
    raw = json.dumps(event_without_hash, sort_keys=True, separators=(',', ':')).encode()
    return hashlib.sha256(raw).hexdigest()

def validate(packet):
    if packet.get('contract') != 'continuity.carrier.v1': return 'REFUSE_CONTRACT'
    if packet.get('provenance') != 'OBSERVED': return 'REFUSE_PROVENANCE'
    if packet.get('status') != 'READY': return 'REFUSE_STATUS'
    events = packet.get('events')
    if not isinstance(events, list) or not events: return 'REFUSE_EVENTS'
    ids = [e.get('id') for e in events]
    if ids != list(range(1, len(ids)+1)): return 'REFUSE_EVENT_SEQUENCE'
    previous = '0'*64
    for e in events:
        body = {k:v for k,v in e.items() if k != 'hash'}
        if e.get('previous_hash') != previous: return 'REFUSE_CHAIN'
        if e.get('hash') != digest(body): return 'REFUSE_HASH'
        previous = e['hash']
    if packet.get('head') != ids[-1] or packet.get('expected_head') != ids[-1]: return 'REFUSE_HEAD'
    return 'PASS'

def make_packet(n=3):
    events=[]; previous='0'*64
    for i in range(1,n+1):
        body={'id':i,'kind':'session-step','summary':f'step-{i}','previous_hash':previous}
        h=digest(body); events.append({**body,'hash':h}); previous=h
    return {'contract':'continuity.carrier.v1','provenance':'OBSERVED','status':'READY','head':n,'expected_head':n,'events':events}

def main():
    cases=[]
    good=make_packet(); cases.append(('good',good,'PASS'))
    bad=json.loads(json.dumps(good)); bad['events'][1]['summary']='tampered'; cases.append(('tampered',bad,'REFUSE_HASH'))
    inherited=json.loads(json.dumps(good)); inherited['provenance']='INHERITED'; cases.append(('inherited',inherited,'REFUSE_PROVENANCE'))
    gap=json.loads(json.dumps(good)); gap['events'][1]['id']=3; cases.append(('gap',gap,'REFUSE_EVENT_SEQUENCE'))
    out=[]
    for name,p,want in cases:
        got=validate(p); out.append({'case':name,'got':got,'expected':want,'ok':got==want})
    print(json.dumps({'all_expected':all(x['ok'] for x in out),'results':out},sort_keys=True))
    return 0 if all(x['ok'] for x in out) else 1
if __name__=='__main__': sys.exit(main())
