package com.adealink.weparty.account.login.manager

import android.content.Intent
import com.adealink.frame.base.IError
import com.adealink.frame.base.Rlt
import com.adealink.frame.coroutine.dispatcher.Dispatcher
import com.adealink.frame.frame.BaseFrame
import com.adealink.frame.log.Log
import com.adealink.frame.network.data.Res
import com.adealink.frame.router.Router
import com.adealink.frame.util.AppUtil
import com.adealink.weparty.App
import com.adealink.weparty.account.constant.TAG_ACCOUNT_LOGIN
import com.adealink.weparty.account.login.LoginActivity
import com.adealink.weparty.account.login.data.GoogleLoginReq
import com.adealink.weparty.account.login.data.LoginResult
import com.adealink.weparty.account.login.data.TestLoginReq
import com.adealink.weparty.account.login.data.ThirdType
import com.adealink.weparty.account.login.datasource.remote.LoginHttpService
import com.adealink.weparty.account.login.phone.data.MobileData
import com.adealink.weparty.account.login.phone.data.PhoneLoginReq
import com.adealink.weparty.debug.DebugActivity
import com.adealink.weparty.module.account.Account
import com.adealink.weparty.module.account.AccountLocalService
import com.adealink.weparty.module.account.ILoginListener
import com.adealink.weparty.module.account.data.AccountErrorRefreshTokenEmpty
import com.adealink.weparty.module.account.data.AccountErrorTokenRefreshing
import com.adealink.weparty.module.account.data.UserKickReason
import com.adealink.weparty.module.profile.ProfileModule
import com.adealink.weparty.storage.AppPref
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.concurrent.atomic.AtomicBoolean

val loginManager: ILoginManager by lazy { LoginManager() }

class LoginManager : BaseFrame<ILoginListener>(), ILoginManager {

    private val loginHttpService by lazy {
        App.instance.networkService.getHttpService(
            LoginHttpService::class.java
        )
    }
    private var isHandlingKickOut = false
    private val handlingRefreshToken = AtomicBoolean(false)

    private var inviteUidCode: String? = null //邀请UID码
    private var inviteRoomIdCode: String? = null //邀请房间码
    private var shareScene: String? = null //分享场景

    override var uid: String = ""
        set(value) {
            field = value
            AppPref.uid = value
        }
    override var token: String = ""
        set(value) {
            field = value
            AppPref.token = value
        }
    override var isLogin: Boolean = false
        set(value) {
            field = value
            AppPref.isLogin = value
        }

    init {
        isLogin = AppPref.isLogin
        uid = AppPref.uid
        token = AppPref.token
    }

    private fun isCurrentLoginActivity(): Boolean {
        return AppUtil.currentActivity is LoginActivity
    }

    private fun handleKickOut(reason: UserKickReason, tipContent: String? = null) {
        if (isHandlingKickOut) {
            return
        }
        isHandlingKickOut = true
        logout(false)
        if (isCurrentLoginActivity()) {
            isHandlingKickOut = false
            return
        }
        isHandlingKickOut = false
        if (isRunLogin()) return
        Router.build(AppUtil.appContext, Account.Login.PATH)
            .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
            .putExtra(Account.Common.EXTRA_KICK_OUT_REASON, reason.value)
            .putExtra(Account.Common.EXTRA_KICK_OUT_CONTENT, tipContent)
            .start()
    }


    private fun isRunLogin() = AppUtil.currentActivity is DebugActivity

    override suspend fun googleLogin(
        token: String,
    ): Rlt<Res<LoginResult>> {
        Log.d(TAG_ACCOUNT_LOGIN, "googleLogin, token:$token")
        val rlt = loginHttpService.googleLogin(
            GoogleLoginReq(
                token = token
            )
        )
        if (rlt is Rlt.Success) {
            val res = rlt.data.data
            if (res != null) {
                loginSuccess(res, ThirdType.GOOGLE)
                notifyLogin()
            }
        }
        return rlt
    }

    override suspend fun phoneLogin(
        region: String,
        number: String,
        verify: String
    ): Rlt<Res<LoginResult>> {
        Log.d(TAG_ACCOUNT_LOGIN, "phoneLogin, $region, $number, $verify")
        val rlt = loginHttpService.phoneLogin(
            PhoneLoginReq(
                mobile = MobileData(region, number),
                verify
            )
        )
        if (rlt is Rlt.Success) {
            val res = rlt.data.data
            if (res != null) {
                loginSuccess(res, ThirdType.PHONE)
                notifyLogin()
            }
        }
        return rlt
    }

