"""Freeze step (2 September 2026, after gate 2): adds SHA-256 and byte size to every saved capture, and attaches to every discovery
row the raw DataForSEO SERP files that its query produced (matched by the query slug used in the file name), each with SHA-256 and
mtime. Idempotent; never refetches. Usage: python3 scripts/freeze.py cohort1 2026-09-02"""
import json,re,os,glob,hashlib,sys,datetime
cohort,date=sys.argv[1],sys.argv[2]
def sha(fn): return hashlib.sha256(open(fn,'rb').read()).hexdigest()
pf=f'data/profiles-{cohort}-{date}.json'; d=json.load(open(pf)); rows=d['rows'] if isinstance(d,dict) else d
for r in rows:
    q=r.get('query') or ''
    slug=re.sub(r'[^a-z0-9]+','-',q.lower()).strip('-')
    files=sorted(glob.glob(f'raw_responses/dataforseo/serp-{slug}-*.json'),key=os.path.getmtime) if slug else []  # latest by mtime is the file used
    r['serp_files']=[{'file':f,'sha256':sha(f),'mtime_utc':datetime.datetime.fromtimestamp(os.path.getmtime(f),datetime.UTC).strftime('%Y-%m-%dT%H:%M:%SZ')} for f in files]
    r['serp_file_used']=(files[-1] if files else None)
json.dump(d,open(pf,'w'),indent=1)
cf=f'data/capture-{cohort}-{date}.json'; c=json.load(open(cf)); crows=c['rows'] if isinstance(c,dict) else c
n=0
for r in crows:
    if r.get('saved') and os.path.exists(r['saved']): r['sha256']=sha(r['saved']); r['bytes']=os.path.getsize(r['saved']); n+=1
json.dump(c,open(cf,'w'),indent=1)
print(f'freeze: {sum(1 for r in rows if r["serp_files"])} discovery rows linked to SERP files; {n} captures hashed')
