//
//  AppBackupConfiguration.swift
//  Workouts
//
//  Copyright 2025 Rouslan Zenetl. All Rights Reserved.
//

import Foundation
import IndieBackup

/// Tells IndieBackup what to snapshot and how to bring the app back to a
/// consistent state after a restore.
///
/// `backupRoot` is the ubiquity container's `Documents/` directory — the exact
/// tree `SyncEngine`'s `DocumentFileStore` writes to: `Splits/`, `Workouts/`,
/// and the `Stubs/` soft-delete tombstones. Backing up that whole tree,
/// tombstones included, is deliberate — a restore that dropped the stubs could
/// resurrect records the user had already deleted. The SwiftData store is never
/// in the backup: it's a rebuildable cache, regenerated from the restored files
/// by `rebuildCacheAfterRestore`.
///
/// This type is nonisolated on purpose — the package reads `backupRoot`
/// synchronously off the main actor during its detached directory mirror. It
/// holds the `@MainActor` `SyncEngine`, which is safe because a global-actor
/// class is implicitly `Sendable`, and the three restore hooks only ever reach
/// it via `await`.
final class AppBackupConfiguration: BackupConfiguration {
    private let syncEngine: SyncEngine

    init(syncEngine: SyncEngine) {
        self.syncEngine = syncEngine
    }

    var backupFileExtension: String { "workoutsbackup" }
    var backupDisplayName: String { "Workouts Backup" }

    /// The container's `Documents/` folder. Resolved directly from `FileManager`
    /// with the app's explicit ubiquity container id so this stays synchronous and
    /// nonisolated; it yields the identical URL `SyncEngine` exposes as
    /// `containerDocumentsURL` once connected. Backup and restore are only
    /// reachable past the iCloud gate, so the container is always resolved by the
    /// time this is read — the package's local `documentsURL` fallback is only a
    /// belt-and-suspenders never-nil guarantee.
    var backupRoot: URL {
        if let container = FileManager.default.url(
            forUbiquityContainerIdentifier: SyncEngine.containerIdentifier
        ) {
            let docs = container.appendingPathComponent("Documents", isDirectory: true)
            try? FileManager.default.createDirectory(at: docs, withIntermediateDirectories: true)
            return docs
        }
        return iCloudDocumentManager.shared.documentsURL
    }

    /// After the restore has replaced the document tree on disk, rebuild the
    /// SwiftData cache from those files.
    func rebuildCacheAfterRestore(progress: @escaping (Double, String) -> Void) async throws {
        progress(0.0, "Rebuilding your library…")
        await syncEngine.rebuildCache()
        progress(1.0, "Library rebuilt")
    }

    /// Stop the metadata observer before the restore touches disk, so the bulk
    /// file mirror isn't observed as a storm of live edits.
    func prepareForRestore() async {
        await syncEngine.beginRestore()
    }

    /// Resume the metadata observer (fresh, re-baselined) after the restore.
    func finishRestore() async {
        await syncEngine.endRestore()
    }
}
