package com.adealink.weparty.network

import android.app.Application
import com.adealink.frame.coroutine.dispatcher.Dispatcher
import com.adealink.frame.log.Log
import com.adealink.frame.network.IConnectStateListener
import com.adealink.frame.network.ISocket
import com.adealink.frame.network.stat.NetQualityStatEvent
import com.adealink.frame.network.util.removeNetworkCallbacks
import com.adealink.frame.network.util.runNetworkThread
import com.adealink.frame.util.ActivityLifecycleCallbacksExt
import com.adealink.frame.util.AppUtil
import com.adealink.frame.util.OnNetworkListener
import com.adealink.frame.util.registerNetworkListener
import com.adealink.frame.util.unregisterNetworkListener
import com.adealink.weparty.App
import com.adealink.weparty.module.account.AccountModule
import com.adealink.weparty.module.account.ILoginListener
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

/**
 * 网络管理器
 */
class NetworkManager : INetworkManager, ILoginListener, IConnectStateListener,
    OnNetworkListener, ActivityLifecycleCallbacksExt,
    CoroutineScope by CoroutineScope(SupervisorJob() + Dispatcher.WENEXT_THREAD_POOL) {

    companion object {
        private const val TAG_NETWORK = "tag_network"
        private const val TAG_PING = "tag_ping"
        private const val PING_DEFAULT_INTERVAL = 10 * 1000L
        private const val RTC_LEAVE_CHANNEL_DELAY = 20_000L
    }

    // TODO: ping接口修改为socket接口
//    private val pingSocketService by lazy {
//        App.instance.networkService.getSocketService(PingSocketService::class.java)
//    }

    private var pingInterval = PING_DEFAULT_INTERVAL
    private val pingRunnable by lazy {
        Runnable {
            ping()
            startPing(pingInterval)
        }
    }
    private var netQualityStatEvent: NetQualityStatEvent? = null

    private var rtcLeaveChannelJob: Job? = null

    override fun init(application: Application) {
        AccountModule.registerListener(this)
        if (AccountModule.isLogin()) {
            handleLogin()
        }
        ConnectionStatusManager.addReconnectClickListener(
            ConnectType.BIZ,
            this::socketConnect
        )
    }

    private fun handleLogin() {
        Log.d(TAG_NETWORK, "handleLogin")
        netQualityStatEvent = NetQualityStatEvent(NetQualityStatEvent.Action.RTT)
        App.instance.networkService.registerConnectStateListener(this)
        socketConnect()
        AppUtil.registerActivityLifecycleCallbacks(this)
        registerNetworkListener(this)
    }

    override fun onLogin() {
        super.onLogin()
        handleLogin()
    }

    override fun onLogout() {
        super.onLogout()
        Log.d(TAG_NETWORK, "onLogout")
        netQualityStatEvent?.send()
        netQualityStatEvent = null
        App.instance.networkService.disConnect(ISocket.CloseCode.NORMAL)
        App.instance.networkService.unregisterConnectStateListener(this)
        AppUtil.unregisterActivityLifecycleCallbacks(this)
        unregisterNetworkListener(this)
        ConnectionStatusManager.removeDisconnectFloatView()
        stopPing()
    }

    override fun onConnectStateChanged(
        connectState: ISocket.ConnectState,
        reason: ISocket.ConnectStateReason
    ) {
        Log.i(TAG_NETWORK, "onConnectStateChanged, connectState:${connectState.name}")
        if (connectState == ISocket.ConnectState.DISCONNECT) {
            ConnectionStatusManager.notifyConnectStatus(ConnectType.BIZ, ConnectStatus.Disconnected)
            stopPing()
            if (rtcLeaveChannelJob == null) {
                rtcLeaveChannelJob = launch {
                    /*
                     * 业务socket断开后，延迟一段时间后离开rtc房间
                     * 防止用户使用另外的设备登录后, 导致后端检查不到幽灵麦的情况
                     */
                    delay(RTC_LEAVE_CHANNEL_DELAY)
                    // TODO: 网络离线
                    //RoomModule.tryLeaveChannel(LeaveChannelReason.SOCKET_CLOSED)
                }
            }
        } else {
            ConnectionStatusManager.notifyConnectStatus(ConnectType.BIZ, ConnectStatus.ConnectingOrConnected)
            //正在连接或已连接
            if (connectState == ISocket.ConnectState.CONNECTED) {
                startPing(0)

                rtcLeaveChannelJob?.cancel()
                rtcLeaveChannelJob = null
                // TODO: 网络恢复,重连
//                launch {
//                    val rlt = RoomModule.joinChannel(JoinChannelReason.SOCKET_RECONNECTED)
//                    if (rlt is Rlt.Failed) {
//                        Log.i(TAG_NETWORK, "joinChannel failed", rlt)
//                    }
//                }
            }

        }
    }

    private fun ping() {
        // TODO: 补充ping逻辑
//        launch {
//            Log.d(TAG_PING, "ping")
//            PingStatEvent(PingStatEvent.Action.PING).send()
//            val startTime = SystemClock.elapsedRealtime()
//            when (val result = pingSocketService.ping(
//                PingReq(
//                    hashMapOf<Int, String>().apply {
//                        this[0] = if (AppUtil.background) "0" else "1"
//                        this[1] = App.instance.mediaManager.getJoinedMediaRoomId()?.toString() ?: ""
//                    },
//                    App.instance.mediaManager.getSupportRtc()
//                )
//            )
//                .apply { Log.logRltD(TAG_PING, "ping res, ", this) }
//            ) {
//                is Rlt.Success -> {
//                    netQualityStatEvent?.recordRtt(SystemClock.elapsedRealtime() - startTime)
//                    pingInterval = result.data.data?.interval ?: PING_DEFAULT_INTERVAL
//                    val inRoomId = result.data.data?.inRoomId ?: 0
//                    if (inRoomId == 0L && App.instance.mediaManager.getJoinedMediaRoomId() != null) {
//                        //房间已掉线，客户端发起重新进房
//                        App.instance.mediaManager.rejoinRoom()
//                    }
//                }
//
//                is Rlt.Failed -> {
//                    netQualityStatEvent?.recordFailed()
//                }
//            }
//        }
    }

    private fun startPing(interval: Long) {
        Log.d(TAG_PING, "startPing, pingInterval:$interval")
        removeNetworkCallbacks(pingRunnable)
        runNetworkThread(pingRunnable, interval)
    }

    private fun stopPing() {
        Log.d(TAG_PING, "stopPing")
        removeNetworkCallbacks(pingRunnable)
    }

    override fun onNetChanged(available: Boolean) {
        Log.d(TAG_NETWORK, "onNetChanged, available:$available")
        if (available) {
            socketConnect()
        }
    }

    override fun onEnterForeGround() {
        super.onEnterForeGround()
        Log.d(TAG_NETWORK, "onEnterForeGround")
        socketConnect()
    }

    private fun socketConnect() {
//        if (!AccountModule.isLogin()) {
//            return
//        }
//
//        if (AppUtil.background) {
//            Log.i(TAG_NETWORK, "socketConnect, background")
//            return
//        }
//
//        App.instance.networkService.connect()
    }

    override fun pingNow() {
        Log.d(TAG_PING, "pingNow")
        startPing(interval = 0)
    }

}