import UIKit
import WowonderMessengerSDK
import DropDown
import Reachability
import SwiftEventBus
import IQKeyboardManagerSwift
import IQKeyboardToolbarManager
import Async
import OneSignalFramework
import FBSDKLoginKit
import GoogleSignIn
import GoogleMobileAds
import FBAudienceNetwork
import GoogleMaps
import CoreData
import Stipop

let appDelegate = UIApplication.shared.delegate as! AppDelegate

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    static var shared: AppDelegate {
        get {
            return UIApplication.shared.delegate as! AppDelegate
        }
    }
    let notificationBuilder = LocalNotificationBuilder.shared
    var window: UIWindow?
    var isInternetConnected = Connectivity.isConnectedToNetwork()
    var reachability = try! Reachability()
    let hostNames = ["google.com", "google.com", "google.com"]
    var hostIndex = 0
    var launchOptions = [UIApplication.LaunchOptionsKey: Any]()
    
    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "WoWonder")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        self.launchOptions = launchOptions ?? [:]
        
        /* Decryption of Cert Key */
        ServerCredentials.setServerDataWithKey(key: AppConstant.key)
        
        self.initFrameworks(application: application, launchOptions: launchOptions)
        
        let path = FileManager
            .default
            .urls(for: .applicationSupportDirectory, in: .userDomainMask)
            .last?
            .absoluteString
            .replacingOccurrences(of: "file://", with: "")
            .removingPercentEncoding
        
        print("DB Path :: \(path ?? "Not found")")
        print("Api =>\(AppInstance.instance._sessionId)")
        
        return true
    }
    
    func applicationWillResignActive(_ application: UIApplication) {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
    }
    
    func applicationDidEnterBackground(_ application: UIApplication) {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
        
        /*self.socketHelper.emit(event: "ping_for_lastseen", data: ["from_id": AppInstance.instance.sessionId ?? ""])*/
    }
    
    func applicationWillEnterForeground(_ application: UIApplication) {
        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
        
    }
    
    func applicationDidBecomeActive(_ application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }
    
    func applicationWillTerminate(_ application: UIApplication) {
        stopNotifier()
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
        
        SocketHelper.shared.emit(event: "on_user_loggedoff", data: ["from_id": AppInstance.instance.sessionId ?? ""])
        SocketHelper.shared.disconnect()
    }
    
}

extension AppDelegate {
    
    func initFrameworks(application: UIApplication, launchOptions: [UIApplication.LaunchOptionsKey: Any]?) {
        
        self.setLanguage()
        
        self.setupTheme()
        
        ApplicationDelegate.initialize()
        ApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions)
        Stipop.initialize()
        startHost(at: 0)
        
        /* IQ Keyboard */
        self.initializeIQKeyboardManager()
        
        DropDown.startListeningToKeyboard()
        self.setupDropdownAppearance()
        
        MobileAds.shared.start(completionHandler: nil)
        
        self.notificationBuilder.registerNotification(application)
    }
    
    private func initializeIQKeyboardManager() {
        IQKeyboardManager.shared.isEnabled = true
        IQKeyboardToolbarManager.shared.isEnabled = true
        IQKeyboardManager.shared.resignOnTouchOutside = true
    }
    
    func setupDropdownAppearance() {
        let appearance =  DropDown.appearance()
        appearance.backgroundColor = .color_fill
        appearance.textColor = .text_color
        appearance.textFont = UIFont(name: "DMSans-Regular", size: 14) ?? UIFont()
        appearance.cornerRadius = 8
        DropDown.startListeningToKeyboard()
    }
    
    // Setup Theme
    func setupTheme() {
        let status = UserDefaults.standard.getDarkMode(Key: "darkMode")
        let isSystemTheme = UserDefaults.standard.getSystemTheme(Key: "SystemTheme")
        if #available(iOS 13.0, *) {
            if isSystemTheme {
                window?.overrideUserInterfaceStyle = UIScreen.main.traitCollection.userInterfaceStyle
            } else {
                if status {
                    window?.overrideUserInterfaceStyle = .dark
                } else {
                    window?.overrideUserInterfaceStyle = .light
                }
            }
        }
    }
    
    // Set Language
    func setLanguage() {
        let preferredLanguage = NSLocale.preferredLanguages[0]
        if preferredLanguage.starts(with: "ar") {
            UIView.appearance().semanticContentAttribute = .forceRightToLeft
        } else {
            UIView.appearance().semanticContentAttribute = .forceLeftToRight
        }
    }
    
}

