import UIKit
import BackgroundTasks

import Theme
import XXLogger
import Defaults
import Integration
import PushFeature
import ToastFeature
import SwiftyDropbox
import LaunchFeature
import DropboxFeature
import CrashReporting
import DependencyInjection

public class AppDelegate: UIResponder, UIApplicationDelegate {
    @Dependency private var pushRouter: PushRouter
    @Dependency private var pushHandler: PushHandling
    @Dependency private var crashReporter: CrashReporter
    @Dependency private var dropboxService: DropboxInterface

    @KeyObject(.hideAppList, defaultValue: false) var hideAppList: Bool
    @KeyObject(.recordingLogs, defaultValue: true) var recordingLogs: Bool
    @KeyObject(.crashReporting, defaultValue: true) var isCrashReportingEnabled: Bool

    var calledStopNetwork = false
    var forceFailedPendingMessages = false

    var coverView: UIView?
    var backgroundTimer: Timer?
    public var window: UIWindow?

    public func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        #if DEBUG
        DependencyRegistrator.registerForMock()
        #else
        DependencyRegistrator.registerForLive()
        #endif

        if recordingLogs {
            XXLogger.start()
        }

        crashReporter.configure()
        crashReporter.setEnabled(isCrashReportingEnabled)

        UNUserNotificationCenter.current().delegate = self

        let window = Window()
        let navController = UINavigationController(rootViewController: LaunchController())
        window.rootViewController = StatusBarViewController(ToastViewController(navController))
        window.backgroundColor = UIColor.white
        window.makeKeyAndVisible()
        self.window = window

        DependencyInjection.Container.shared.register(
            PushRouter.live(navigationController: navController)
        )

        return true
    }

    public func application(application: UIApplication, shouldAllowExtensionPointIdentifier: String) -> Bool {
        false
    }

    public func applicationDidEnterBackground(_ application: UIApplication) {
        if let session = try? DependencyInjection.Container.shared.resolve() as SessionType {
            let backgroundTask = application.beginBackgroundTask(withName: "xx.stop.network") {}

            // An option here would be: create async completion closure

            backgroundTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
                guard UIApplication.shared.backgroundTimeRemaining > 8 else {
                    if !self.calledStopNetwork {
                        self.calledStopNetwork = true
                        session.stop()
                    } else {
                        if session.hasRunningTasks == false {
                            application.endBackgroundTask(backgroundTask)
                            timer.invalidate()
                        }
                    }

                    return
                }

                guard UIApplication.shared.backgroundTimeRemaining > 9 else {
                    if !self.forceFailedPendingMessages {
                        self.forceFailedPendingMessages = true
                        session.forceFailMessages()
                    }

                    return
                }
            }
        }
    }

    public func applicationWillResignActive(_ application: UIApplication) {
        if hideAppList {
            coverView?.removeFromSuperview()
            coverView = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
            coverView?.frame = window?.bounds ?? .zero
            window?.addSubview(coverView!)
        }
    }
    
    public func applicationWillTerminate(_ application: UIApplication) {
        if let session = try? DependencyInjection.Container.shared.resolve() as SessionType {
            session.stop()
        }
    }

    public func applicationWillEnterForeground(_ application: UIApplication) {
        if backgroundTimer != nil {
            backgroundTimer?.invalidate()
            backgroundTimer = nil
        }

        if let session = try? DependencyInjection.Container.shared.resolve() as SessionType {
            guard self.calledStopNetwork == true else { return }
            session.start()
            self.calledStopNetwork = false
        }
    }

    public func applicationDidBecomeActive(_ application: UIApplication) {
        application.applicationIconBadgeNumber = 0
        coverView?.removeFromSuperview()
    }

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

// MARK: Notifications

extension AppDelegate: UNUserNotificationCenterDelegate {
    public func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        didReceive response: UNNotificationResponse,
        withCompletionHandler completionHandler: @escaping () -> Void
    ) {
        let userInfo = response.notification.request.content.userInfo
        pushHandler.handleAction(pushRouter, userInfo, completionHandler)
    }

    public func application(
        _ application: UIApplication,
        didReceiveRemoteNotification notification: [AnyHashable: Any],
        fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
    ) {
        pushHandler.handlePush(notification, completionHandler)
    }

    public func application(
        _: UIApplication,
        didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
    ) {
        pushHandler.registerToken(deviceToken)
    }
}