from pathlib import Path
import requests,time,json,datetime as dt,fcntl
R=Path(__file__).resolve().parent
def utc():return dt.datetime.now(dt.timezone.utc).isoformat()
def dump(p,d):
    p.parent.mkdir(parents=True,exist_ok=True);tmp=p.with_suffix(p.suffix+'.tmp');tmp.write_text(json.dumps(d,ensure_ascii=False));tmp.replace(p)
class Client:
    def __init__(self):
        self.lock=(R/'collection.lock').open('w');fcntl.flock(self.lock,fcntl.LOCK_EX|fcntl.LOCK_NB)
        self.session=requests.Session();self.session.headers['User-Agent']='SteamCatalogResearch/0.6 (bounded late-update evidence)'
    def get(self,url,params,kind):
        policy=json.loads((R/'collection_policy.json').read_text());assert policy['active'] and not (R/'STOP').exists()
        log=R/'requests.jsonl';old=[json.loads(s) for s in log.read_text().splitlines()] if log.exists() else []
        assert len(old)<policy['max_requests']
        if old:time.sleep(max(0,policy['min_interval_seconds']-(time.time()-old[-1]['epoch'])))
        row={'sequence':len(old)+1,'at':utc(),'epoch':time.time(),'url':url,'params':params,'kind':kind}
        try:
            r=self.session.get(url,params=params,timeout=(10,35),stream=True);row['status']=r.status_code;r.raise_for_status();size=0;chunks=[]
            for chunk in r.iter_content(65536):
                size+=len(chunk)
                if size>policy['max_response_bytes']:raise RuntimeError('Response size cap')
                chunks.append(chunk)
            row['bytes']=size;return b''.join(chunks)
        except Exception as e:
            row['error']=str(e);policy['active']=False;policy['stop_reason']=str(e);dump(R/'collection_policy.json',policy);raise
        finally:
            row['finished_at']=utc()
            with log.open('a') as f:f.write(json.dumps(row)+'\n')
    def news(self,appid):
        p=R/'news'/f'{appid}.json'
        if p.exists():return json.loads(p.read_text())
        params={'appid':int(appid),'count':200,'maxlength':0,'feeds':'steam_community_announcements'}
        raw=self.get('https://api.steampowered.com/ISteamNews/GetNewsForApp/v2/',params,'official_news');d=json.loads(raw);assert d.get('appnews',{}).get('appid')==int(appid)
        dump(p,{'appid':int(appid),'retrieved_at':utc(),'params':params,'response':d});return json.loads(p.read_text())
