#!/usr/bin/env python3
"""Manual OAuth helper for Google Fit REST.

Uses existing Desktop OAuth client at ~/.config/gogcli/client_secret.json.
Commands:
  auth-url   -> prints URL to authorize fitness.activity.read
  exchange URL_OR_CODE -> exchanges code, saves token JSON, prints access token preview
  token      -> refreshes/prints current access token
"""
import json, os, sys, secrets, urllib.parse, urllib.request, time
from pathlib import Path

CLIENT_FILE = Path(os.environ.get('GOOGLE_CLIENT_SECRET_FILE', '/home/ubuntu/.config/gog/credentials.json'))
FALLBACK_CLIENT_FILE = Path('/home/ubuntu/.config/gogcli/client_secret.json')
STATE_FILE = Path('/home/ubuntu/health-bridge/.google_fit_oauth_state.json')
TOKEN_FILE = Path('/home/ubuntu/health-bridge/google_fit_token.json')
SCOPE = os.environ.get('GOOGLE_FIT_SCOPES', 'https://www.googleapis.com/auth/fitness.activity.read')
REDIRECT_URI = 'http://localhost'


def client():
    data = json.loads(CLIENT_FILE.read_text())
    if 'installed' in data:
        data = data['installed']
    client_id = data['client_id']
    client_secret = data.get('client_secret')
    token_uri = data.get('token_uri', 'https://oauth2.googleapis.com/token')
    auth_uri = data.get('auth_uri', 'https://accounts.google.com/o/oauth2/auth')
    if not client_secret and FALLBACK_CLIENT_FILE.exists():
        fb = json.loads(FALLBACK_CLIENT_FILE.read_text()).get('installed', {})
        if fb.get('client_id') == client_id:
            client_secret = fb.get('client_secret')
    return client_id, client_secret, token_uri, auth_uri


def post(url, data):
    enc = urllib.parse.urlencode(data).encode()
    req = urllib.request.Request(url, data=enc, headers={'Content-Type': 'application/x-www-form-urlencoded'})
    with urllib.request.urlopen(req, timeout=60) as r:
        return json.loads(r.read().decode())


def auth_url():
    client_id, _, _, auth_uri = client()
    state = secrets.token_urlsafe(24)
    STATE_FILE.write_text(json.dumps({'state': state, 'created_at': time.time()}))
    q = urllib.parse.urlencode({
        'client_id': client_id,
        'redirect_uri': REDIRECT_URI,
        'response_type': 'code',
        'scope': SCOPE,
        'access_type': 'offline',
        'prompt': 'consent',
        'state': state,
    })
    print(f'{auth_uri}?{q}')


def extract_code(arg):
    if arg.startswith('http://') or arg.startswith('https://'):
        u = urllib.parse.urlparse(arg)
        qs = urllib.parse.parse_qs(u.query)
        if 'error' in qs:
            raise SystemExit('OAuth error: ' + qs['error'][0])
        return qs['code'][0]
    return arg


def exchange(arg):
    code = extract_code(arg)
    client_id, client_secret, token_uri, _ = client()
    tok = post(token_uri, {
        'client_id': client_id,
        'client_secret': client_secret,
        'code': code,
        'redirect_uri': REDIRECT_URI,
        'grant_type': 'authorization_code',
    })
    tok['obtained_at'] = int(time.time())
    TOKEN_FILE.write_text(json.dumps(tok, indent=2))
    print(json.dumps({'saved': str(TOKEN_FILE), 'scope': tok.get('scope'), 'has_refresh_token': bool(tok.get('refresh_token')), 'access_token_prefix': tok.get('access_token','')[:12]}, indent=2))


def get_token():
    tok = json.loads(TOKEN_FILE.read_text())
    exp = tok.get('obtained_at', 0) + tok.get('expires_in', 0) - 120
    if time.time() >= exp:
        client_id, client_secret, token_uri, _ = client()
        new = post(token_uri, {
            'client_id': client_id,
            'client_secret': client_secret,
            'refresh_token': tok['refresh_token'],
            'grant_type': 'refresh_token',
        })
        tok.update(new)
        tok['obtained_at'] = int(time.time())
        TOKEN_FILE.write_text(json.dumps(tok, indent=2))
    print(tok['access_token'])


def main():
    if len(sys.argv) < 2 or sys.argv[1] not in {'auth-url','exchange','token'}:
        print('usage: google_fit_oauth.py auth-url | exchange URL_OR_CODE | token', file=sys.stderr)
        return 2
    if sys.argv[1] == 'auth-url': auth_url()
    elif sys.argv[1] == 'exchange':
        if len(sys.argv) < 3: raise SystemExit('missing URL_OR_CODE')
        exchange(sys.argv[2])
    else: get_token()

if __name__ == '__main__':
    raise SystemExit(main())
