#!/usr/bin/env python3
"""
ADSB Weekly Backup
Generates a full CSV and uploads to Google Drive using requests library.
Overwrites previous backup — only one file ever exists.
"""

import os, json, csv, io, time, base64, subprocess, tempfile, requests
from datetime import datetime

DATA_DIR    = '/home/pi/adsb-analyzer/data'
CREDS_FILE  = '/home/pi/adsb-analyzer/adsb-backup-2fa1c8fbaa7e.json'
FOLDER_ID   = '1sGdc47U9xhiqLBo-3PB0MGqBKTLWd7Sk'
BACKUP_NAME = 'adsb_backup.csv'
LOG_FILE    = '/home/pi/adsb-analyzer/adsb.log'

COLS = ['flight','hex','category','make','model','owner',
        'alt_baro','gs','r_dst','visits','total_sightings','first_seen','last_seen']

def log(msg):
    ts = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    line = f'[{ts}] [backup] {msg}'
    print(line)
    try:
        with open(LOG_FILE, 'a') as f: f.write(line + '\n')
    except: pass

def load_all_data():
    merged = {}
    files = sorted([f for f in os.listdir(DATA_DIR) if f.endswith('.json')])
    log(f'Loading {len(files)} daily log files...')
    for fn in files:
        try:
            with open(os.path.join(DATA_DIR, fn)) as f:
                day = json.load(f)
            for hex_id, rec in day.items():
                if hex_id not in merged:
                    merged[hex_id] = dict(rec)
                else:
                    merged[hex_id]['total_sightings'] = merged[hex_id].get('total_sightings',0) + rec.get('total_sightings',0)
                    merged[hex_id]['visits'] = merged[hex_id].get('visits',0) + rec.get('visits',0)
                    if rec.get('first_seen','') < merged[hex_id].get('first_seen',''): merged[hex_id]['first_seen'] = rec['first_seen']
                    if rec.get('last_seen','') > merged[hex_id].get('last_seen',''):
                        merged[hex_id]['last_seen'] = rec['last_seen']
                        for field in ['alt_baro','gs','r_dst','category','flight','make','model','owner']:
                            if field in rec: merged[hex_id][field] = rec[field]
        except Exception as e:
            log(f'Error reading {fn}: {e}')
    log(f'Merged {len(merged):,} unique aircraft')
    return merged

def generate_csv(data):
    out = io.StringIO()
    w = csv.writer(out)
    w.writerow(COLS)
    for rec in data.values():
        w.writerow([rec.get(c,'') for c in COLS])
    return out.getvalue().encode('utf-8')

def get_token(creds):
    now = int(time.time())
    header  = base64.urlsafe_b64encode(json.dumps({'alg':'RS256','typ':'JWT'}).encode()).rstrip(b'=')
    payload = base64.urlsafe_b64encode(json.dumps({
        'iss': creds['client_email'],
        'scope': 'https://www.googleapis.com/auth/drive',
        'aud': 'https://oauth2.googleapis.com/token',
        'iat': now, 'exp': now + 3600
    }).encode()).rstrip(b'=')
    signing_input = header + b'.' + payload
    with tempfile.NamedTemporaryFile(mode='w', suffix='.pem', delete=False) as kf:
        kf.write(creds['private_key']); key_path = kf.name
    try:
        result = subprocess.run(['openssl','dgst','-sha256','-sign',key_path], input=signing_input, capture_output=True)
        sig = base64.urlsafe_b64encode(result.stdout).rstrip(b'=')
    finally:
        os.unlink(key_path)
    jwt = (signing_input + b'.' + sig).decode()
    r = requests.post('https://oauth2.googleapis.com/token', data={
        'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer',
        'assertion': jwt
    })
    r.raise_for_status()
    return r.json()['access_token']

def find_file(token):
    r = requests.get('https://www.googleapis.com/drive/v3/files', headers={'Authorization': f'Bearer {token}'}, params={
        'q': f"name='{BACKUP_NAME}' and '{FOLDER_ID}' in parents and trashed=false",
        'fields': 'files(id,name)'
    })
    r.raise_for_status()
    files = r.json().get('files', [])
    return files[0]['id'] if files else None

def upload(token, csv_bytes, existing_id=None):
    headers = {'Authorization': f'Bearer {token}'}
    metadata = {'name': BACKUP_NAME, 'mimeType': 'text/csv'}
    if not existing_id:
        metadata['parents'] = [FOLDER_ID]
    files = {
        'metadata': ('metadata', json.dumps(metadata), 'application/json'),
        'file': ('file', csv_bytes, 'text/csv')
    }
    if existing_id:
        r = requests.patch(f'https://www.googleapis.com/upload/drive/v3/files/{existing_id}?uploadType=multipart', headers=headers, files=files)
    else:
        r = requests.post('https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart', headers=headers, files=files)
    if not r.ok:
        log(f'Upload error {r.status_code}: {r.text}')
        r.raise_for_status()
    return r.json().get('id')

def main():
    log('=== Weekly backup starting ===')
    with open(CREDS_FILE) as f: creds = json.load(f)
    log(f'Credentials: {creds["client_email"]}')
    data = load_all_data()
    if not data: log('No data'); return
    csv_bytes = generate_csv(data)
    log(f'CSV: {len(data):,} records, {len(csv_bytes)/1024:.1f} KB')
    log('Authenticating...')
    token = get_token(creds)
    log('Authenticated')
    existing_id = find_file(token)
    if existing_id: log(f'Overwriting existing file: {existing_id}')
    else: log('Creating new file')
    file_id = upload(token, csv_bytes, existing_id)
    log(f'Done! File ID: {file_id}')
    log(f'Backed up {len(data):,} aircraft to Google Drive')
    log('=== Backup complete ===')

if __name__ == '__main__':
    main()
