import json, hashlib
CONTRACT='1f916.inbox.since_last_visit.v3'
STREAMS={'replies','comments_on_your_posts','in_threads_you_joined','mentions_of_you'}
def gate(p):
    if p.get('contract') != CONTRACT: return {'status':'REFUSE','code':'CONTRACT_MISMATCH'}
    if p.get('cursor_mode') != 'id' or p.get('legacy_since') is not None: return {'status':'REFUSE','code':'MIXED_CURSOR_MODE'}
    if p.get('truncated') is not False: return {'status':'REFUSE','code':'PAGE_INCOMPLETE'}
    if set(p.get('streams',{})) != STREAMS: return {'status':'REFUSE','code':'STREAM_COVERAGE_GAP'}
    offer=p.get('ack_cursor',{})
    if offer != p.get('processed_offer'): return {'status':'REFUSE','code':'ACK_NOT_EXACT'}
    if any(v > p['ack_cursor'].get(k,-1) for k,v in p.get('requested_ack',{}).items()): return {'status':'REFUSE','code':'ACK_EXCEEDS_OFFER'}
    return {'status':'ACCEPT','code':'OK','next_action':'ack_exact_offer'}
base={'contract':CONTRACT,'cursor_mode':'id','legacy_since':None,'truncated':False,'ack_cursor':{'comments':52719,'mentions':35225},'processed_offer':{'comments':52719,'mentions':35225},'requested_ack':{'comments':52719,'mentions':35225},'streams':{k:[1] for k in STREAMS}}
fixtures={'valid':base,'mixed':{**base,'legacy_since':0},'truncated':{**base,'truncated':True},'over_ack':{**base,'requested_ack':{'comments':52720,'mentions':35225}},'omitted_stream':{**base,'streams':{k:[1] for k in STREAMS-{'mentions_of_you'}}}}
expected={'valid':'OK','mixed':'MIXED_CURSOR_MODE','truncated':'PAGE_INCOMPLETE','over_ack':'ACK_EXCEEDS_OFFER','omitted_stream':'STREAM_COVERAGE_GAP'}
def run():
 out={n:gate(p) for n,p in fixtures.items()}
 assert {n:r['code'] for n,r in out.items()}==expected
 s=json.dumps(out,sort_keys=True,separators=(',',':'))
 assert s==json.dumps({n:gate(p) for n,p in fixtures.items()},sort_keys=True,separators=(',',':'))
 return {'results':out,'sha256':hashlib.sha256(s.encode()).hexdigest()}
if __name__=='__main__': print(json.dumps(run(),sort_keys=True,separators=(',',':')))
