import json, datetime, urllib.request, urllib.parse, urllib.error
from pathlib import Path
from email.utils import parsedate_to_datetime
from datetime import timezone, timedelta

TOKEN_PATH = Path.home() / '.hermes' / 'google_token.json'
REAUTH = '⚠️ Morning digest unavailable — Google OAuth token needs re-auth. Run: python3 /usr/local/lib/hermes-agent/setup.py --auth-url'

def load(): return json.loads(TOKEN_PATH.read_text())
def save(t):
    TOKEN_PATH.write_text(json.dumps(t, indent=2)); TOKEN_PATH.chmod(0o600)
def api(url, tok):
    req=urllib.request.Request(url, headers={'Authorization': 'Bearer '+tok})
    try:
        with urllib.request.urlopen(req, timeout=20) as r: return r.status, json.loads(r.read().decode())
    except urllib.error.HTTPError as e: return e.code, e.read().decode()[:500]
def refresh(t):
    form=urllib.parse.urlencode({'client_id':t['client_id'],'client_secret':t['client_secret'],'refresh_token':t['refresh_token'],'grant_type':'refresh_token'}).encode()
    req=urllib.request.Request('https://oauth2.googleapis.com/token',data=form,headers={'Content-Type':'application/x-www-form-urlencoded'},method='POST')
    try:
        with urllib.request.urlopen(req,timeout=20) as r: b=json.loads(r.read().decode())
    except Exception: return None
    if 'access_token' not in b: return None
    n=dict(t); n['access_token']=b['access_token']; n['token']=b['access_token']; n['expiry']=datetime.datetime.now(timezone.utc).timestamp()+b.get('expires_in',3600)
    if b.get('scope'): n['scopes']=b['scope'].split()
    save(n); return n

def call(url,t):
    tok=t.get('access_token') or t.get('token'); s,d=api(url,tok)
    if s==401:
        t=refresh(t)
        if not t: return None,None,False
        s,d=api(url,t['access_token'])
    return s,d,True

def syd(ts):
    try:
        d=datetime.datetime.fromtimestamp(int(ts)/1000,timezone.utc).astimezone(datetime.timezone(datetime.timedelta(hours=10)))
        return d.strftime('%H:%M')
    except: return ''
def hdr(d,n): return next((h['value'] for h in d.get('payload',{}).get('headers',[]) if h['name'].lower()==n.lower()),'')

t=load()
now=datetime.datetime.now(timezone.utc); end=now+timedelta(days=7)
gurl='https://gmail.googleapis.com/gmail/v1/users/me/messages?q=is%3Aunread%20in%3Ainbox&maxResults=20'
curl='https://www.googleapis.com/calendar/v3/calendars/primary/events?timeMin='+urllib.parse.quote(now.strftime('%Y-%m-%dT%H:%M:%SZ'))+'&timeMax='+urllib.parse.quote(end.strftime('%Y-%m-%dT%H:%M:%SZ'))+'&singleEvents=true&orderBy=startTime&maxResults=30'
gs,gd,ok=call(gurl,t)
if not ok: print(REAUTH); raise SystemExit
ids=[x['id'] for x in gd.get('messages',[])]
msgs=[]
for mid in ids:
    s,d,ok=call('https://gmail.googleapis.com/gmail/v1/users/me/messages/'+mid+'?format=full',t)
    if not ok: print(REAUTH); raise SystemExit
    labels=d.get('labelIds',[])
    if 'CATEGORY_PROMOTIONS' in labels or 'CATEGORY_SOCIAL' in labels: continue
    msgs.append((int(d.get('internalDate','0')),hdr(d,'From'),hdr(d,'Subject'),d.get('snippet','').replace('\n',' ')))
msgs.sort(reverse=True)
cs,cd,cok=call(curl,t)
if not cok: cal=None
else: cal=cd.get('items',[])
local=now.astimezone(datetime.timezone(timedelta(hours=10)))
print(json.dumps({'date':local.strftime('%A, %d %b'),'emails':msgs[:5],'calendar':cal},ensure_ascii=False))
