package com.github.shadowsocks.http

import com.ccnet.acc.NetworkAdapter
import com.github.shadowsocks.bg.AuthManager
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import timber.log.Timber
import java.io.IOException
import java.net.InetAddress
import java.net.Socket
import java.security.SecureRandom
import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager

object HttpUtils {

    val okHttpClient: OkHttpClient
    val gson: Gson

    init {
        okHttpClient = OkHttpClient().newBuilder()
            .readTimeout(60, TimeUnit.SECONDS)//设置读取超时时间
            .writeTimeout(60, TimeUnit.SECONDS)//设置写的超时时间
            .connectTimeout(60, TimeUnit.SECONDS)//设置连接超时时间
            //https支持
            .hostnameVerifier { hostname, session -> true }
            .sslSocketFactory(initSSLSocketFactory(), initTrustManager())
            .build()
        gson = Gson()
    }

    val jsonType get() = "application/json; charset=utf-8".toMediaTypeOrNull()

    inline fun <reified T> postSync(
        url: String,
        token: String?,
        params: Any
    ): ApiResult<T> {
        val body: RequestBody = gson.toJson(params).toString().toRequestBody(jsonType)
        val request = Request.Builder()
            .url(if (url.startsWith("http")) url else HttpConfig.baseUrl + url)
            .post(body)
        token?.let {
            request.addHeader("Authorization", it)
        }
        val call = okHttpClient.newCall(request.build())
        val resultType = object : TypeToken<ApiResult<T>>() {}.type
        try {
            call.execute().use { response ->
                val string = response.body?.string()
                println(string)
                return gson.fromJson(string, resultType)
            }
        } catch (e: IOException) {
            Timber.e(e, "AuthManager 调用接口错误")
            return ApiResult(ApiError.NetworkError)
        }
    }

    private fun initSSLSocketFactory(): SSLSocketFactory {
        var sslContext: SSLContext? = null
        try {
            sslContext = SSLContext.getInstance("SSL")
            val xTrustArray = arrayOf(initTrustManager())
            sslContext.init(
                null,
                xTrustArray, SecureRandom()
            )
        } catch (e: Exception) {
            e.printStackTrace()
        }

        return AutoSwitchNetworkSocketFactory(sslContext!!.socketFactory)
    }

    private class AutoSwitchNetworkSocketFactory(private val delegate: SSLSocketFactory) :
        SSLSocketFactory() {
        override fun createSocket(
            s: Socket?,
            host: String?,
            port: Int,
            autoClose: Boolean
        ): Socket {
            val bindNetwork = NetworkAdapter.bindNetwork
            val createSocket = delegate.createSocket(s, host, port, autoClose)
            bindNetwork?.bindSocket(createSocket)
            return createSocket
        }

        override fun createSocket(host: String?, port: Int): Socket {
            val bindNetwork = NetworkAdapter.bindNetwork
            return if (bindNetwork != null) {
                bindNetwork.socketFactory.createSocket(host, port)
            } else {
                delegate.createSocket(host, port)
            }
        }

        override fun createSocket(
            host: String?,
            port: Int,
            localHost: InetAddress?,
            localPort: Int
        ): Socket {
            val bindNetwork = NetworkAdapter.bindNetwork
            return if (bindNetwork != null) {
                bindNetwork.socketFactory.createSocket(host, port, localHost, localPort)
            } else {
                delegate.createSocket(host, port, localHost, localPort)
            }
        }

        override fun createSocket(host: InetAddress?, port: Int): Socket {
            val bindNetwork = NetworkAdapter.bindNetwork
            return if (bindNetwork != null) {
                bindNetwork.socketFactory.createSocket(host, port)
            } else {
                delegate.createSocket(host, port)
            }
        }

        override fun createSocket(
            address: InetAddress?,
            port: Int,
            localAddress: InetAddress?,
            localPort: Int
        ): Socket {
            val bindNetwork = NetworkAdapter.bindNetwork
            return if (bindNetwork != null) {
                bindNetwork.socketFactory.createSocket(address, port, localAddress, localPort)
            } else {
                delegate.createSocket(address, port, localAddress, localPort)
            }
        }

        override fun getDefaultCipherSuites(): Array<String> {
            return delegate.defaultCipherSuites
        }

        override fun getSupportedCipherSuites(): Array<String> {
            return delegate.supportedCipherSuites
        }

    }

    private fun initTrustManager(): X509TrustManager {
        return object : X509TrustManager {
            override fun getAcceptedIssuers(): Array<X509Certificate> {
                return arrayOf()
            }

            override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {
            }

            override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {
            }
        }
    }

}