extension AppDelegate {
    
    func saveContext () {
        let context = persistentContainer.viewContext
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }
    
}

extension AppDelegate {
    
    func startHost(at index: Int) {
        stopNotifier()
        setupReachability(hostNames[index], useClosures: false)
        startNotifier()
        DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
            self.startHost(at: (index + 1) % 3)
        }
    }
    
    func setupReachability(_ hostName: String?, useClosures: Bool) {
        let reachability:  Reachability?
        if let hostName = hostName {
            reachability = try! Reachability(hostname: hostName)
        } else {
            reachability = try! Reachability()
        }
        self.reachability = try! reachability ??  Reachability.init()
        if useClosures {
        } else {
            NotificationCenter.default.addObserver(
                self,
                selector: #selector(reachabilityChanged(_:)),
                name: .reachabilityChanged,
                object: reachability
            )
        }
    }
    
    func startNotifier() {
        do {
            try reachability.startNotifier()
        } catch {
            return
        }
    }
    
    func stopNotifier() {
        reachability.stopNotifier()
        NotificationCenter.default.removeObserver(self, name: .reachabilityChanged, object: nil)
    }
    
    @objc func reachabilityChanged(_ note: Notification) {
        Async.main({
            let reachability = note.object as! Reachability
            switch reachability.connection {
            case .wifi:
                self.isInternetConnected = true
                SwiftEventBus.post(EventBusConstants.EventBusConstantsUtils.EVENT_INTERNET_CONNECTED)
                SwiftEventBus.post(EventBusConstants.EventBusConstantsUtils.EVENT_CONNECT_SOCKET_CALL)
            case .cellular:
                self.isInternetConnected = true
                SwiftEventBus.post(EventBusConstants.EventBusConstantsUtils.EVENT_INTERNET_CONNECTED)
            case .unavailable:
                self.isInternetConnected = false
                SwiftEventBus.post(EventBusConstants.EventBusConstantsUtils.EVENT_INTERNET_DIS_CONNECTED)
            }
        })
    }
    
}

extension AppDelegate {
    
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print("Failed to register for notifications: \(error.localizedDescription)")
    }
    
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        let deviceTokenString = deviceToken.map { String(format: "%02.2hhx", arguments: [$0]) }.joined()
        print(deviceTokenString)
    }
    
    func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
        print(error)
    }
    
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any],
                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        print("userinfo === >>>>> ", userInfo)
        let aps = userInfo["aps"] as? [String: Any]
        let alert = aps?["alert"] as? [String: String]
        let title = alert?["title"]
        let body = alert?["body"]
        print(title ?? "nil")
        print(body ?? "nil")
        
        completionHandler(.noData)
    }
    
    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
      var handled: Bool
      handled = GIDSignIn.sharedInstance.handle(url)
      if handled {
        return true
      }
      // Handle other custom URL types.

      // If not handled by this app, return false.
      return false
    }
    
}

// MARK: OSPushSubscriptionObserver
extension AppDelegate: OSPushSubscriptionObserver {
    
    func onPushSubscriptionDidChange(state: OneSignalUser.OSPushSubscriptionChangedState) {
        if let playerId = OneSignal.User.pushSubscription.id {
            self.savePlayerId(playerId)
        }
    }
    
    func savePlayerId(_ playerId: String) {
        print("playerId >>>>> ", playerId)
        UserDefaults.standard.setDeviceId(value: playerId, ForKey: Local.DEVICE_ID.DeviceId)
    }
    
}
