import NetworkExtension
import SystemConfiguration
import SystemConfiguration.CaptiveNetwork

@objc(WifiConnect)
class WifiConnect: NSObject {

    @objc(connectToWifi:withResolver:withRejecter:)
    func connectToWifi(args: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
        let username = args["username"] as! String
        let password = args["password"] as! String
        let ssid = args["ssid"] as! String
        let domainSuffix = args["domainSuffix"] as! String
        let ignoreNetworkErrorOnSimulator = args["ignoreNetworkErrorOnSimulator"] as! Bool

        let isHiddenSSID = args["isHiddenSSID"] as! Bool

        #if targetEnvironment(simulator)
            if ignoreNetworkErrorOnSimulator {
                resolve(nil)
            } else {
                reject("E_RUNNING_ON_SIMULATOR", "Connecting to a WiFi network on simulator is not supported.", nil)
            }
            return
        #endif

        let eapSettings = NEHotspotEAPSettings()
        eapSettings.username = username
        eapSettings.password = password
        eapSettings.isTLSClientCertificateRequired = false
        eapSettings.supportedEAPTypes = [NSNumber(value: NEHotspotEAPSettings.EAPType.EAPPEAP.rawValue)]
        eapSettings.trustedServerNames = [domainSuffix]

        let hotspotConfiguration = NEHotspotConfiguration(ssid: ssid, eapSettings: eapSettings)
        hotspotConfiguration.joinOnce = false

        if #available(iOS 13.0, *) {
            hotspotConfiguration.hidden = isHiddenSSID
        }

        let completionHandler: (Error?) -> Void = { (error) in
            if let error = error {
                let nsError = error as NSError
                if nsError.domain == "NEHotspotConfigurationErrorDomain" {
                    let errorCode = self.interpretHotspotConfigurationError(code: nsError.code)
                    if errorCode == "E_ALREADY_CONFIGURED" {
                        // The network is already configured. But now we received new credentials.
                        // Therefore, we need to delete the configuration and set it again.
                        NEHotspotConfigurationManager.shared.removeConfiguration(forSSID: ssid)
                        self.connectToWifi(args: args, resolve: resolve, reject: reject)
                    } else {
                        reject(errorCode, error.localizedDescription, error)
                    }
                } else {
                    reject("E_UNKNOWN_ERROR", error.localizedDescription, error)
                }
            } else {
                resolve(nil)
            }
        }

        NEHotspotConfigurationManager.shared.apply(hotspotConfiguration, completionHandler: completionHandler)

        // ? Note - remove the code above from line 18-60 and uncomment this code to enable
        // ? push notifications checks before connect (it's enabled in 0.8.0)
        // * Checking if user allows push notifications
        // * If YES -> user can connect to wifi-network
        // * If not -> user can't connect and getting error

        // let notificationSettings = UNUserNotificationCenter.current();
        // notificationSettings.getNotificationSettings(completionHandler: { permission in
        //     switch permission.authorizationStatus  {
        //     case .authorized:
        //         print("User granted permission for notification")
        //         // User can connect to Wifi only if accepted push notifications permissions
        //         #if targetEnvironment(simulator)
        //             if ignoreNetworkErrorOnSimulator {
        //                 resolve(nil)
        //             } else {
        //                 reject("E_RUNNING_ON_SIMULATOR", "Connecting to a WiFi network on simulator is not supported.", nil)
        //             }
        //             return
        //         #endif

        //         let eapSettings = NEHotspotEAPSettings()
        //         eapSettings.username = username
        //         eapSettings.password = password
        //         eapSettings.isTLSClientCertificateRequired = false
        //         eapSettings.supportedEAPTypes = [NSNumber(value: NEHotspotEAPSettings.EAPType.EAPPEAP.rawValue)]
        //         eapSettings.trustedServerNames = [domainSuffix]

        //         let hotspotConfiguration = NEHotspotConfiguration(ssid: ssid, eapSettings: eapSettings)
        //         hotspotConfiguration.joinOnce = false

        //         if #available(iOS 13.0, *) {
        //             hotspotConfiguration.hidden = isHiddenSSID
        //         }

        //         let completionHandler: (Error?) -> Void = { (error) in
        //             if let error = error {
        //                 let nsError = error as NSError
        //                 if nsError.domain == "NEHotspotConfigurationErrorDomain" {
        //                     let errorCode = self.interpretHotspotConfigurationError(code: nsError.code)
        //                     if errorCode == "E_ALREADY_CONFIGURED" {
        //                         // The network is already configured. But now we received new credentials.
        //                         // Therefore, we need to delete the configuration and set it again.
        //                         NEHotspotConfigurationManager.shared.removeConfiguration(forSSID: ssid)
        //                         self.connectToWifi(args: args, resolve: resolve, reject: reject)
        //                     } else {
        //                         reject(errorCode, error.localizedDescription, error)
        //                     }
        //                 } else {
        //                     reject("E_UNKNOWN_ERROR", error.localizedDescription, error)
        //                 }
        //             } else {
        //                 resolve(nil)
        //             }
        //         }

