import Libbox
import Library
import SwiftUI
#if os(iOS)
    import FileProvider
    import UIKit
#elseif os(macOS)
    import ServiceManagement
#endif

@MainActor
public struct CoreView: View {
    @Environment(\.scenePhase) private var scenePhase
    @EnvironmentObject private var environments: ExtensionEnvironments
    @State private var isLoading = true
    @State private var alert: AlertState?

    @State private var disableDeprecatedWarnings = false

    @State private var version = ""
    @State private var dataSize: String?
    @State private var dataSizeLoaded = false

    #if os(macOS)
        @State private var helperUnavailable = false
    #endif

    public init() {}
    public var body: some View {
        Group {
            if isLoading {
                ProgressView().onAppear {
                    Task {
                        await loadSettings()
                    }
                }
            } else {
                FormView {
                    FormTextItem("Version", version)
                    if let dataSize {
                        FormTextItem("Data Size", dataSize)
                    } else if !dataSizeLoaded {
                        HStack {
                            Text("Data Size")
                            Spacer()
                            ProgressView()
                        }
                    } else {
                        #if os(macOS)
                            HStack {
                                Text("Data Size")
                                Spacer()
                                Text("Unavailable")
                                    .foregroundStyle(.red)
                                    .onTapGesture {
                                        alert = helperRequiredAlert()
                                    }
                            }
                        #endif
                    }

                    if Variant.isBeta {
                        FormToggle("Disable Deprecated Warnings", "Do not show warnings about usages of deprecated features.", $disableDeprecatedWarnings, header: "Beta Settings") {
                            newValue in
                            await SharedPreferences.disableDeprecatedWarnings.set(newValue)
                        }
                    }

                    Section("Working Directory") {
                        #if os(macOS)
                            if !Variant.useSystemExtension {
                                FormButton {
                                    NSWorkspace.shared.selectFile(nil, inFileViewerRootedAtPath: FilePath.workingDirectory.relativePath)
                                } label: {
                                    Label("Open", systemImage: "macwindow.and.cursorarrow")
                                }
                            }
                        #elseif os(iOS)
                            if #available(iOS 16.0, *) {
                                FormButton {
                                    Task {
                                        await openInFilesApp()
                                    }
                                } label: {
                                    Label("Browse", systemImage: "folder.fill")
                                }
                            }
                        #endif
                        FormButton(role: .destructive) {
                            Task {
                                await confirmDestroyWorkingDirectory()
                            }
                        } label: {
                            Label("Destroy", systemImage: "trash.fill")
                        }
                        .foregroundColor(.red)
                    }
                }
            }
        }
        .navigationTitle("Core")
        .alert($alert)
        .onAppear {
            guard !isLoading else {
                return
            }
            Task {
                await refreshWorkingDirectorySize()
            }
        }
        .onChangeCompat(of: scenePhase) { newValue in
            guard newValue == .active, !isLoading else {
                return
            }
            Task {
                await refreshWorkingDirectorySize()
            }
        }
        #if os(iOS)
        .navigationBarTitleDisplayMode(.inline)
        #endif
    }

    private nonisolated func loadSettings() async {
        if Variant.screenshotMode {
            await MainActor.run {
                version = "<redacted>"
                dataSize = LibboxFormatBytes(1000 * 1000 * 10)
                isLoading = false
            }
        } else {
            await MainActor.run {
                version = LibboxVersion()
                isLoading = false
            }
            await loadSettingsBackground()
        }
    }

    private nonisolated func loadSettingsBackground() async {
        let disableDeprecatedWarnings = await SharedPreferences.disableDeprecatedWarnings.get()
        await MainActor.run {
            self.disableDeprecatedWarnings = disableDeprecatedWarnings
        }
        await refreshWorkingDirectorySize()
    }

    private nonisolated func refreshWorkingDirectorySize() async {
        guard !Variant.screenshotMode else {
            return
        }
        #if os(macOS)
            let helperUnavailable = Variant.useSystemExtension && HelperServiceManager.rootHelperStatus != .enabled
        #endif
        let workingDirectory = FilePath.workingDirectory
        let dataSize: String? = await BlockingIO.run {
            #if os(macOS)
                if Variant.useSystemExtension {
                    if helperUnavailable {
                        return nil
                    }
                    guard let size = try? RootHelperClient.shared.getWorkingDirectorySize() else {
                        return nil
                    }
                    return LibboxFormatBytes(size)
                } else {
                    return (try? workingDirectory.formattedSize()) ?? "Unknown"
                }
            #else
                return (try? workingDirectory.formattedSize()) ?? "Unknown"
            #endif
        }
        await MainActor.run {
            #if os(macOS)
                self.helperUnavailable = helperUnavailable
            #endif
            self.dataSize = dataSize
            self.dataSizeLoaded = true
        }
    }

    private func confirmDestroyWorkingDirectory() async {
        #if os(macOS)
            if helperUnavailable {
                alert = helperRequiredAlert()
                return
            }
        #endif
        if environments.extensionProfile?.status.isConnected == true {
            alert = AlertState(
                title: String(localized: "Service is Running"),
                message: String(localized: "The service must be stopped before destroying the working directory."),
                primaryButton: .destructive(String(localized: "Stop Service and Continue")) { [self] in
                    Task {
                        await stopServiceAndDestroy()
                    }
                },
                secondaryButton: .cancel()
            )
        } else {
            await destroyWorkingDirectory()
        }
    }

    private func stopServiceAndDestroy() async {
        do {
            try await environments.extensionProfile!.stop()
            await destroyWorkingDirectory()
        } catch {
            alert = AlertState(action: "stop service before destroying working directory", error: error)
        }
    }

    private func destroyWorkingDirectory() async {
        do {
            let workingDirectory = FilePath.workingDirectory
            #if os(macOS)
                if Variant.useSystemExtension {
                    try await BlockingIO.run {
                        try RootHelperClient.shared.cleanWorkingDirectory()
                    }
                } else {
                    try await BlockingIO.run {
                        try Self.clearWorkingDirectoryContents(at: workingDirectory)
                    }
                }
            #else
                try await BlockingIO.run {
                    try Self.clearWorkingDirectoryContents(at: workingDirectory)
                }
                #if os(iOS)
                    if #available(iOS 16.0, *) {
                        await notifyFileProviderWorkingDirectoryChanged()
                    }
                #endif
            #endif
            isLoading = true
        } catch {
            alert = AlertState(action: "destroy working directory", error: error)
        }
    }

    private nonisolated static func clearWorkingDirectoryContents(at url: URL) throws {
        guard FileManager.default.fileExists(atPath: url.path) else {
            return
        }
        let contents = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil)
        for item in contents {
            try FileManager.default.removeItem(at: item)
        }
    }

    #if os(macOS)
        private func helperRequiredAlert() -> AlertState {
            AlertState(
                title: String(localized: "Helper Service Required"),
                message: String(localized: "Managing working directory requires Helper Service."),
                primaryButton: .default(String(localized: "App Settings")) {
                    NotificationCenter.default.post(name: .navigateToSettingsPage, object: SettingsPage.app)
                },
                secondaryButton: .cancel(String(localized: "Ok"))
            )
        }
    #endif

    #if os(iOS)
        @available(iOS 16.0, *)
        private nonisolated func fileProviderManager() async throws -> NSFileProviderManager {
            let domains = try await NSFileProviderManager.domains()
            guard let domain = domains.first(where: { $0.identifier.rawValue == AppConfiguration.fileProviderDomainID }) else {
                throw NSError(domain: "CoreView", code: 0, userInfo: [NSLocalizedDescriptionKey: "File provider domain not found"])
            }
            guard let manager = NSFileProviderManager(for: domain) else {
                throw NSError(domain: "CoreView", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to get file provider manager"])
            }
            return manager
        }

        @available(iOS 16.0, *)
        private nonisolated func notifyFileProviderWorkingDirectoryChanged() async {
            do {
                let manager = try await fileProviderManager()
                try await manager.signalEnumerator(for: .rootContainer)
                try await manager.signalEnumerator(for: .workingSet)
            } catch {
                await MainActor.run {
                    alert = AlertState(action: "notify Files app about working directory changes", error: error)
                }
            }
        }

        @available(iOS 16.0, *)
        private nonisolated func openInFilesApp() async {
            do {
                let manager = try await fileProviderManager()
                try await manager.signalEnumerator(for: .workingSet)
                let url = try await manager.getUserVisibleURL(for: .rootContainer)
                guard let sharedURL = URL(string: "shareddocuments://\(url.path)") else {
                    throw NSError(domain: "CoreView", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to create shared documents URL"])
                }
                await MainActor.run {
                    UIApplication.shared.open(sharedURL)
                }
            } catch {
                await MainActor.run {
                    alert = AlertState(action: "open working directory in Files", error: error)
                }
            }
        }
    #endif
}

private extension URL {
    func formattedSize() throws -> String? {
        guard let enumerator = FileManager.default.enumerator(
            at: self,
            includingPropertiesForKeys: [.totalFileAllocatedSizeKey]
        ) else {
            return nil
        }
        var size = 0
        while let url = enumerator.nextObject() as? URL {
            size += try url.resourceValues(forKeys: [.totalFileAllocatedSizeKey]).totalFileAllocatedSize ?? 0
        }
        let formatter = ByteCountFormatter()
        formatter.countStyle = .file
        guard let byteCount = formatter.string(for: size) else {
            return nil
        }
        return byteCount
    }
}
