package com.example.vastlib.service

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.os.Build
import android.os.Handler
import android.os.HandlerThread
import androidx.annotation.RequiresApi
import com.example.vastlib.utils.LogUtils
import com.example.vastlib.utils.NetworkManager

class NetworkController private constructor() : ConnectivityManager.NetworkCallback() {
    private lateinit var context: Context
    var switchSuccess = false
    private var connManager: ConnectivityManager? = null
    private var networkType: Int? = null
    private var activeNetwork: Network? = null
    private var handlerThread: HandlerThread? = null

    fun init(context: Context):NetworkController {
        this.context = context
        return this
    }

    companion object {
        @SuppressLint("StaticFieldLeak")
        private var instance: NetworkController? = null
        private val lock: Any = Any()
        fun getInstance(): NetworkController = instance ?: synchronized(lock) {
            instance ?: NetworkController().apply {
                instance = this
            }
            return instance!!
        }
    }

    fun restore() {
        onUnavailable()
    }

    fun switchNetworkToGprs() {
        LogUtils.info("${System.currentTimeMillis()} start try switch network to gprs")
        switchSuccess = false
        connManager =
            (context.getSystemService(Context.CONNECTIVITY_SERVICE) as? ConnectivityManager)
        activeNetwork = connManager?.activeNetwork
        networkType = connManager?.getNetworkInfo(activeNetwork)?.type
        val builder = NetworkRequest.Builder().apply {
            addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
            addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
        }
        val delay = 10_000
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            instance?.let {
                connManager?.requestNetwork(
                    builder.build(),
                    it,
                    Handler(HandlerThread("" + System.currentTimeMillis()).apply {
                        handlerThread = this
                        start()
                    }.looper)
                )
            }
        } else {
            kotlin.runCatching {
                val clazz = Class.forName("android.net.ConnectivityManager")
                val method = clazz.getDeclaredMethod(
                    "requestNetwork",
                    NetworkRequest::class.java,
                    ConnectivityManager::class.java,
                    Int::class.java
                )
                method.isAccessible = true
                method.invoke(connManager, builder.build(), instance, delay)
            }.onFailure {
                LogUtils.error(it)
                instance?.let { it1 -> connManager?.requestNetwork(builder.build(), it1) }
            }
        }
    }

    override fun onAvailable(network: Network) {
        super.onAvailable(network)
        activeNetwork = network
        switchSuccess =
            connManager?.let { NetworkManager.bindProcessToNetwork(it, network) } == true
        networkType = connManager?.getNetworkInfo(network)?.type
        LogUtils.info("${System.currentTimeMillis()} switch network result $switchSuccess network type $networkType")
    }

    override fun onUnavailable() {
        super.onUnavailable()
        kotlin.runCatching {
            LogUtils.info("${System.currentTimeMillis()} restore network to normal")
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                connManager?.unregisterNetworkCallback(this)
            }
            if (switchSuccess) {
                NetworkManager.repairNetwork(context)
            }
            handlerThread?.quitSafely()
        }.onFailure {
            LogUtils.error(it)
        }
    }
}