#!/usr/bin/env python3
"""Opt-in local copy. Token in MESTO_ACH_TOKEN; never pass secrets on command line.

Run from the owner's own scheduler, e.g. every hour. No built-in background process.
python3 backup-client.py --directory /path/chosen/by/owner
Enable personal copies in Gardens first. The token expires and needs renewed login.
"""
import argparse
import hashlib
import io
import json
import os
import secrets
import tempfile
import urllib.request
import zipfile
from pathlib import Path

BASE='https://mesto.aination.center/live/experience'

def main():
    parser=argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--directory',required=True)
    args=parser.parse_args()
    token=os.environ.get('MESTO_ACH_TOKEN','')
    if not token:
        raise SystemExit('Set MESTO_ACH_TOKEN in the private process environment.')
    headers={'Authorization':'Bearer '+token}
    with urllib.request.urlopen(urllib.request.Request(BASE+'/me',headers=headers),timeout=20) as response:
        me=json.load(response)
    if not me.get('account') or not (me.get('preferences') or {}).get('backup_enabled'):
        raise SystemExit('Personal copying is not enabled for this authenticated owner.')
    with urllib.request.urlopen(urllib.request.Request(BASE+'/export',headers=headers),timeout=30) as response:
        raw=response.read(50_000_001);expected=response.headers.get('X-Archive-SHA256')
    if len(raw)>50_000_000 or hashlib.sha256(raw).hexdigest()!=expected:
        raise SystemExit('Archive size or checksum verification failed.')
    with zipfile.ZipFile(io.BytesIO(raw)) as archive:
        manifest=json.loads(archive.read('manifest.json'))
        for name,sha in manifest['files'].items():
            if hashlib.sha256(archive.read(name)).hexdigest()!=sha:
                raise SystemExit('Archive member verification failed.')
        data=json.loads(archive.read('experience.json'))
        if data['owner']!=me['account']:
            raise SystemExit('Owner mismatch.')
    dest=Path(args.directory).expanduser().resolve();dest.mkdir(parents=True,exist_ok=True,mode=0o700)
    name='experience-'+str(data['exported_at'])+'-'+expected[:12]+'.zip'
    path=dest/name
    fd,temp=tempfile.mkstemp(prefix='.experience-',dir=dest)
    try:
        with os.fdopen(fd,'wb') as f:f.write(raw);f.flush();os.fsync(f.fileno())
        os.replace(temp,path)
    finally:
        if os.path.exists(temp):os.unlink(temp)
    if hashlib.sha256(path.read_bytes()).hexdigest()!=expected:
        raise SystemExit('Destination readback failed.')
    receipt={'sha256':expected,'destination':'Выбранная владельцем папка'}
    req=urllib.request.Request(BASE+'/backup-receipt',data=json.dumps(receipt).encode(),headers={**headers,'Content-Type':'application/json','Idempotency-Key':'backup-'+secrets.token_hex(16)})
    with urllib.request.urlopen(req,timeout=20) as response:
        if not json.load(response).get('saved'):raise SystemExit('Receipt rejected; local copy is saved.')
    print(json.dumps({'path':str(path),'sha256':expected,'status':'SAVED_READ_BACK_RECEIPT_ACCEPTED'},ensure_ascii=False))

if __name__=='__main__':main()