        //         NEHotspotConfigurationManager.shared.apply(hotspotConfiguration, completionHandler: completionHandler)
        //     case .denied:
        //         print("User denied notification permission")
        //         reject("E_USER_REJECTED", "User denied notification permission. Push notifications permission should be accepted", nil)
        //         return
        //     case .notDetermined:
        //         print("Notification permission haven't been asked yet")
        //         reject("E_USER_REJECTED", "Notification permission haven't been asked yet. Push notifications permission should be accepted", nil)
        //     case .provisional:
        //         // @available(iOS 12.0, *)
        //         print("The application is authorized to post non-interruptive user notifications.")
        //     case .ephemeral:
        //         // @available(iOS 14.0, *)
        //         print("The application is temporarily authorized to post notifications. Only available to app clips.")
        //     @unknown default:
        //         print("Unknow Status")
        //     }
        // })
    }
    
    @objc(deleteConfiguration:withResolver:withRejecter:)
    func deleteConfiguration(args: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
        let ignoreNetworkErrorOnSimulator = args["ignoreNetworkErrorOnSimulator"] as! Bool
        
        #if targetEnvironment(simulator)
        if ignoreNetworkErrorOnSimulator {
            resolve(nil)
        } else {
            reject("E_RUNNING_ON_SIMULATOR", "Connecting to a WiFi network on simulator is not supported.", nil)
        }
        return
        #endif
        
        let completionHandler: (([String]) -> Void) = { (ssids) in
            for ssid in ssids {
                if #available(iOS 11.0, *) {
                    NEHotspotConfigurationManager.shared.removeConfiguration(forSSID: ssid)
                } else {
                    // Fallback on earlier versions
                }
            }
            resolve(nil)
        }
        
        if #available(iOS 11.0, *) {
            NEHotspotConfigurationManager.shared.getConfiguredSSIDs(completionHandler: completionHandler)
        } else {
            // Fallback on earlier versions
        }
    }
    
    @available(iOS 11.0, *)
    @objc(isWifiConfigured:withRejecter:)
    func isWifiConfigured(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
        let completionHandler: (([String]) -> Void) = { (ssids) in
            if (ssids.isEmpty) {
                resolve(false)
            } else {
                resolve(true)
            }
        }
        
        if #available(iOS 11.0, *) {
            NEHotspotConfigurationManager.shared.getConfiguredSSIDs(completionHandler: completionHandler)
        } else {
            // Fallback on earlier versions
        }
    }
    
    private func interpretHotspotConfigurationError(code: Int) -> String {
        if let configError = NEHotspotConfigurationError(rawValue: code) {
            switch configError {
            case .invalid, .invalidSSID, .invalidWPAPassphrase, .invalidEAPSettings:
                return "E_INVALID_WIFI_CONFIGURATION"
            case .userDenied:
                return "E_USER_REJECTED"
            case .alreadyAssociated:
                return "E_ALREADY_CONFIGURED"
            default:
                return "E_UNKNOWN_ERROR"
            }
        }
        return "E_UNKNOWN_ERROR"
    }
    
    @objc(isConnectedToWifi:withResolver:withRejecter:)
    func isConnectedToWifi(ssid: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
        if #available(iOS 14.0, *) {
            DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
                NEHotspotNetwork.fetchCurrent(completionHandler: { (network: NEHotspotNetwork?) -> Void in
                    if network == nil || network?.ssid != ssid {
                        resolve(false)
                    } else {
                        resolve(true)
                    }
                })
            }
        } else {
            CNCopyCurrentNetworkInfo("en0" as CFString)
            
            guard let networkInfo = CNCopyCurrentNetworkInfo("en0" as CFString) else {
                print("System error: Could not retrieve network information")
                resolve(false)
                return
            }
            guard let SSIDDict = (networkInfo as NSDictionary) as? [String: AnyObject] else {
            print("System error: interface information is not a string-keyed dictionary")
                resolve(false)
                return
            }
            
            if SSIDDict[kCNNetworkInfoKeySSID as String] as! String != ssid {
                resolve(false)
                return
            } else {
                resolve(true)
            }
        }
    }
}
