//  AdvancedPrefsView.swift
//  Tusker
//
//  Created by Shadowfacts on 6/13/19.
//  Copyright © 2019 Shadowfacts. All rights reserved.
//

import SwiftUI
import Pachyderm
import CoreData
import CloudKit
import UserAccounts

struct AdvancedPrefsView : View {
    @ObservedObject var preferences = Preferences.shared
    @State private var imageCacheSize: Int64 = 0
    @State private var mastodonCacheSize: Int64 = 0
    @State private var cloudKitStatus: CKAccountStatus?
    @State private var isShowingFeatureFlagAlert = false
    @State private var featureFlagName = ""
    
    var body: some View {
        List {
            formattingSection
            cloudKitSection
            errorReportingSection
            cachingSection
            featureFlagSection
            
        }
        .listStyle(.insetGrouped)
        .appGroupedListBackground(container: PreferencesNavigationController.self)
        .onTapGesture(count: 3) {
            featureFlagName = ""
            isShowingFeatureFlagAlert = true
        }
        .alert("Enable Feature Flag", isPresented: $isShowingFeatureFlagAlert) {
            TextField("Flag Name", text: $featureFlagName)
                .textInputAutocapitalization(.never)
                .autocorrectionDisabled()
            
            Button("Cancel", role: .cancel) {}
            Button("Enable") {
                if let flag = Preferences.FeatureFlag(rawValue: featureFlagName) {
                    preferences.enabledFeatureFlags.insert(flag)
                }
            }
        } message: {
            Text("Warning: Feature flags are intended for development and debugging use only. They are experimental and subject to change at any time.")
        }
        .navigationBarTitle(Text("Advanced"))
    }
    
    var formattingFooter: some View {
        var s: AttributedString = "This option is only supported with Pleroma and some compatible Mastodon instances (such as Glitch).\n"
        if let account = UserAccountsManager.shared.getMostRecentAccount() {
           let mastodonController = MastodonController.getForAccount(account)
            // shouldn't need to load the instance here, because loading it is kicked off my the scene delegate
            if !mastodonController.instanceFeatures.probablySupportsMarkdown {
                var warning = AttributedString("\(account.instanceURL.host!) does not appear to support formatting. Using formatting symbols may not have an effect.")
                warning[AttributeScopes.SwiftUIAttributes.FontAttribute.self] = .caption.bold()
                s += warning
            }
        }
        return Text(s).lineLimit(nil)
    }
    
    var formattingSection: some View {
        Section(footer: formattingFooter) {
            Picker(selection: $preferences.statusContentType, label: Text("Post Content Type")) {
                ForEach(StatusContentType.allCases, id: \.self) { type in
                    Text(type.displayName).tag(type)
                }//.navigationBarTitle("Post Content Type")
                // see FB6838291
            }
        }
        .appGroupedListRowBackground()
    }
    
    var cloudKitSection: some View {
        Section {
            HStack {
                Text("iCloud Status")
                Spacer()
                switch cloudKitStatus {
                case nil:
                    EmptyView()
                case .available:
                    Text("Available")
                case .couldNotDetermine:
                    Text("Could not determine")
                case .noAccount:
                    Text("No account")
                case .restricted:
                    Text("Restricted")
                case .temporarilyUnavailable:
                    Text("Temporarily Unavailable")
                @unknown default:
                    Text(String(describing: cloudKitStatus!))
                }
            }
        }
        .appGroupedListRowBackground()
        .task {
            do {
                let status = try await CKContainer.default().accountStatus()
                self.cloudKitStatus = status
            } catch {
                Logging.general.error("Unable to get CloudKit status: \(String(describing: error))")
            }
        }
    }
    
    var errorReportingSection: some View {
        Section {
            Toggle("Report Errors Automatically", isOn: $preferences.reportErrorsAutomatically)
        } footer: {
            var privacyPolicy: AttributedString = "Privacy Policy"
            let _ = privacyPolicy.link = URL(string: "https://vaccor.space/tusker#privacy")!
            if preferences.reportErrorsAutomatically {
                Text(AttributedString("App crashes and errors will be automatically reported to the developer. You may be prompted to add additional information.\n") + privacyPolicy)
                    .lineLimit(nil)
            } else {
                Text(AttributedString("Errors will not be reported automatically. When a crash occurs, you may be asked to report it manually.\n") + privacyPolicy)
                    .lineLimit(nil)
            }
        }
        .appGroupedListRowBackground()
    }
    
