#!/usr/bin/env python3
import hashlib,json,sys
from pathlib import Path
h=lambda p:hashlib.sha256(p.read_bytes()).hexdigest();canon=lambda x:json.dumps(x,sort_keys=True,separators=(',',':')).encode();r=Path(sys.argv[1] if len(sys.argv)>1 else __file__).resolve();r=r if r.is_dir() else r.parent;errors=[]
try:
 m=json.loads((r/'candidate-manifest.json').read_text());s=json.loads((r/'candidate-seal.json').read_text());v=json.loads((r/'verifier-input.json').read_text());inv=json.loads((r/'hash-inventory.json').read_text());core=json.loads((r/'bindings/core.json').read_text());rows=inv['entries']
 for e in rows:
  p=r/e['path']
  if not p.is_file() or p.is_symlink() or p.stat().st_size!=e['bytes'] or h(p)!=e['sha256']:errors.append('inventory:'+e['path'])
 iid=hashlib.sha256(canon(rows)).hexdigest();c=dict(core);cd=c.pop('core_digest');calc=hashlib.sha256(canon(c)).hexdigest();full=hashlib.sha256(canon({'core_digest':cd,'inventory_digest':iid})).hexdigest()
 if iid!=inv['inventory_digest'] or iid!=m['inventory_digest']:errors.append('inventory_digest')
 if calc!=cd or cd!=m['core_candidate_digest'] or cd!=v['expected_core_candidate_digest']:errors.append('core_digest')
 if full!=m['full_candidate_digest'] or full!=s['full_candidate_digest'] or full!=v['expected_full_candidate_digest']:errors.append('full_digest')
 p=Path(v['predecessor_root']);pm=json.loads((p/'candidate-manifest.json').read_text())
 if pm['full_candidate_digest']!=core['predecessor_full_digest'] or h(p/'candidate-manifest.json')!=core['predecessor_manifest_sha256'] or h(p/'hash-inventory.json')!=core['predecessor_inventory_sha256'] or h(Path(v['predecessor_wheel']))!=core['predecessor_wheel_sha256']:errors.append('predecessor')
 if h(Path(v['profile_wheel']))!='f75856d6d986b64d3d2f083aec2f865c7aea19f5950b84ff06e519f2f6505af6':errors.append('profile_wheel')
 if h(Path(v['polling_recovery_seal']))!='00d44de03e8dde668da8058091751291b13457c8bd086917dfe85e42b3813df8' or h(Path(v['observer_v63_seal']))!='2833428cfab41826a5cb54f90bb51c56f40b2468850d02687f7341c791b7f803':errors.append('prior_evidence')
 if [x['path'] for x in core['source_delta']]!=['gateway/platforms/nutrition_coaching.py','tests/gateway/test_nutrition_coaching.py']:errors.append('delta')
 wd=json.loads((r/'artifacts/wheel-delta.json').read_text())
 if wd['status']!='PASS' or [x['path'] for x in wd['changed']]!=['gateway/platforms/nutrition_coaching.py']:errors.append('wheel_delta')
 q=core['quality']
 if q['red']['failed']!=1 or q['green']['passed']!=1 or q['related']['passed']!=335 or q['ruff']!='PASS' or not q['wheel_reproducible']:errors.append('quality')
 prod=(r/'snapshot/after/gateway/platforms/nutrition_coaching.py').read_text();before=(r/'snapshot/before/gateway/platforms/nutrition_coaching.py').read_text()
 if prod.count('self._service_state_store.ensure()')!=1 or 'self._service_state_store.ensure()' in before:errors.append('production_fix')
except Exception as e:errors.append(type(e).__name__+':'+str(e))
out={'schema':'task26-customer-service-state-bootstrap-independent-verification-v1','status':'PASS' if not errors else 'FAIL','full_candidate_digest':m.get('full_candidate_digest') if 'm' in locals() else None,'inventory_entries':len(rows) if 'rows' in locals() else 0,'errors':errors};print(json.dumps(out,sort_keys=True,separators=(',',':')));raise SystemExit(bool(errors))
