import UIKit
import BackgroundTasks

import Theme
import XXModels
import XXLogger
import Defaults
import PushFeature
import ToastFeature
import LaunchFeature
import CrashReporting
import DependencyInjection

import XXClient
import XXMessengerClient

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 messenger = try? DependencyInjection.Container.shared.resolve() as Messenger,
       let database = try? DependencyInjection.Container.shared.resolve() as Database,
       let cMix = try? messenger.cMix.tryGet() {
      let backgroundTask = application.beginBackgroundTask(withName: "xx.stop.network") {}

      backgroundTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { timer in
        print(">>> .backgroundTimeRemaining: \(UIApplication.shared.backgroundTimeRemaining)")

        guard UIApplication.shared.backgroundTimeRemaining > 8 else {
          if !self.calledStopNetwork {
            self.calledStopNetwork = true
            try! messenger.stop()
            print(">>> Called stopNetworkFollower")
          } else {
            if cMix.hasRunningProcesses() == false {
              application.endBackgroundTask(backgroundTask)
              timer.invalidate()
            }
          }

          return
        }

        guard UIApplication.shared.backgroundTimeRemaining > 9 else {
          if !self.forceFailedPendingMessages {
            self.forceFailedPendingMessages = true

            let query = Message.Query(status: [.sending])
            let assignment = Message.Assignments(status: .sendingFailed)
            _ = try? database.bulkUpdateMessages(query, assignment)
          }

          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 messenger = try? DependencyInjection.Container.shared.resolve() as Messenger {
      try? messenger.stop()
    }
  }

  public func applicationWillEnterForeground(_ application: UIApplication) {
    if backgroundTimer != nil {
      backgroundTimer?.invalidate()
      backgroundTimer = nil
      print(">>> Invalidated background timer")
    }

    if let messenger = try? DependencyInjection.Container.shared.resolve() as Messenger,
       let cMix = messenger.cMix.get() {
      guard self.calledStopNetwork == true else { return }
      try? cMix.startNetworkFollower(timeoutMS: 10_000)
      print(">>> Called startNetworkFollower")
      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)
  }

  public func application(
    _ application: UIApplication,
    continue userActivity: NSUserActivity,
    restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
  ) -> Bool {
    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
          let incomingURL = userActivity.webpageURL,
          let username = getUsernameFromInvitationDeepLink(incomingURL),
          let router = try? DependencyInjection.Container.shared.resolve() as PushRouter else {
      return false
    }

    router.navigateTo(.search(username: username), {})
    return true
  }
}

func getUsernameFromInvitationDeepLink(_ url: URL) -> String? {
  if let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
     components.scheme == "https",
     components.host == "elixxir.io",
     components.path == "/connect",
     let queryItem = components.queryItems?.first(where: { $0.name == "username" }),
     let username = queryItem.value {
    return username
  }

  return nil
}

// 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)
  }
}