package no.naiv.meddetsamme.backup import android.content.Context import java.time.Instant import java.time.ZoneOffset import java.time.format.DateTimeFormatter import no.naiv.meddetsamme.data.MedDatabase import no.naiv.meddetsamme.settings.SettingsStore /** * The one entry point for export, import and auto-backup — all three share * [BackupSerializer] and [BackupCrypto] by construction (brief decision #6). */ class BackupManager(private val context: Context) { private val db get() = MedDatabase.get(context) /** Plaintext JSON export (caller decides whether/how to encrypt). */ suspend fun exportJson(): String { val dao = db.backupDao() return BackupSerializer.export(dao.allMedications(), dao.allDoseTimes(), dao.allDoseLogs()) } /** Export encrypted with a *typed* passphrase (never stored — manual path). */ suspend fun exportEncrypted(passphrase: CharArray): ByteArray = BackupCrypto.all().first().encrypt(exportJson().toByteArray(Charsets.UTF_8), passphrase) /** * Import from file content: encrypted (format auto-detected) or plain JSON. * Replace-all; the caller MUST re-arm alarms afterwards. */ suspend fun import(data: ByteArray, passphrase: CharArray?): Int { val crypto = BackupCrypto.detect(data) val json = when { crypto != null -> { requireNotNull(passphrase) { "Denne filen er kryptert — passordfrase kreves" } crypto.decrypt(data, passphrase).toString(Charsets.UTF_8) } else -> data.toString(Charsets.UTF_8) } val file = BackupSerializer.parse(json) BackupSerializer.restore(db, file) return file.medications.size } /** * Unattended backup → S3. Encrypts with the STORED passphrase if one is * configured (protects against bucket compromise only — see SettingsStore), * otherwise uploads plaintext JSON by explicit user choice. * Distinct timestamped objects; retention is the bucket's lifecycle rule's * problem, not ours. */ suspend fun runAutoBackup(): String { val settings = SettingsStore(context) val s3 = checkNotNull(settings.s3Client()) { "Backup er ikke konfigurert" } val json = exportJson().toByteArray(Charsets.UTF_8) val passphrase = settings.autoBackupPassphrase val (body, suffix) = if (passphrase.isNullOrEmpty()) { json to "json" } else { val crypto = BackupCrypto.all().first() crypto.encrypt(json, passphrase.toCharArray()) to "json.${crypto.formatId}" } val stamp = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'") .withZone(ZoneOffset.UTC).format(Instant.now()) val key = "meddetsamme/backup-$stamp.$suffix" s3.put(key, body) return key } }