//
//  AppDelegate.swift
//  Nextcloud
//
//  Created by Marino Faggiana on 04/09/14 (19/02/21 swift).
//  Copyright (c) 2014 Marino Faggiana. All rights reserved.
//
//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

import UIKit
import BackgroundTasks
import NextcloudKit
import TOPasscodeViewController
import LocalAuthentication
import Firebase
import WidgetKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, TOPasscodeViewControllerDelegate, NCAccountRequestDelegate, NCViewCertificateDetailsDelegate, NCUserBaseUrl {

    var backgroundSessionCompletionHandler: (() -> Void)?
    var window: UIWindow?

    @objc var account: String = ""
    @objc var urlBase: String = ""
    @objc var user: String = ""
    @objc var userId: String = ""
    @objc var password: String = ""

    var deletePasswordSession: Bool = false
    var activeLogin: NCLogin?
    var activeLoginWeb: NCLoginWeb?
    var activeServerUrl: String = ""
    @objc var activeViewController: UIViewController?
    var mainTabBar: NCMainTabBar?
    var activeMetadata: tableMetadata?
    var isSearchingMode: Bool = false

    let listFilesVC = ThreadSafeDictionary<String,NCFiles>()
    let listFavoriteVC = ThreadSafeDictionary<String,NCFavorite>()
    let listOfflineVC = ThreadSafeDictionary<String,NCOffline>()

    var disableSharesView: Bool = false
    var documentPickerViewController: NCDocumentPickerViewController?
    var networkingProcessUpload: NCNetworkingProcessUpload?
    var shares: [tableShare] = []
    var timerErrorNetworking: Timer?

    var errorITMS90076: Bool = false

    private var privacyProtectionWindow: UIWindow?
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        let userAgent = CCUtility.getUserAgent() as String
        let isSimulatorOrTestFlight = NCUtility.shared.isSimulatorOrTestFlight()
        let versionNextcloudiOS = String(format: NCBrandOptions.shared.textCopyrightNextcloudiOS, NCUtility.shared.getVersionApp())

        UserDefaults.standard.register(defaults: ["UserAgent": userAgent])
        if !CCUtility.getDisableCrashservice() && !NCBrandOptions.shared.disable_crash_service {
            FirebaseApp.configure()
        }

        CCUtility.createDirectoryStandard()
        CCUtility.emptyTemporaryDirectory()

        NKCommon.shared.setup(delegate: NCNetworking.shared)
        NKCommon.shared.setup(userAgent: userAgent)

        startTimerErrorNetworking()

        // LOG
        var levelLog = 0
        if let pathDirectoryGroup = CCUtility.getDirectoryGroup()?.path {
            NKCommon.shared.pathLog = pathDirectoryGroup
        }

        if NCBrandOptions.shared.disable_log {

            NCUtilityFileSystem.shared.deleteFile(filePath: NKCommon.shared.filenamePathLog)
            NCUtilityFileSystem.shared.deleteFile(filePath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! + "/" + NKCommon.shared.filenameLog)

        } else {

            levelLog = CCUtility.getLogLevel()
            NKCommon.shared.levelLog = levelLog
            NKCommon.shared.copyLogToDocumentDirectory = true
            if isSimulatorOrTestFlight {
                NKCommon.shared.writeLog("Start session with level \(levelLog) " + versionNextcloudiOS + " (Simulator / TestFlight)")
            } else {
                NKCommon.shared.writeLog("Start session with level \(levelLog) " + versionNextcloudiOS)
            }
        }

        // LOG Account
        if let account = NCManageDatabase.shared.getActiveAccount() {
            NKCommon.shared.writeLog("Account active \(account.account)")
            if CCUtility.getPassword(account.account).isEmpty {
                NKCommon.shared.writeLog("PASSWORD NOT FOUND for \(account.account)")
            }
        }

        // ITMS-90076: Potential Loss of Keychain Access
        if let account = NCManageDatabase.shared.getActiveAccount(), CCUtility.getPassword(account.account).isEmpty, NCUtility.shared.getVersionApp(withBuild: false).starts(with: "4.4") {
            errorITMS90076 = true
        }

        // Activate user account
        if let activeAccount = NCManageDatabase.shared.getActiveAccount() {

            // FIX 3.0.5 lost urlbase
            if activeAccount.urlBase.count == 0 {
                let user = activeAccount.user + " "
                let urlBase = activeAccount.account.replacingOccurrences(of: user, with: "")
                activeAccount.urlBase = urlBase
                NCManageDatabase.shared.updateAccount(activeAccount)
            }

            settingAccount(activeAccount.account, urlBase: activeAccount.urlBase, user: activeAccount.user, userId: activeAccount.userId, password: CCUtility.getPassword(activeAccount.account))

            NCBrandColor.shared.settingThemingColor(account: activeAccount.account)

        } else {

            CCUtility.deleteAllChainStore()
            if let bundleID = Bundle.main.bundleIdentifier {
                UserDefaults.standard.removePersistentDomain(forName: bundleID)
            }

            NCBrandColor.shared.createImagesThemingColor()
        }

        // Create user color
        NCBrandColor.shared.createUserColors()

        // initialize
        NotificationCenter.default.addObserver(self, selector: #selector(initialize), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterInitialize), object: nil)
        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterInitialize, userInfo:["atStart":1])

        // Process upload
        networkingProcessUpload = NCNetworkingProcessUpload()

        // Push Notification & display notification
        application.registerForRemoteNotifications()
        UNUserNotificationCenter.current().delegate = self
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { _, _ in }

        // Store review
        if !NCUtility.shared.isSimulatorOrTestFlight() {
            let review = NCStoreReview()
            review.incrementAppRuns()
            review.showStoreReview()
        }

        // Background task: register
        BGTaskScheduler.shared.register(forTaskWithIdentifier: NCGlobal.shared.refreshTask, using: nil) { task in
            self.handleRefreshTask(task)
        }
        BGTaskScheduler.shared.register(forTaskWithIdentifier: NCGlobal.shared.processingTask, using: nil) { task in
            self.handleProcessingTask(task)
        }

        // Intro
        if NCBrandOptions.shared.disable_intro {
            CCUtility.setIntro(true)
            if account == "" {
                openLogin(viewController: nil, selector: NCGlobal.shared.introLogin, openLoginWeb: false)
            }
        } else {
            if !CCUtility.getIntro() {
                if let viewController = UIStoryboard(name: "NCIntro", bundle: nil).instantiateInitialViewController() {
                    let navigationController = NCLoginNavigationController.init(rootViewController: viewController)
                    window?.rootViewController = navigationController
                    window?.makeKeyAndVisible()
                }
            }
        }

        // Passcode
        DispatchQueue.main.async {
            self.presentPasscode {
                self.enableTouchFaceID()
            }
        }

        return true
    }

    // MARK: - Life Cycle

    // L' applicazione entrerà in primo piano (attivo sempre)
    func applicationDidBecomeActive(_ application: UIApplication) {

        self.deletePasswordSession = false

        if !NCAskAuthorization.shared.isRequesting {
            // Privacy
            hidePrivacyProtectionWindow()
        }

        NCSettingsBundleHelper.setVersionAndBuildNumber()

        if account == "" { return }

        networkingProcessUpload?.verifyUploadZombie()

        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterApplicationDidBecomeActive)
    }

    // L' applicazione entrerà in primo piano (attivo solo dopo il background)
    func applicationWillEnterForeground(_ application: UIApplication) {

        if account == "" { return }
        guard let activeAccount = NCManageDatabase.shared.getActiveAccount() else { return }

        // Account changed ??
        if activeAccount.account != account {
            settingAccount(activeAccount.account, urlBase: activeAccount.urlBase, user: activeAccount.user, userId: activeAccount.userId, password: CCUtility.getPassword(activeAccount.account))

            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterInitialize)
        }

        NKCommon.shared.writeLog("Application will enter in foreground")

        // START TIMER UPLOAD PROCESS
        if NCUtility.shared.isSimulator() {
            networkingProcessUpload?.startTimer()
        }
        
        // Initialize Auto upload
        NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in
            NKCommon.shared.writeLog("Initialize Auto upload with \(items) uploads")
        }

        // Required unsubscribing / subscribing
        NCPushNotification.shared().pushNotification()

        // Request Service Server Nextcloud
        NCService.shared.startRequestServicesServer()
        
        // Request TouchID, FaceID
        enableTouchFaceID()
        
        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterApplicationWillEnterForeground)
        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterRichdocumentGrabFocus)
        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSourceNetwork)
    }

    // L' applicazione si dimetterà dallo stato di attivo
    func applicationWillResignActive(_ application: UIApplication) {

        if account == "" { return }

        if CCUtility.getPrivacyScreenEnabled() {
            // Privacy
            showPrivacyProtectionWindow()
        }

        // Reload Widget
        if #available(iOS 14.0, *) {
            WidgetCenter.shared.reloadAllTimelines()
            //WidgetCenter.shared.reloadTimelines(ofKind: "DashboardWidget")
        }

        // Clear operation queue
        NCOperationQueue.shared.cancelAllQueue()
        // Clear download
        NCNetworking.shared.cancelAllDownloadTransfer()

        // Clear older files
        let days = CCUtility.getCleanUpDay()
        if let directory = CCUtility.getDirectoryProviderStorage() {
            NCUtilityFileSystem.shared.cleanUp(directory: directory, days: TimeInterval(days))
        }

        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterApplicationWillResignActive)
    }

    // L' applicazione è entrata nello sfondo
    func applicationDidEnterBackground(_ application: UIApplication) {

        if account == "" { return }

        // STOP TIMER UPLOAD PROCESS
        if NCUtility.shared.isSimulator() {
            networkingProcessUpload?.stopTimer()
        }

        scheduleAppRefresh()
        scheduleAppProcessing()

        // Passcode
        presentPasscode { }

        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterApplicationDidEnterBackground)
    }

    // L'applicazione terminerà
    func applicationWillTerminate(_ application: UIApplication) {

        NCNetworking.shared.cancelAllDownloadTransfer()
        NKCommon.shared.writeLog("bye bye")
    }

    // MARK: -

    @objc private func initialize() {
        guard !account.isEmpty else { return }

        NKCommon.shared.writeLog("initialize Main")

        // Registeration push notification
        NCPushNotification.shared().pushNotification()

        // Start Auto Upload
        NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in
            NKCommon.shared.writeLog("Initialize Auto upload with \(items) uploads")
        }

        // Start services
        NCService.shared.startRequestServicesServer()

        // close detail
        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterMenuDetailClose)

        // Reload Widget
        if #available(iOS 14.0, *) {
            WidgetCenter.shared.reloadAllTimelines()
        }

        // Registeration domain File Provider
        // FileProviderDomain *fileProviderDomain = [FileProviderDomain new];
        // [fileProviderDomain removeAllDomains];
        // [fileProviderDomain registerDomains];
    }

    // MARK: - Background Task

    /*
    @discussion Schedule a refresh task request to ask that the system launch your app briefly so that you can download data and keep your app's contents up-to-date. The system will fulfill this request intelligently based on system conditions and app usage.
     */
    func scheduleAppRefresh() {

        let request = BGAppRefreshTaskRequest(identifier: NCGlobal.shared.refreshTask)
        request.earliestBeginDate = Date(timeIntervalSinceNow: 6 * 60) // Refresh after 6 minutes.
        do {
            try BGTaskScheduler.shared.submit(request)
            NKCommon.shared.writeLog("Refresh task success submit request 6 minutes \(request)")
        } catch {
            NKCommon.shared.writeLog("Refresh task failed to submit request: \(error)")
        }
    }

    /*
     @discussion Schedule a processing task request to ask that the system launch your app when conditions are favorable for battery life to handle deferrable, longer-running processing, such as syncing, database maintenance, or similar tasks. The system will attempt to fulfill this request to the best of its ability within the next two days as long as the user has used your app within the past week.
     */
    func scheduleAppProcessing() {

        let request = BGProcessingTaskRequest(identifier: NCGlobal.shared.processingTask)
        request.earliestBeginDate = Date(timeIntervalSinceNow: 5 * 60) // Refresh after 5 minutes.
        request.requiresNetworkConnectivity = false
        request.requiresExternalPower = false
        do {
            try BGTaskScheduler.shared.submit(request)
            NKCommon.shared.writeLog("Background Processing task success submit request 5 minutes \(request)")
        } catch {
            NKCommon.shared.writeLog("Background Processing task failed to submit request: \(error)")
        }
    }

    func handleRefreshTask(_ task: BGTask) {
        scheduleAppRefresh()
        
        guard !account.isEmpty else {
            task.setTaskCompleted(success: true)
            return
        }
        
        NKCommon.shared.writeLog("Start handler refresh task [Auto upload]")

        NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in
            NKCommon.shared.writeLog("Completition handler refresh task [Auto upload] with \(items) uploads")
            task.setTaskCompleted(success: true)
        }
    }

    func handleProcessingTask(_ task: BGTask) {
        scheduleAppProcessing()
        
        guard !account.isEmpty else {
            task.setTaskCompleted(success: true)
            return
        }

        NKCommon.shared.writeLog("Start handler processing task [Auto upload]")
        
        NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in
            NKCommon.shared.writeLog("Completition handler procesing task [Auto upload] with \(items) uploads")
            task.setTaskCompleted(success: true)
        }
    }

    // MARK: - Background Networking Session

    func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {

        NKCommon.shared.writeLog("Start handle Events For Background URLSession: \(identifier)")
        backgroundSessionCompletionHandler = completionHandler
    }

    // MARK: - Push Notifications

    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler(UNNotificationPresentationOptions.alert)
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        completionHandler()
    }

    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        NCNetworking.shared.checkPushNotificationServerProxyCertificateUntrusted(viewController: self.window?.rootViewController) { error in
            if error == .success {
                NCPushNotification.shared().registerForRemoteNotifications(withDeviceToken: deviceToken)
            }
        }
    }

    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        NCPushNotification.shared().applicationdidReceiveRemoteNotification(userInfo) { result in
            completionHandler(result)
        }
    }

    // MARK: - Login & checkErrorNetworking

    @objc func openLogin(viewController: UIViewController?, selector: Int, openLoginWeb: Bool) {

        // [WEBPersonalized] [AppConfig]
        if NCBrandOptions.shared.use_login_web_personalized || NCBrandOptions.shared.use_AppConfig {

            if activeLoginWeb?.view.window == nil {
                activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
                activeLoginWeb?.urlBase = NCBrandOptions.shared.loginBaseUrl
                showLoginViewController(activeLoginWeb, contextViewController: viewController)
            }
            return
        }

        // Nextcloud standard login
        if selector == NCGlobal.shared.introSignup {

            if activeLoginWeb?.view.window == nil {
                activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
                if selector == NCGlobal.shared.introSignup {
                    activeLoginWeb?.urlBase = NCBrandOptions.shared.linkloginPreferredProviders
                } else {
                    activeLoginWeb?.urlBase = self.urlBase
                }
                showLoginViewController(activeLoginWeb, contextViewController: viewController)
            }

        } else if NCBrandOptions.shared.disable_intro && NCBrandOptions.shared.disable_request_login_url {

            if activeLoginWeb?.view.window == nil {
                activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
                activeLoginWeb?.urlBase = NCBrandOptions.shared.loginBaseUrl
                showLoginViewController(activeLoginWeb, contextViewController: viewController)
            }

        } else if openLoginWeb {

            if activeLoginWeb?.view.window == nil {
                activeLoginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb
                activeLoginWeb?.urlBase = urlBase
                showLoginViewController(activeLoginWeb, contextViewController: viewController)
            }

        } else {

            if activeLogin?.view.window == nil {
                activeLogin = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLogin") as? NCLogin
                showLoginViewController(activeLogin, contextViewController: viewController)
            }
        }
    }

    func showLoginViewController(_ viewController: UIViewController?, contextViewController: UIViewController?) {

        if contextViewController == nil {
            if let viewController = viewController {
                let navigationController = NCLoginNavigationController.init(rootViewController: viewController)
                navigationController.navigationBar.barStyle = .black
                navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText
                navigationController.navigationBar.barTintColor = NCBrandColor.shared.customer
                navigationController.navigationBar.isTranslucent = false
                window?.rootViewController = navigationController
                window?.makeKeyAndVisible()
            }
        } else if contextViewController is UINavigationController {
            if let contextViewController = contextViewController, let viewController = viewController {
                (contextViewController as! UINavigationController).pushViewController(viewController, animated: true)
            }
        } else {
            if let viewController = viewController, let contextViewController = contextViewController {
                let navigationController = NCLoginNavigationController.init(rootViewController: viewController)
                navigationController.modalPresentationStyle = .fullScreen
                navigationController.navigationBar.barStyle = .black
                navigationController.navigationBar.tintColor = NCBrandColor.shared.customerText
                navigationController.navigationBar.barTintColor = NCBrandColor.shared.customer
                navigationController.navigationBar.isTranslucent = false
                contextViewController.present(navigationController, animated: true) { }
            }
        }
    }
    
    @objc func startTimerErrorNetworking() {
        timerErrorNetworking = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(checkErrorNetworking), userInfo: nil, repeats: true)
    }

    @objc private func checkErrorNetworking() {
        
        // check unauthorized server (401/403)
        if account != "" && CCUtility.getPassword(account)!.count == 0 {
            openLogin(viewController: window?.rootViewController, selector: NCGlobal.shared.introLogin, openLoginWeb: true)
        }
    }
    
    func trustCertificateError(host: String) {

        guard let currentHost = URL(string: self.urlBase)?.host,
              let pushNotificationServerProxyHost = URL(string: NCBrandOptions.shared.pushNotificationServerProxy)?.host,
              host != pushNotificationServerProxyHost,
              host == currentHost
        else { return }

        let certificateHostSavedPath = CCUtility.getDirectoryCerificates()! + "/" + host + ".der"
        var title = NSLocalizedString("_ssl_certificate_changed_", comment: "")
        
        if !FileManager.default.fileExists(atPath: certificateHostSavedPath) {
            title = NSLocalizedString("_connect_server_anyway_", comment: "")
        }
        
        let alertController = UIAlertController(title: title, message: NSLocalizedString("_server_is_trusted_", comment: ""), preferredStyle: .alert)
        
        alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default, handler: { action in
            NCNetworking.shared.writeCertificate(host: host)
        }))
        
        alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .default, handler: { action in }))
        
        alertController.addAction(UIAlertAction(title: NSLocalizedString("_certificate_details_", comment: ""), style: .default, handler: { action in
            if let navigationController = UIStoryboard(name: "NCViewCertificateDetails", bundle: nil).instantiateInitialViewController() as? UINavigationController {
                let viewController = navigationController.topViewController as! NCViewCertificateDetails
                viewController.delegate = self
                viewController.host = host
                self.window?.rootViewController?.present(navigationController, animated: true)
            }
        }))
        
        window?.rootViewController?.present(alertController, animated: true)
    }

    func viewCertificateDetailsDismiss(host: String) {
        trustCertificateError(host: host)
    }

    // MARK: - Account

    @objc func settingAccount(_ account: String, urlBase: String, user: String, userId: String, password: String) {

        self.account = account
        self.urlBase = urlBase
        self.user = user
        self.userId = userId
        self.password = password

        _ = NCFunctionCenter.shared

        NKCommon.shared.setup(account: account, user: user, userId: userId, password: password, urlBase: urlBase)
        let serverVersionMajor = NCManageDatabase.shared.getCapabilitiesServerInt(account: account, elements: NCElementsJSON.shared.capabilitiesVersionMajor)
        if serverVersionMajor > 0 {
            NKCommon.shared.setup(nextcloudVersion: serverVersionMajor)
        }
        NCKTVHTTPCache.shared.restartProxy(user: user, password: password)
    }

    @objc func deleteAccount(_ account: String, wipe: Bool) {

        if let account = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", account)) {
            NCPushNotification.shared().unsubscribingNextcloudServerPushNotification(account.account, urlBase: account.urlBase, user: account.user, withSubscribing: false)
        }
        
        let results = NCManageDatabase.shared.getTableLocalFiles(predicate: NSPredicate(format: "account == %@", account), sorted: "ocId", ascending: false)
        for result in results {
            CCUtility.removeFile(atPath: CCUtility.getDirectoryProviderStorageOcId(result.ocId))
        }
        NCManageDatabase.shared.clearDatabase(account: account, removeAccount: true)
        
        CCUtility.clearAllKeysEnd(toEnd: account)
        CCUtility.clearAllKeysPushNotification(account)
        CCUtility.setPassword(account, password: nil)

        if wipe {
            settingAccount("", urlBase: "", user: "", userId: "", password: "")
            let accounts = NCManageDatabase.shared.getAccounts()
            if accounts?.count ?? 0 > 0 {
                if let newAccount = accounts?.first {
                    self.changeAccount(newAccount)
                }
            } else {
                openLogin(viewController: window?.rootViewController, selector: NCGlobal.shared.introLogin, openLoginWeb: false)
            }
        }
    }

    @objc func changeAccount(_ account: String) {

        NCManageDatabase.shared.setAccountActive(account)
        if let tableAccount = NCManageDatabase.shared.getActiveAccount() {

            NCOperationQueue.shared.cancelAllQueue()
            NCNetworking.shared.cancelAllTask()

            settingAccount(tableAccount.account, urlBase: tableAccount.urlBase, user: tableAccount.user, userId: tableAccount.userId, password: CCUtility.getPassword(tableAccount.account))

            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterInitialize)
        }
    }

    // MARK: - Account Request

    func accountRequestChangeAccount(account: String) {
        changeAccount(account)
    }
    
    func requestAccount() {

        if isPasscodePresented() { return }
        if !CCUtility.getAccountRequest() { return }

        let accounts = NCManageDatabase.shared.getAllAccount()

        if accounts.count > 1 {
            
            if let vcAccountRequest = UIStoryboard(name: "NCAccountRequest", bundle: nil).instantiateInitialViewController() as? NCAccountRequest {

                vcAccountRequest.activeAccount = NCManageDatabase.shared.getActiveAccount()
                vcAccountRequest.accounts = accounts
                vcAccountRequest.enableTimerProgress = true
                vcAccountRequest.enableAddAccount = false
                vcAccountRequest.dismissDidEnterBackground = false
                vcAccountRequest.delegate = self

                let screenHeighMax = UIScreen.main.bounds.height - (UIScreen.main.bounds.height/5)
                let numberCell = accounts.count
                let height = min(CGFloat(numberCell * Int(vcAccountRequest.heightCell) + 45), screenHeighMax)

                let popup = NCPopupViewController(contentController: vcAccountRequest, popupWidth: 300, popupHeight: height+20)
                popup.backgroundAlpha = 0.8

                UIApplication.shared.keyWindow?.rootViewController?.present(popup, animated: true)
                
                vcAccountRequest.startTimer()
            }
        }
    }

    // MARK: - Passcode

    func presentPasscode(completion: @escaping () -> ()) {

        let laContext = LAContext()
        var error: NSError?

        defer { self.requestAccount() }

        let presentedViewController = window?.rootViewController?.presentedViewController
        guard !account.isEmpty, CCUtility.isPasscodeAtStartEnabled(), !(presentedViewController is NCLoginNavigationController) else { return }

        // Make sure we have a privacy window (in case it's not enabled)
        showPrivacyProtectionWindow()

        let passcodeViewController = TOPasscodeViewController(passcodeType: .sixDigits, allowCancel: false)
        passcodeViewController.delegate = self
        passcodeViewController.keypadButtonShowLettering = false
        if CCUtility.getEnableTouchFaceID() && laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
            if error == nil {
                if laContext.biometryType == .faceID  {
                    passcodeViewController.biometryType = .faceID
                } else if laContext.biometryType == .touchID  {
                    passcodeViewController.biometryType = .touchID
                }
                passcodeViewController.allowBiometricValidation = true
                passcodeViewController.automaticallyPromptForBiometricValidation = false
            }
        }

        // show passcode on top of privacy window
        privacyProtectionWindow?.rootViewController?.present(passcodeViewController, animated: true, completion: {
            completion()
        })
    }

    func isPasscodePresented() -> Bool {
        return privacyProtectionWindow?.rootViewController?.presentedViewController is TOPasscodeViewController
    }

    func enableTouchFaceID() {
        guard !account.isEmpty,
              CCUtility.getEnableTouchFaceID(),
              CCUtility.isPasscodeAtStartEnabled(),
              let passcodeViewController = privacyProtectionWindow?.rootViewController?.presentedViewController as? TOPasscodeViewController
        else { return }

        LAContext().evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: NCBrandOptions.shared.brand) { (success, error) in
            if success {
                DispatchQueue.main.async {
                    passcodeViewController.dismiss(animated: true) {
                        self.hidePrivacyProtectionWindow()
                        self.requestAccount()
                    }
                }
            }
        }
    }

    func didInputCorrectPasscode(in passcodeViewController: TOPasscodeViewController) {
        DispatchQueue.main.async {
            passcodeViewController.dismiss(animated: true) {
                self.hidePrivacyProtectionWindow()
                self.requestAccount()
            }
        }
    }

    func passcodeViewController(_ passcodeViewController: TOPasscodeViewController, isCorrectCode code: String) -> Bool {
        return code == CCUtility.getPasscode()
    }

    func didPerformBiometricValidationRequest(in passcodeViewController: TOPasscodeViewController) {
        enableTouchFaceID()
    }

    // MARK: - Privacy Protection

    private func showPrivacyProtectionWindow() {
        guard privacyProtectionWindow == nil else {
            privacyProtectionWindow?.isHidden = false
            return
        }

        privacyProtectionWindow = UIWindow(frame: UIScreen.main.bounds)

        let storyboard = UIStoryboard(name: "LaunchScreen", bundle: nil)
        let initialViewController = storyboard.instantiateInitialViewController()

        self.privacyProtectionWindow?.rootViewController = initialViewController

        privacyProtectionWindow?.windowLevel = .alert + 1
        privacyProtectionWindow?.makeKeyAndVisible()
    }

    func hidePrivacyProtectionWindow() {
        guard !(privacyProtectionWindow?.rootViewController?.presentedViewController is TOPasscodeViewController) else { return }
        UIWindow.animate(withDuration: 0.25) {
            self.privacyProtectionWindow?.alpha = 0
        } completion: { _ in
            self.privacyProtectionWindow?.isHidden = true
            self.privacyProtectionWindow = nil
        }
    }
    
    // MARK: - Open URL

    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {

        if account == "" { return false }

        let scheme = url.scheme
        let action = url.host
        var fileName: String = ""
        var serverUrl: String = ""
        var matchedAccount: tableAccount?

        /*
         Example:
         nextcloud://open-action?action=create-voice-memo
         */

        if scheme == "nextcloud" && action == "open-action" {

            if let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) {
                let queryItems = urlComponents.queryItems
                guard let actionScheme = CCUtility.value(forKey: "action", fromQueryItems: queryItems), let rootViewController = window?.rootViewController else { return false }
                
                switch actionScheme {
                case NCGlobal.shared.actionUploadAsset:

                    NCAskAuthorization.shared.askAuthorizationPhotoLibrary(viewController: rootViewController) { hasPermission in
                        if hasPermission {
                            NCPhotosPickerViewController.init(viewController: rootViewController, maxSelectedAssets: 0, singleSelectedMode: false)
                        }
                    }
                    
                case NCGlobal.shared.actionScanDocument:
                    
                    NCCreateScanDocument.shared.openScannerDocument(viewController: rootViewController)
                    
                case NCGlobal.shared.actionTextDocument:
                    
                    guard let navigationController = UIStoryboard(name: "NCCreateFormUploadDocuments", bundle: nil).instantiateInitialViewController(), let directEditingCreators = NCManageDatabase.shared.getDirectEditingCreators(account: account), let directEditingCreator = directEditingCreators.first(where: { $0.editor == NCGlobal.shared.editorText}) else { return false }
                    
                    navigationController.modalPresentationStyle = UIModalPresentationStyle.formSheet

                    let viewController = (navigationController as! UINavigationController).topViewController as! NCCreateFormUploadDocuments
                    viewController.editorId = NCGlobal.shared.editorText
                    viewController.creatorId = directEditingCreator.identifier
                    viewController.typeTemplate = NCGlobal.shared.templateDocument
                    viewController.serverUrl = activeServerUrl
                    viewController.titleForm = NSLocalizedString("_create_nextcloudtext_document_", comment: "")

                    rootViewController.present(navigationController, animated: true, completion: nil)
                    
                case NCGlobal.shared.actionVoiceMemo:
                    
                    NCAskAuthorization.shared.askAuthorizationAudioRecord(viewController: rootViewController) { hasPermission in
                        if hasPermission {
                            let fileName = CCUtility.createFileNameDate(NSLocalizedString("_voice_memo_filename_", comment: ""), extension: "m4a")!
                            let viewController = UIStoryboard(name: "NCAudioRecorderViewController", bundle: nil).instantiateInitialViewController() as! NCAudioRecorderViewController

                            viewController.delegate = self
                            viewController.createRecorder(fileName: fileName)
                            viewController.modalTransitionStyle = .crossDissolve
                            viewController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext

                            rootViewController.present(viewController, animated: true, completion: nil)
                        }
                    }

                default:
                    print("No action")
                }
            }
        }

        /*
         Example:
         nextcloud://open-file?path=Talk/IMG_0000123.jpg&user=marinofaggiana&link=https://cloud.nextcloud.com/f/123
         */

        else if scheme == "nextcloud" && action == "open-file" {

            if !isSearchingMode, let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) {

                let queryItems = urlComponents.queryItems
                guard let userScheme = CCUtility.value(forKey: "user", fromQueryItems: queryItems) else { return false }
                guard let pathScheme = CCUtility.value(forKey: "path", fromQueryItems: queryItems) else { return false }
                guard let linkScheme = CCUtility.value(forKey: "link", fromQueryItems: queryItems) else { return false }

                if let activeAccount = NCManageDatabase.shared.getActiveAccount() {

                    let urlBase = URL(string: activeAccount.urlBase)
                    let user = activeAccount.user
                    if linkScheme.contains(urlBase?.host ?? "") && userScheme == user {
                        matchedAccount = activeAccount
                    } else {
                        let accounts = NCManageDatabase.shared.getAllAccount()
                        for account in accounts {
                            guard let accountURL = URL(string: account.urlBase) else { return false }
                            if linkScheme.contains(accountURL.host ?? "") && userScheme == account.user {
                                changeAccount(account.account)
                                matchedAccount = account
                                break
                            }
                        }
                    }

                    if matchedAccount != nil {

                        let webDAV = NCUtilityFileSystem.shared.getWebDAV(account: self.account) + "/files/" + self.userId
                        if pathScheme.contains("/") {
                            fileName = (pathScheme as NSString).lastPathComponent
                            serverUrl = matchedAccount!.urlBase + "/" + webDAV + "/" + (pathScheme as NSString).deletingLastPathComponent
                        } else {
                            fileName = pathScheme
                            serverUrl = matchedAccount!.urlBase + "/" + webDAV
                        }

                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                            NCFunctionCenter.shared.openFileViewInFolder(serverUrl: serverUrl, fileNameBlink: fileName)
                        }

                    } else {

                        guard let domain = URL(string: linkScheme)?.host else { return true }
                        fileName = (pathScheme as NSString).lastPathComponent
                        let message = String(format: NSLocalizedString("_account_not_available_", comment: ""), userScheme, domain, fileName)

                        let alertController = UIAlertController(title: NSLocalizedString("_info_", comment: ""), message: message, preferredStyle: .alert)
                        alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))

                        window?.rootViewController?.present(alertController, animated: true, completion: { })

                        return false
                    }
                }
            }
        } else {
            app.open(url)
        }

        return true
    }
}

// MARK: - NCAudioRecorder ViewController Delegate

extension AppDelegate: NCAudioRecorderViewControllerDelegate {

    func didFinishRecording(_ viewController: NCAudioRecorderViewController, fileName: String) {

        guard
            let navigationController = UIStoryboard(name: "NCCreateFormUploadVoiceNote", bundle: nil).instantiateInitialViewController() as? UINavigationController,
                let viewController = navigationController.topViewController as? NCCreateFormUploadVoiceNote
        else { return }
        navigationController.modalPresentationStyle = .formSheet
        viewController.setup(serverUrl: activeServerUrl, fileNamePath: NSTemporaryDirectory() + fileName, fileName: fileName)
        window?.rootViewController?.present(navigationController, animated: true)
    }

    func didFinishWithoutRecording(_ viewController: NCAudioRecorderViewController, fileName: String) {
    }
}

extension AppDelegate: NCCreateFormUploadConflictDelegate {
    func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) {
        guard let metadatas = metadatas, !metadatas.isEmpty else { return }
        networkingProcessUpload?.createProcessUploads(metadatas: metadatas)
    }
}