#!/usr/bin/env python3
import json, os, sys, urllib.request, urllib.error
from datetime import datetime, date, time, timedelta
from zoneinfo import ZoneInfo
FIT_URL='https://www.googleapis.com/fitness/v1/users/me/dataset:aggregate'; INGEST_URL='http://100.87.116.90:3007/ingest'; HEALTH_TOKEN='XXMYWq-ft146fRL0AlNV_5P9Vg4NNJJa4pHxqDvzRkg'; SUBJECT='chicho'; TZ='America/Argentina/Buenos_Aires'; SOURCE='google_fit_rest'
AGG=[{'dataSourceId':'derived:com.google.step_count.delta:com.google.android.gms:estimated_steps'},{'dataTypeName':'com.google.calories.expended'},{'dataTypeName':'com.google.distance.delta'}]
def ms(d,z): return int(datetime.combine(d,time.min,tzinfo=z).timestamp()*1000)
def iso(ms,z=ZoneInfo('UTC')): return datetime.fromtimestamp(int(ms)/1000,z).isoformat().replace('+00:00','Z')
def req(url,body,headers):
 r=urllib.request.Request(url,data=json.dumps(body).encode(),headers=headers,method='POST')
 with urllib.request.urlopen(r,timeout=60) as resp: return json.loads(resp.read().decode())
def num(v): return float(v.get('fpVal') if v.get('fpVal') is not None else (v.get('intVal') or 0))
def main():
 token=os.environ['GOOGLE_FIT_ACCESS_TOKEN']; z=ZoneInfo(TZ); start=date.fromisoformat(os.environ.get('GOOGLE_FIT_START','2015-01-01')); end=date.fromisoformat(os.environ.get('GOOGLE_FIT_END',datetime.now(z).date().isoformat()))
 days={}; chunks=0; cur=start
 while cur<=end:
  ce=min(cur+timedelta(days=29),end); body={'aggregateBy':AGG,'bucketByTime':{'period':{'type':'day','value':1,'timeZoneId':TZ}},'startTimeMillis':ms(cur,z),'endTimeMillis':ms(ce+timedelta(days=1),z)}
  fit=req(FIT_URL,body,{'Authorization':f'Bearer {token}','Content-Type':'application/json;encoding=utf-8'}); chunks+=1
  for b in fit.get('bucket',[]):
   d=datetime.fromtimestamp(int(b['startTimeMillis'])/1000,z).date().isoformat(); rec=days.setdefault(d,{'type':'daily_aggregate','subject':SUBJECT,'source':SOURCE,'local_date':d,'period_start':iso(b['startTimeMillis']),'period_end':iso(b['endTimeMillis']),'record_key':f'daily_aggregate:{SUBJECT}:{d}','steps':0,'total_kcal':0.0,'distance_m':0.0})
   for ds in b.get('dataset',[]):
    for p in ds.get('point',[]):
     dtype=p.get('dataTypeName',''); vals=p.get('value',[])
     if dtype=='com.google.step_count.delta': rec['steps']+=sum(int(v.get('intVal') or 0) for v in vals)
     elif dtype=='com.google.calories.expended': rec['total_kcal']+=sum(num(v) for v in vals)
     elif dtype=='com.google.distance.delta': rec['distance_m']+=sum(num(v) for v in vals)
  cur=ce+timedelta(days=1)
 records=list(days.values()); ingest=req(INGEST_URL,{'subject':SUBJECT,'timezone':TZ,'records':records},{'Authorization':f'Bearer {HEALTH_TOKEN}','Content-Type':'application/json'})
 print(json.dumps({'status':'ok','chunks':chunks,'records':len(records),'days_with_steps':sum(1 for r in records if r['steps']>0),'days_with_distance':sum(1 for r in records if r['distance_m']>0),'step_sum':sum(r['steps'] for r in records),'distance_m_sum':round(sum(r['distance_m'] for r in records),2),'ingest':ingest},indent=2))
if __name__=='__main__': main()
