package com.example.base.utils

import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInfo
import android.content.res.Configuration
import android.content.res.Resources
import android.net.Uri
import android.net.wifi.WifiManager
import android.os.Build
import android.provider.Settings
import android.telephony.TelephonyManager
import android.text.TextUtils
import androidx.annotation.RequiresPermission
import java.io.File
import java.net.InetAddress
import java.net.NetworkInterface
import java.net.SocketException
import java.util.Enumeration
import java.util.UUID


object DeviceUtils {

    /**
     * Whether the device is rooted
     */
    fun isDeviceRooted(): Boolean {
        val su = "su"
        val locations = arrayOf(
            "/system/bin/", "/system/xbin/", "/sbin/", "/system/sd/xbin/",
            "/system/bin/failsafe/", "/data/local/xbin/", "/data/local/bin/", "/data/local/",
            "/system/sbin/", "/usr/bin/", "/vendor/bin/"
        )
        for (location in locations) {
            if (File(location + su).exists()) {
                return true
            }
        }
        return false
    }

    fun getAppVersion(): PackageInfo? {
        try {
            return Utils.getApp().packageManager.getPackageInfo(
                Utils.getApp().packageName,
                0
            )
        } catch (e: Exception) {

        }
        return null
    }

    /**
     * AppName
     */
    fun getAppName(): String? {
        return getAppVersion()?.let {
            Utils.getApp().resources.getString(it.applicationInfo.labelRes)
        }
    }

    /**
     * VersionName
     */
    fun getSDKVersionName(): String? {
        return Build.VERSION.RELEASE
    }

    /**
     * VersionCode
     */
    fun getSDKVersionCode(): Int {
        return Build.VERSION.SDK_INT
    }

    /**
     * AndroidID
     */
    @SuppressLint("HardwareIds")
    fun getAndroidID(): String? {
        val id: String? = Settings.Secure.getString(
            Utils.getApp().contentResolver,
            Settings.Secure.ANDROID_ID
        )
        return if ("9774d56d682e549c" == id) "" else id ?: ""
    }

    /**
     * Mac Address
     */
    @SuppressLint("MissingPermission")
    fun getMMacAddress(): String? {
        val macAddress = getMacAddress()
        if (macAddress != "" || getWifiEnabled()) return macAddress
        setWifiEnabled(true)
        setWifiEnabled(false)
        return getMacAddress()
    }

    private fun getWifiEnabled(): Boolean {
        @SuppressLint("WifiManagerLeak") val manager =
            Utils.getApp().getSystemService(Context.WIFI_SERVICE) as? WifiManager
                ?: return false
        return manager.isWifiEnabled
    }

    /**
     * enable or disable wifi
     */
    @RequiresPermission(Manifest.permission.CHANGE_WIFI_STATE)
    private fun setWifiEnabled(enabled: Boolean) {
        @SuppressLint("WifiManagerLeak") val manager =
            Utils.getApp().getSystemService(Context.WIFI_SERVICE) as? WifiManager
                ?: return
        if (enabled == manager.isWifiEnabled) return
        manager.isWifiEnabled = enabled
    }

    /**
     * Mac Address
     */
    @RequiresPermission(allOf = [Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.INTERNET])
    fun getMacAddress(vararg excepts: String?): String? {
        var macAddress: String? = getMacAddressByNetworkInterface()
        if (isAddressNotInExcepts(macAddress, *excepts)) {
            return macAddress
        }
        macAddress = getMacAddressByInetAddress()
        if (isAddressNotInExcepts(macAddress, *excepts)) {
            return macAddress
        }
        macAddress = getMacAddressByWifiInfo()

        return if (isAddressNotInExcepts(macAddress, *excepts)) {
            macAddress
        } else ""
    }

    private fun isAddressNotInExcepts(
        address: String?,
        vararg excepts: String?
    ): Boolean {
        if (excepts.isEmpty()) {
            return "02:00:00:00:00:00" != address
        }
        for (filter in excepts) {
            if (address == filter) {
                return false
            }
        }
        return true
    }

