import IndieAbout
import SwiftData
import SwiftUI

@main
struct NotesApp: App {
    #if os(iOS)
    @UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
    #endif
    @State private var appServices: AppServices?
    @State private var pendingBackupURL: URL?
    @Environment(\.scenePhase) private var scenePhase

    var body: some Scene {
        WindowGroup {
            Group {
                if let appServices {
                    ContentView()
                        .modelContainer(appServices.modelContainer)
                        .environment(appServices.syncEngine)
                        .environment(appServices.contextService)
                        .environment(appServices.audioRecorder)
                        .environment(appServices.transcriptionSettings)
                        .environment(appServices.transcriptionService)
                        .environmentObject(appServices.backupController)
                } else {
                    ProgressView()
                }
            }
            .task {
                if appServices == nil {
                    let services = await AppServices()
                    appServices = services
                    #if os(iOS)
                    // Hand the (already-activated) watch receiver the live sync
                    // engine so a delivery arriving now drains immediately, and
                    // route ingest acks back so the watch can free its copies.
                    let receiver = appDelegate.watchReceiver
                    receiver.syncEngine = services.syncEngine
                    services.syncEngine.onWatchRecordingHandled = { [weak receiver] id in
                        receiver?.acknowledgeIngest(id: id)
                    }
                    #endif
                    await services.startDeferredServices()
                    if let pending = pendingBackupURL {
                        pendingBackupURL = nil
                        restore(pending, with: services)
                    }
                }
            }
            .onOpenURL { url in
                handle(url: url)
            }
            .onChange(of: scenePhase) { _, phase in
                guard phase == .active, let appServices else { return }
                let syncEngine = appServices.syncEngine
                let contextService = appServices.contextService
                Task {
                    // Fresh context whenever the user comes back to the app —
                    // recall ranking should reflect where they are right now.
                    await contextService.refresh()
                }
                Task {
                    switch syncEngine.iCloudStatus {
                    case .unavailable:
                        // The user typically fixes iCloud in Settings and
                        // switches straight back — retry on foreground.
                        await syncEngine.connect()
                    case .available:
                        // Pull in records that synced down (and drop ones
                        // deleted elsewhere) while the app was backgrounded.
                        // Skip during a restore — it stops the monitors and
                        // rebuilds the cache itself; a reconcile racing the
                        // file swap would import a half-restored state.
                        if !appServices.backupController.isRestoring {
                            await syncEngine.reconcile()
                            // Ingest any watch recordings staged since we last
                            // looked (delivered while backgrounded).
                            await syncEngine.ingestStagedWatchRecordings()
                        }
                    case .checking:
                        break
                    }
                }
            }
        }
        #if os(macOS)
        .defaultSize(width: 900, height: 700)
        .commands {
            IndieAboutCommand(configuration: .init(
                documents: [
                    .license(filename: "LICENSE", extension: "md"),
                    .custom(title: "Changelog", filename: "CHANGELOG", extension: "md")
                ]
            ))
        }
        #endif
    }

    /// A tapped `.notesbackup` file (Files, AirDrop, Mail) restores the
    /// backup. If it arrives before services finish launching, it's queued.
    private func handle(url: URL) {
        guard url.isFileURL, url.pathExtension == "notesbackup" else { return }
        if let appServices {
            restore(url, with: appServices)
        } else {
            pendingBackupURL = url
        }
    }

    private func restore(_ url: URL, with services: AppServices) {
        let didScope = url.startAccessingSecurityScopedResource()
        Task {
            defer { if didScope { url.stopAccessingSecurityScopedResource() } }
            try? await services.backupController.restoreBackup(from: url)
        }
    }
}