    override suspend fun testLogin(): Rlt<Res<LoginResult>> {
        Log.d(TAG_ACCOUNT_LOGIN, "testLogin")
        val rlt = loginHttpService.testLogin(TestLoginReq())
        if (rlt is Rlt.Success) {
            val res = rlt.data.data
            if (res != null) {
                loginSuccess(res, ThirdType.GOOGLE)
                notifyLogin()
            }
        }
        return rlt
    }

    private fun loginSuccess(result: LoginResult, loginType: ThirdType) {
        val userInfo = result.userInfo
        AppPref.deviceLogined = true
        isLogin = true
        uid = userInfo.uid
        token = result.token
        AppPref.refreshToken = result.token
        ProfileModule.setMyUserInfo(userInfo)
        AccountLocalService.loginType = loginType.value
    }

    override fun getLoginDeviceId(): String {
        val gaId = App.instance.googleService.getGAId()
        if (gaId.isEmpty()) {
            return App.instance.deviceIdService.getDeviceId() ?: ""
        }
        return gaId
    }

    override fun logout(toLogin: Boolean?) {
        isLogin = false
        uid = ""
        token = ""
        // 置空拉新/邀请相关的参数
        inviteUidCode = null
        inviteRoomIdCode = null
        shareScene = null

        LogoutCleaner.clean()

        notifyLogout()
        if (toLogin == true) {
            if (isCurrentLoginActivity()) {
                return
            }
            Router.build(AppUtil.appContext, Account.Login.PATH)
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
                .start()
        }
    }

    override fun onKickedOffline(reason: UserKickReason) {
        launch {
            withContext(Dispatcher.UI) {
                handleKickOut(UserKickReason.REASON_LOCAL_KICK_TOKEN_EXPIRE)
            }
        }
    }

    override fun notifyLogin() {
        dispatch { it.onLogin() }
    }

    override fun notifyLogout() {
        dispatch { it.onLogout() }
    }

    override fun refreshToken() {
        launch {
            suspendRefreshToken()
        }
    }

    private suspend fun suspendRefreshToken(): Rlt<Any> {
        if (!handlingRefreshToken.compareAndSet(false, true)) {
            return Rlt.Failed(AccountErrorTokenRefreshing())
        }

        if (AppPref.refreshToken.isEmpty()) {
            withContext(Dispatcher.UI) {
                logout(false)
                handleKickOut(UserKickReason.REASON_LOCAL_KICK_TOKEN_EXPIRE)
            }
            handlingRefreshToken.set(false)
            return Rlt.Failed(AccountErrorRefreshTokenEmpty())
        }

        var refreshTokenSuccess = false
        var failedRlt = Rlt.Failed(IError())
        loop@ for (i in 1..3) {

            // 期间可能已经退出登录
            if (AppPref.refreshToken.isEmpty()) {
                break@loop
            }

            val rlt = loginHttpService.refreshToken()
            Log.d(TAG_ACCOUNT_LOGIN, "refresh token(${i}), $rlt")
            when (rlt) {
                is Rlt.Success -> {
//                    val newToken = rlt.data.data
//                    if (newToken.isNullOrEmpty()) {
//                        continue@loop
//                    }
                    refreshTokenSuccess = true
//                    token = newToken
//                    Log.d(TAG_ACCOUNT_LOGIN, "refresh token success, new Token:${newToken}")
                    Log.d(TAG_ACCOUNT_LOGIN, "refresh token success")
                    break@loop
                }

                is Rlt.Failed -> {
                    failedRlt = rlt
                }
            }
        }
        handlingRefreshToken.set(false)
        if (refreshTokenSuccess) {
            return Rlt.Success(true)
        }
        return failedRlt
    }

    override fun onTokenExpire() {
        launch {
            when (val refreshTokenRlt = suspendRefreshToken()) {
                is Rlt.Success -> {
                    //ntd.
                }

                is Rlt.Failed -> {
                    withContext(Dispatcher.UI) {
                        logout(false)
                        handleKickOut(UserKickReason.REASON_LOCAL_KICK_TOKEN_EXPIRE)
                    }
                }
            }
        }
    }

    override fun checkRemoteVirtualAppConfig() {
        launch {
            val block = App.instance.securityService.checkRemoteVirtualAppConfig()
            if (block) {
                logout(true)
            }
        }
    }

}

