package com.ejada.hmg.hmgwifi

import android.annotation.SuppressLint
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.net.wifi.*
import android.net.wifi.SupplicantState.ASSOCIATED
import android.net.wifi.SupplicantState.COMPLETED
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import com.ejada.hmg.MainActivity
import com.ejada.hmg.utils.HMGUtils
import java.security.cert.X509Certificate

class WpaEnterprise(private val mainActivity: MainActivity, private var SSID: String) {
    private var TAG = "WpaEnterprise"

    private lateinit var identity:String
    private lateinit var password:String
    private lateinit var completion:((status: Boolean, message: String) -> Unit)

    fun connect(identity:String, password:String, completion: (status: Boolean, message: String) -> Unit) {
        this.password = password
        this.identity = identity
        this.completion = completion

        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
            apiGreaterThen28()
        }else if(Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
            apiLessThen29()
        }
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    fun apiLessThen29(){
        val wifiManager = mainActivity.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager

        val wifi = WifiConfiguration()
        wifi.SSID = """"$SSID""""
        wifi.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP)
        wifi.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X)
        wifi.enterpriseConfig = enterpriseConfig()
        wifi.networkId = ssidToNetworkId(wifi.SSID, wifiManager)
        if (wifi.networkId == -1) {
            wifiManager.addNetwork(wifi)
        } else {
            Log.v(TAG, "WiFi found - updating it.\n")
            wifiManager.updateNetwork(wifi)
        }
        Log.v(TAG, "saving config.\n")
        wifiManager.saveConfiguration()
        wifi.networkId = ssidToNetworkId(wifi.SSID, wifiManager)

        Log.v(TAG, "wifi ID in device = " + wifi.networkId)

        var supState: SupplicantState
        val networkIdToConnect = wifi.networkId
        if (networkIdToConnect >= 0) {
            Log.v(TAG, "Start connecting...\n")

            // We disable the network before connecting, because if this was the last connection before
            // a disconnect(), this will not reconnect.
            wifiManager.disableNetwork(networkIdToConnect)
            wifiManager.enableNetwork(networkIdToConnect, true)

            val wifiInfo: WifiInfo = wifiManager.connectionInfo

            HMGUtils.timer(5000,false){
                supState = wifiInfo.supplicantState
                Log.i(TAG, "Done connect to network : status =  $supState")
                val successStates = listOf(COMPLETED, ASSOCIATED)
                if (successStates.contains(supState))
                    completion(true,"Connected to internet Wifi")
                else
                    completion(false,"errorConnectingHmgNetwork")
            }

        } else {
            Log.v(TAG, "WifiWizard: cannot connect to network")
            completion(false,"errorConnectingHmgNetwork")
        }
    }

    /**
     * This method takes a given String, searches the current list of configured WiFi
     * networks, and returns the networkId for the network if the SSID matches. If not,
     * it returns -1.
     */
    @SuppressLint("MissingPermission")
    private fun ssidToNetworkId(ssid: String, wifiManager: WifiManager): Int {
        val currentNetworks = wifiManager.configuredNetworks
        var networkId = -1
        // For each network in the list, compare the SSID with the given one
        for (test in currentNetworks) {
            if (test.SSID == ssid) {
                networkId = test.networkId
                break
            }
        }
        return networkId
    }


    @RequiresApi(Build.VERSION_CODES.Q)
    fun apiGreaterThen28(){
        val connectivityManager = mainActivity.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

        Log.e(TAG, "connection wifi with Android Q+")
        val wifiNetworkSpecifier: WifiNetworkSpecifier = WifiNetworkSpecifier.Builder()
            .setSsid(SSID)
            .setWpa2EnterpriseConfig(enterpriseConfig())
            .build()

        val networkRequest: NetworkRequest = NetworkRequest.Builder()
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                .setNetworkSpecifier(wifiNetworkSpecifier)
                .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) //removeCapability added for hotspots without internet
                .build()

        val networkCallback = object : ConnectivityManager.NetworkCallback() {
            override fun onAvailable(network: Network) {
                super.onAvailable(network)
                connectivityManager.bindProcessToNetwork(network)
                completion(true, "200")
                Log.e(TAG, "onAvailable")
            }

            override fun onLosing(network: Network, maxMsToLive: Int) {
                super.onLosing(network, maxMsToLive)
                Log.e(TAG, "onLosing")
            }

            override fun onLost(network: Network) {
                super.onLost(network)
                Log.e(TAG, "onLosing")
                Log.e(TAG, "losing active connection")
            }

            override fun onUnavailable() {
                super.onUnavailable()
                completion(false, "401")
                Log.e(TAG, "onUnavailable")
            }

        }

        //timeout add because "No devices found" wasn't handled correct and doesn't throw Unavailable
        connectivityManager.requestNetwork(networkRequest, networkCallback, 30000)
    }

    fun enterpriseConfig() : WifiEnterpriseConfig{
        // Initialize the WifiConfiguration object
        val enterpriseConfig = WifiEnterpriseConfig()
        enterpriseConfig.eapMethod = WifiEnterpriseConfig.Eap.PEAP
        enterpriseConfig.identity = identity
        enterpriseConfig.password = password
        enterpriseConfig.phase2Method = WifiEnterpriseConfig.Phase2.NONE
//        enterpriseConfig.caCertificates = WifiEnterpriseConfig.Phase2.
        return enterpriseConfig;
    }

}