    var cachingSection: some View {
        Section {
            Button(action: clearCache) {
                Text("Clear Mastodon Cache")
            }.foregroundColor(.red)
            Button(action: clearImageCaches) {
                Text("Clear Image Caches")
            }.foregroundColor(.red)
        } header: {
            Text("Caching")
        } footer: {
            var s: AttributedString = "Clearing caches will restart the app."
            if imageCacheSize != 0 {
                s += AttributedString("\nImage cache size: \(ByteCountFormatter().string(fromByteCount: imageCacheSize))")
            }
            if mastodonCacheSize != 0 {
                s += AttributedString("\nMastodon cache size: \(ByteCountFormatter().string(fromByteCount: mastodonCacheSize))")
            }
            return Text(s)
        }
        .appGroupedListRowBackground()
        .task {
            imageCacheSize = [
                ImageCache.avatars,
                .headers,
                .attachments,
                .emojis,
            ].map {
                $0.getDiskSizeInBytes() ?? 0
            }.reduce(0, +)
            mastodonCacheSize = UserAccountsManager.shared.accounts.map {
                let descriptions = MastodonController.getForAccount($0).persistentContainer.persistentStoreDescriptions
                return descriptions.map {
                    guard let url = $0.url else {
                        return 0
                    }
                    return FileManager.default.recursiveSize(url: url) ?? 0
                }.reduce(0, +)
            }.reduce(0, +)
        }
    }
    
    @ViewBuilder
    var featureFlagSection: some View {
        if !preferences.enabledFeatureFlags.isEmpty {
            Section {
                ForEach(preferences.enabledFeatureFlags.map(\.rawValue).sorted(), id: \.self) { name in
                    Text(verbatim: name)
                        .contextMenu {
                            Button(role: .destructive) {
                                preferences.enabledFeatureFlags.remove(.init(rawValue: name)!)
                            } label: {
                                Label("Remove", systemImage: "trash")
                            }
                        }
                }
                .onDelete { indices in
                    let sortedFlags = preferences.enabledFeatureFlags.sorted(by: { $0.rawValue < $1.rawValue })
                    let removed = indices.map { sortedFlags[$0] }
                    preferences.enabledFeatureFlags.subtract(removed)
                }
            } header: {
                Text("Feature Flags")
            }
        }
    }
    
    private func clearCache() {
        for account in UserAccountsManager.shared.accounts {
            let controller = MastodonController.getForAccount(account)
            let container = controller.persistentContainer
            do {
                let statusesReq = NSBatchDeleteRequest(fetchRequest: StatusMO.fetchRequest())
                try container.viewContext.execute(statusesReq)
                let accountsReq = NSBatchDeleteRequest(fetchRequest: AccountMO.fetchRequest())
                try container.viewContext.execute(accountsReq)
                let relationshipsReq = NSBatchDeleteRequest(fetchRequest: RelationshipMO.fetchRequest())
                try container.viewContext.execute(relationshipsReq)
            } catch {
                Logging.general.error("Error while clearing Mastodon cache: \(String(describing: error), privacy: .public)")
            }
        }
        resetUI()
    }
    
    private func clearImageCaches() {
        [
            ImageCache.avatars,
            ImageCache.headers,
            ImageCache.attachments,
            ImageCache.emojis,
        ].forEach {
            try! $0.reset()
        }
        resetUI()
    }
    
    private func resetUI() {
        let mostRecent = UserAccountsManager.shared.getMostRecentAccount()!
        NotificationCenter.default.post(name: .activateAccount, object: nil, userInfo: ["account": mostRecent])
    }
}

extension StatusContentType {
    var displayName: String {
        switch self {
        case .plain:
            return "Plain"
        case .markdown:
            return "Markdown"
        case .html:
            return "HTML"
        }
    }
}

#if DEBUG
struct AdvancedPrefsView_Previews : PreviewProvider {
    static var previews: some View {
        AdvancedPrefsView()
    }
}
#endif