    @SuppressLint("MissingPermission", "HardwareIds")
    private fun getMacAddressByWifiInfo(): String? {
        try {
            val wifi = Utils.getApp()
                .applicationContext
                .getSystemService(Context.WIFI_SERVICE) as? WifiManager
            if (wifi != null) {
                val info = wifi.connectionInfo
                if (info != null) return info.macAddress
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return "02:00:00:00:00:00"
    }

    private fun getMacAddressByNetworkInterface(): String? {
        try {
            val nis: Enumeration<NetworkInterface> = NetworkInterface.getNetworkInterfaces()
            while (nis.hasMoreElements()) {
                val ni: NetworkInterface? = nis.nextElement()
                if (ni == null || !ni.name.equals("wlan0", true)) continue
                val macBytes: ByteArray? = ni.hardwareAddress
                if (macBytes != null && macBytes.isNotEmpty()) {
                    val sb = StringBuilder()
                    for (b in macBytes) {
                        sb.append(String.format("%02x:", b))
                    }
                    return sb.substring(0, sb.length - 1)
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return "02:00:00:00:00:00"
    }

    private fun getMacAddressByInetAddress(): String? {
        try {
            val inetAddress: InetAddress? = getInetAddress()
            if (inetAddress != null) {
                val ni: NetworkInterface? = NetworkInterface.getByInetAddress(inetAddress)
                if (ni != null) {
                    val macBytes: ByteArray? = ni.hardwareAddress
                    if (macBytes != null && macBytes.isNotEmpty()) {
                        val sb = StringBuilder()
                        for (b in macBytes) {
                            sb.append(String.format("%02x:", b))
                        }
                        return sb.substring(0, sb.length - 1)
                    }
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return "02:00:00:00:00:00"
    }

    private fun getInetAddress(): InetAddress? {
        try {
            val nis: Enumeration<NetworkInterface> = NetworkInterface.getNetworkInterfaces()
            while (nis.hasMoreElements()) {
                val ni: NetworkInterface = nis.nextElement()
                // To prevent phone of xiaomi return "10.0.2.15"
                if (!ni.isUp()) continue
                val addresses: Enumeration<InetAddress> = ni.inetAddresses
                while (addresses.hasMoreElements()) {
                    val inetAddress: InetAddress = addresses.nextElement()
                    if (!inetAddress.isLoopbackAddress) {
                        val hostAddress: String = inetAddress.hostAddress
                        if (hostAddress.indexOf(':') < 0) return inetAddress
                    }
                }
            }
        } catch (e: SocketException) {
            e.printStackTrace()
        }
        return null
    }

    /**
     * obtain the equipment manufacturer
     */
    fun getManufacturer(): String? {
        return Build.MANUFACTURER
    }

    /**
     * Device Name
     */
    fun getDeviceName(): String? {
        return Build.DEVICE
    }

    /**
     * get device model
     */
    fun getModel(): String? {
        var model = Build.MODEL
        model = model?.trim { it <= ' ' }?.replace("\\s*".toRegex(), "") ?: ""
        return model
    }

    /**
     * Returns an ordered list of ABIs supported by this device. The most preferred ABI is the first element in the list
     */
    fun getABIs(): Array<String?>? {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Build.SUPPORTED_ABIS
        } else {
            if (!TextUtils.isEmpty(Build.CPU_ABI2)) {
                arrayOf(Build.CPU_ABI, Build.CPU_ABI2)
            } else arrayOf(Build.CPU_ABI)
        }
    }

    /**
     * isTablet
     */
    fun isTablet(): Boolean {
        return ((Resources.getSystem().configuration.screenLayout
                and Configuration.SCREENLAYOUT_SIZE_MASK)
                >= Configuration.SCREENLAYOUT_SIZE_LARGE)
    }

    /**
     * isEmulator
     */
    fun isEmulator(): Boolean {
        val checkProperty = (Build.FINGERPRINT.startsWith("generic")
                || Build.FINGERPRINT.toLowerCase().contains("vbox")
                || Build.FINGERPRINT.toLowerCase().contains("test-keys")
                || Build.MODEL.contains("google_sdk")
                || Build.MODEL.contains("Emulator")
                || Build.MODEL.contains("Android SDK built for x86")
                || Build.MANUFACTURER.contains("Genymotion")
                || Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")
                || "google_sdk" == Build.PRODUCT)
        if (checkProperty) return true
        var operatorName = ""
        val tm =
            Utils.getApp().getSystemService(Context.TELEPHONY_SERVICE) as? TelephonyManager
        if (tm != null) {
            val name = tm.networkOperatorName
            if (name != null) {
                operatorName = name
            }
        }
        val checkOperatorName = operatorName.toLowerCase() == "android"
        if (checkOperatorName) return true
        val url = "tel:" + "123456"
        val intent = Intent()
        intent.data = Uri.parse(url)
        intent.action = Intent.ACTION_DIAL
        return intent.resolveActivity(Utils.getApp().packageManager) == null
    }

    private const val KEY_UDID = "KEY_UDID"

    @Volatile
    private var udid: String? = null

    /**
     * get the only device ID
     * <pre>{1}{UUID(macAddress)}</pre>
     * <pre>{2}{UUID(androidId )}</pre>
     * <pre>{9}{UUID(random    )}</pre>
     *
     * @return the unique device id
     */
    @SuppressLint("MissingPermission", "HardwareIds")
    fun getUniqueDeviceId(): String? {
        return getUniqueDeviceId("", true)
    }

    /**
     * getUniqueDeviceId
     * <pre>android 10 deprecated {prefix}{1}{UUID(macAddress)}</pre>
     * <pre>{prefix}{2}{UUID(androidId )}</pre>
     * <pre>{prefix}{9}{UUID(random    )}</pre>
     */
    @SuppressLint("MissingPermission", "HardwareIds")
    fun getUniqueDeviceId(prefix: String, useCache: Boolean): String? {
        if (!useCache) {
            return getUniqueDeviceIdReal(prefix)
        }
        if (udid == null) {
            synchronized(DeviceUtils::class.java) {
                if (udid == null) {
                    val id = SPUtils.getString(KEY_UDID, "")
                    if (id.isNotEmpty()) {
                        udid = id
                        return udid
                    }
                    return getUniqueDeviceIdReal(prefix)
                }
            }
        }
        return udid
    }

    private fun getUniqueDeviceIdReal(prefix: String): String? {
        try {
            val androidId = getAndroidID()
            if (!TextUtils.isEmpty(androidId)) {
                return saveUdid(prefix + 2, androidId)
            }
        } catch (ignore: Exception) { /**/
        }
        return saveUdid(prefix + 9, "")
    }

    private fun saveUdid(prefix: String, id: String?): String? {
        udid = getUdid(prefix, id)
        SPUtils.putString(KEY_UDID, udid!!)
        return udid
    }

    private fun getUdid(prefix: String, id: String?): String {
        return if (id == "") {
            prefix + UUID.randomUUID().toString().replace("-", "")
        } else prefix + UUID.nameUUIDFromBytes(id!!.toByteArray()).toString().replace("-", "")
    }

}