#!/usr/bin/env python3
"""Copia consistente de todos los datos: bases SQLite (sistema, obra principal y proyectos) y adjuntos.

Las bases se copian con la API de respaldo de SQLite, sin detener el servidor. Pensado también para un cron del hosting:
  python scripts/backup.py --dir ~/obra_respaldos --keep 14
"""
import argparse, os, sqlite3, tempfile, zipfile
from datetime import datetime
from pathlib import Path

p=argparse.ArgumentParser(description=__doc__,formatter_class=argparse.RawDescriptionHelpFormatter)
p.add_argument('--data',type=Path,default=Path(os.getenv('TWIN_DATA',Path(__file__).resolve().parents[1]/'data')))
dest=p.add_mutually_exclusive_group(required=True)
dest.add_argument('--out',type=Path,help='archivo .zip de destino')
dest.add_argument('--dir',type=Path,help='carpeta: crea respaldo-AAAAMMDD-HHMMSS.zip')
p.add_argument('--keep',type=int,default=0,help='con --dir, conservar solo los N respaldos más recientes')
a=p.parse_args()
if not (a.data/'twin.sqlite').exists(): raise SystemExit(f'No existe {a.data/"twin.sqlite"}')
out=a.out or a.dir/f'respaldo-{datetime.now():%Y%m%d-%H%M%S}.zip'
if out.exists(): raise SystemExit('El destino ya existe; usá otro nombre')
out.parent.mkdir(parents=True,exist_ok=True)

# Bases vigentes: sistema, obra principal y una por proyecto. Se omiten las copias «-respaldo-» y «.bak».
bases=[f for f in [a.data/'system.sqlite',a.data/'twin.sqlite',*sorted(a.data.glob('projects/*/twin.sqlite'))] if f.exists()]
with tempfile.TemporaryDirectory() as temp, zipfile.ZipFile(out,'x',compression=zipfile.ZIP_DEFLATED) as z:
    for i,f in enumerate(bases):
        snap=Path(temp)/f'{i}.sqlite'
        with sqlite3.connect(f) as source,sqlite3.connect(snap) as copy: source.backup(copy)
        z.write(snap,'data/'+f.relative_to(a.data).as_posix())
    for folder in [a.data/'uploads',*sorted(a.data.glob('projects/*/uploads'))]:
        for f in folder.glob('*'):
            if f.is_file(): z.write(f,'data/'+f.relative_to(a.data).as_posix())
if a.dir and a.keep>0:
    for old in sorted(a.dir.glob('respaldo-*.zip'))[:-a.keep]: old.unlink()
print(out.resolve())
