"""Complete the three already-authorized free-game contexts with all acquisition types.

The Steam-purchase filter is not an audience census for a free game. Keep this
supplement separate and use the SAME 250-request cap as the main collection.
"""
from pathlib import Path
import json,time,datetime as dt
import pandas as pd
import requests
R=Path(__file__).resolve().parent;pages=R/'free_context_pages';pages.mkdir(exist_ok=True)
ledger=R/'requests.jsonl';used=len(ledger.read_text().splitlines());rows=[];status=[];last=0.0
t=pd.read_parquet(R/'targets.parquet').set_index('appid')
policy={'targets':[769970,1673600,1746370],'purchase_type':'all','shared_total_request_cap':250,'active':True,'reason':'Free games may have reviews outside the Steam-purchase channel; avoid treating a zero filtered count as no reviews.'}
(R/'free_context_policy.json').write_text(json.dumps(policy,indent=2))
try:
 for appid in policy['targets']:
  cursor='*';page=0;seen=set();rr=[]
  while True:
   dest=pages/f'{appid}_{page:03}.json'
   if dest.exists():data=json.loads(dest.read_text());assert data['requested_cursor']==cursor
   else:
    assert used<250,'Shared request cap reached'
    time.sleep(max(0,2-(time.monotonic()-last)));last=time.monotonic()
    params=dict(json=1,filter='recent',language='all',purchase_type='all',review_type='all',num_per_page=100,filter_offtopic_activity=1,cursor=cursor)
    at=dt.datetime.now(dt.timezone.utc).isoformat()
    with ledger.open('a') as f:f.write(json.dumps(dict(at=at,appid=appid,page=page,scope='free_context',params=params))+'\n')
    used+=1
    resp=requests.get(f'https://store.steampowered.com/appreviews/{appid}',params=params,timeout=(10,30),headers={'User-Agent':'SteamCatalogResearch/0.2 (bounded public review study)'})
    resp.raise_for_status();assert len(resp.content)<2000000
    raw=resp.json();assert raw.get('success')==1
    safe=[{k:r.get(k) for k in ['recommendationid','language','review','voted_up','timestamp_created','timestamp_updated','steam_purchase','received_for_free','written_during_early_access']} for r in raw.get('reviews',[])]
    data=dict(at=at,requested_cursor=cursor,next_cursor=raw.get('cursor'),query_summary=raw.get('query_summary'),reviews=safe,url=resp.url)
    dest.write_text(json.dumps(data,ensure_ascii=False))
   if page==0:summary=data['query_summary']
   batch=data['reviews'];new=[r for r in batch if r['recommendationid'] not in seen]
   for r in new:seen.add(r['recommendationid']);rr.append(dict(appid=appid,game=t.loc[appid,'name'],developer='Desert Fox',**r))
   if not batch:break
   assert new and data['next_cursor']!=cursor
   cursor=data['next_cursor'];page+=1
  rows.extend(rr);status.append(dict(appid=appid,collected=len(rr),summary_total=summary['total_reviews']))
  print(t.loc[appid,'name'],len(rr),'all-acquisition reviews; shared requests',used,flush=True)
 pd.DataFrame(rows).to_parquet(R/'free_context_reviews.parquet',index=False)
 (R/'free_context_status.json').write_text(json.dumps(status,indent=2))
finally:
 policy['active']=False;policy['shared_requests_used']=used
 (R/'free_context_policy.json').write_text(json.dumps(policy,indent=2))
