package com.doverfuelingsolutions.issp.fusion

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import com.doverfuelingsolutions.issp.R
import com.doverfuelingsolutions.issp.api.FuelInfoApi
import com.doverfuelingsolutions.issp.api.dto.DFSResult
import com.doverfuelingsolutions.issp.api.entity.NozzleInfo
import com.doverfuelingsolutions.issp.data.GlobalData
import com.doverfuelingsolutions.issp.fusion.callback.OnFusionStatus
import com.doverfuelingsolutions.issp.utils.StringUtil
import com.doverfuelingsolutions.issp.utils.ValidateUtil
import com.wayne.www.waynelib.util.log.DFSLog
import com.doverfuelingsolutions.issp.utils.sp.SPKeys
import com.doverfuelingsolutions.issp.utils.sp.SPUtil
import com.doverfuelingsolutions.issp.utils.thread.ThreadUtil
import com.doverfuelingsolutions.issp.view.fragment.callback.RefreshNozzle
import com.doverfuelingsolutions.issp.view.fragment.callback.RefreshOrder
import com.wayne.www.waynelib.fdc.FdcClient
import com.wayne.www.waynelib.fdc.OnFdcClientStateChangedListener
import com.wayne.www.waynelib.fdc.OnFdcMessageReceivedListener
import com.wayne.www.waynelib.fdc.OnFdcServiceResponseReceivedListener
import com.wayne.www.waynelib.fdc.message.*
import com.wayne.www.waynelib.util.HsLogTagFlag
import com.wayne.www.waynelib.util.HsLogUtils
import kotlinx.coroutines.*
import java.util.*
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import kotlin.math.min

object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
    OnFdcServiceResponseReceivedListener,
    OnFdcMessageReceivedListener {

    private var isInitialized = false
    var stateFusion: FdcClient.FdcClientState = FdcClient.FdcClientState.Stopped
    private const val timeoutMax = 4000
    private var reconnectTime = 0
    private var isKeepDisconnected = false

    var isLogin = false
        private set
    var isFetchPumpInfo = false
        private set
    val nozzles = arrayListOf<NozzleInfo>()

    private val coroutineIO = CoroutineScope(Dispatchers.IO)
    private var jobRestart: Job? = null

    var onFusionStatus: OnFusionStatus? = null
    var onRefreshNozzleUi: RefreshNozzle? = null
    var onRefreshOrderUi: RefreshOrder? = null

    override fun onFdcClientStateChanged(sender: FdcClient?, state: FdcClient.FdcClientState?) {
        DFSLog.d("Fusion state changed: ${state?.name?.toLowerCase(Locale.CHINESE)}")
        if (sender == null || state == null) return

        stateFusion = state
        // Reset reconnectTime.
        if (state != FdcClient.FdcClientState.Connecting && state != FdcClient.FdcClientState.MyAddReConnect) {
            isKeepDisconnected = false
            if (reconnectTime > 0) {
                reconnectTime = 0
            }
        }
        when (state) {
            FdcClient.FdcClientState.Connected -> {
                coroutineIO.launch {
                    val deferred1 = async { loginToFusion() }
                    val deferred2 = async { getPumpInfo() }
                    val result1 = deferred1.await()
                    val result2 = deferred2.await()

                    if (result1 && result2.success) {
                        onFusionStatus?.onFusionStatus(FusionStatus.Connected)
                    } else {
                        onFusionStatus?.onFusionStatus(FusionStatus.ConnectedWithoutInfo)
                    }
                }
            }
            FdcClient.FdcClientState.Connecting -> {
                onFusionStatus?.onFusionStatus(FusionStatus.Connecting)
            }
            FdcClient.FdcClientState.DisconnectedByHeartbeat -> {
                onFusionStatus?.onFusionStatus(FusionStatus.Connecting)
                coroutineIO.launch {
                    // Try restart if current client remains Connecting or DisconnectedByHeartbeat after delay.
                    delay(2000)
                    if (stateFusion == FdcClient.FdcClientState.Connecting || stateFusion == FdcClient.FdcClientState.DisconnectedByHeartbeat) {
                        // Result of restart has to be MyAddReConnect or Connected so the custom Connecting status triggered above can't last forever.
                        DFSLog.i("Try restart fusion client as current client remains Connecting or DisconnectedByHeartbeat after delay")
                        restart()
                    }
                }
            }
            FdcClient.FdcClientState.Stopped -> {
                // Try restart if client is not stopped manually.
                if (isInitialized) {
                    onFusionStatus?.onFusionStatus(FusionStatus.Connecting)
                    // Result of restart has to be MyAddReConnect or Connected.
                    restart()
                }
            }
            FdcClient.FdcClientState.MyAddReConnect -> {
                // Status MyAddReConnect means client socket has been closed.
                // Restart might or might not works.
                // Prevent infinite loop cause restart could also trigger MyAddReConnect.
                jobRestart = coroutineIO.launch {
                    isKeepDisconnected = true

                    val delayTime = reconnectTime * 10L
                    reconnectTime = min(reconnectTime + 1, 6)
                    if (delayTime > 0) {
                        onFusionStatus?.onFusionStatus(FusionStatus.Disconnected)
                        delay(delayTime * 1000)
                    }
                    if (isKeepDisconnected) {
                        DFSLog.d("Try to restart 'Disconnected' fusion client.")
                        onFusionStatus?.onFusionStatus(FusionStatus.Connecting)
                        restart()
                    }
                }
            }
        }
    }

    override fun onServiceResponseReceived(sender: FdcClient?, serviceResponse: ServiceResponse?) {
//        if (serviceResponse == null) return
//        HsLogUtils.o(HsLogTagFlag.FDC_TAG, serviceResponse)
//        HsLogUtils.o(
//            HsLogTagFlag.FDC_TAG,
//            "====================================================================================================================================="
//        )
//        if ("AuthentificationError" == serviceResponse.overallResult) {
//            HsLogUtils.o("AuthentificationError", "restart")
//            coroutineIO.launch {
//                sender?.restartAsync()
//            }
//        }
//        if (serviceResponse.singleDeviceClass == null) return
//        when (serviceResponse.requestType) {
//            "ClearFuelSaleTrx" ->                 //支付成功
//                if ("Success" == serviceResponse.overallResult) { //清除成功，则移除
//                    val dc = serviceResponse.singleDeviceClass
////                    HsSpUtils.removeClearOrders(dc)
//                    // 支付成功后，移除本地云订单，清单
//                    GlobalScope.launch(Dispatchers.IO) {
//                        SPUtil.removeLockOrder(dc)
//                        val resultClear = clearOrder(dc)
//                        if (resultClear.success) {
//                            dc.state = "Cleared"
//                            SPUtil.removeUnclearedOrder(dc)
//                            SPUtil.removeWayneOrder(dc)
//                        } else {
//                            // 失败则记下来，后续再尝试消单
//                            SPUtil.addUnclearedOrder(dc)
//                        }
//
//                    }
//                }
//            "LockFuelSaleTrx" -> {
//                //锁定成功
//                if ("Success" == serviceResponse.overallResult) {
//                    val dc = serviceResponse.singleDeviceClass
////                    HsSpUtils.saveLastLockOrder(dc) //当前正在操作的订单，主要怕收到锁命令时把他锁了
////                    HsSpUtils.saveNeedUnLockOrder(dc)
//                    SPUtil.addLockOrder(dc)
//                    return
//                }
//            }
//            "UnlockFuelSaleTrx" -> if ("Success" == serviceResponse.overallResult) {
//                //解锁成功
////                HsSpUtils.removeUnLockSuccessOrder(serviceResponse.singleDeviceClass)
//                val dc = serviceResponse.singleDeviceClass
//                SPUtil.removeLockOrder(dc)
//            }
//        }
    }

    override fun onFdcMessageReceived(sender: FdcClient?, fdcMessage: FdcMessage?) {
        if (fdcMessage == null || fdcMessage is FdcMessageHeartbeat
            || fdcMessage is FdcMessageFuelPointCurrentFuellingStatus
            || fdcMessage is FdcMessageFPStateChange
        ) return
        HsLogUtils.o(HsLogTagFlag.FDC_TAG, fdcMessage.toString())
        HsLogUtils.o(HsLogTagFlag.FDC_TAG, fdcMessage)
        if (fdcMessage.messageType == "FuelSaleTrx" && fdcMessage is FdcMessageFuelSaleTrx) {
            val ffst = fdcMessage as FdcMessageFuelSaleTrx
            if (ffst.singleDeviceClass == null) return
            val dc = ffst.singleDeviceClass

            //获取到的 dc 中 releaseTokenAttribute 为 null ,而 releaseTokenElement 不为 null ，将其值赋予 releaseTokenAttribute
            //两者意义相同，这里赋予 releaseTokenAttribute 是让下面 SPUtil.removexxx 能正常找到订单，进而删除
            if (dc.releaseTokenAttribute == null && dc.releaseTokenElement != null) dc.releaseTokenAttribute = dc.releaseTokenElement
            if (dc.releaseTokenElement == null && dc.releaseTokenAttribute != null) dc.releaseTokenElement = dc.releaseTokenAttribute
            // 到这获取到更新的订单——或全新，或旧订单更新，或旧订单删除

            if (null != dc.state) {
                when (dc.state) {
                    "Payable" -> {  // 新的订单，可以支付
                        //将订单页面增加此可支付订单
//                        DFSLog.i("订单订单订单",dc.toString())

                        //----------------------------------------------------
                        var isNew = true
                        val orders = SPUtil.getOrderListByPump(dc.pumpNo)
                        orders.forEach {
                            if (it.pumpNo == dc.pumpNo && it.releaseTokenElement == dc.releaseTokenElement  && it.transactionSeqNo == dc.transactionSeqNo) {
                                it.state = dc.state
                                isNew = false
                            }
                        }
                        if (isNew) {
                            //这里获取到的 releaseTokenAttribute 为 null,但其等同于 releaseTokenElement
                            dc.releaseTokenAttribute = dc.releaseTokenElement
                            val nozzle = nozzles.find { it.pumpId == dc.pumpNo && it.logicId == dc.nozzleNo }
                            nozzle?.let {
                                dc.nozzleNo = it.physicalId
                                dc.logicNo = it.logicId
                                dc.productName = it.barcodeName
                            }
                            orders.add(0,dc)

                            SPUtil.setOrderListByPump(dc.pumpNo, orders)
                            onRefreshOrderUi?.onRefreshOrderUi(dc.pumpNo,orders) //更新订单页面
                        } else {
                            SPUtil.setOrderListByPump(dc.pumpNo,orders)
                            onRefreshOrderUi?.onRefreshOrderUi(dc.pumpNo,orders) //更新订单页面
                        }
                        onRefreshNozzleUi?.onRefreshNozzleUi(dc.pumpNo, true)//不管是不是新订单，都表示这把枪有单，需高亮显示

                    }
                    "Cleared" -> if (null != dc.pumpNo && null != dc.transactionSeqNo) { //需要消除的单（一般是已经支付过了，或本机支付的，或其他设备支付的）

                        SPUtil.removeUnclearedOrder(dc)
                        SPUtil.removeWayneOrder(dc)
                        SPUtil.removeLockOrder(dc)

                        //将订单页面减去此需消除订单
                        val orders = SPUtil.getOrderListByPump(dc.pumpNo)
                        val result = orders.remove(orders.find { it.pumpNo == dc.pumpNo && it.releaseTokenElement == dc.releaseTokenElement && it.transactionSeqNo == dc.transactionSeqNo })
                        SPUtil.setOrderListByPump(dc.pumpNo,orders)
                        onRefreshNozzleUi?.onRefreshNozzleUi(dc.pumpNo, orders.isNotEmpty())   //枪页面显示浅色
                        onRefreshOrderUi?.onRefreshOrderUi(dc.pumpNo,orders)

                        if (!result) {
                            val findOrder = "${dc.pumpNo}-${dc.releaseTokenElement}-${dc.transactionSeqNo}-${dc.releaseTokenAttribute}"
                            val ordersStr = orders.map { "${it.pumpNo}-${it.releaseTokenElement}-${it.transactionSeqNo}-${it.releaseTokenAttribute}" }
                            DFSLog.i("查找的订单：${findOrder}","当前订单：${ordersStr}")
                        }

                    }
                    "Locked" -> if (null != dc.pumpNo && null != dc.transactionSeqNo) { // 锁单（被某一设备正在支付），或本机锁，或其他设备锁
//                        DFSLog.i("订单订单订单",dc.toString())
                        //---------------------------------------------------------

                        val order = SPUtil.getOrderListByPump(dc.pumpNo)
                        order.find { it.pumpNo == dc.pumpNo && it.releaseTokenElement == dc.releaseTokenElement  && it.transactionSeqNo == dc.transactionSeqNo }?.let {
                            it.state = dc.state
                            it.lockingApplicationSender = dc.lockingApplicationSender
                        }
                        SPUtil.setOrderListByPump(dc.pumpNo, order)
                        onRefreshOrderUi?.onRefreshOrderUi(dc.pumpNo,order)

                    }
                }
            }
        }
    }

    // 更新本地订单存储
    private fun updateLocalOrder(dc: DeviceClass){
        val pumpSourceOrder = SPUtil.getOrderListByPump(dc.pumpNo)

        var isExist = false
        pumpSourceOrder.forEach {
            if (it.transactionSeqNo.equals(dc.transactionSeqNo)) { //存在更新数据
                isExist = true
                it.state = dc.state
                it.releaseTokenAttribute = dc.releaseTokenAttribute
                it.releaseTokenElement = dc.releaseTokenElement
            }
        }
        if (!isExist) pumpSourceOrder.add(dc) //不存在添加

        SPUtil.setOrderListByPump(dc.pumpNo, pumpSourceOrder)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun initialize() {
        DFSLog.i("FusionManager is initializing.")
        isInitialized = true
        FdcClient.getDefault().addOnFdcClientStateChangedListeners(this)
        FdcClient.getDefault().addOnFdcServiceResponseReceivedListeners(this)
        FdcClient.getDefault().addOnFdcMessageReceivedListeners(this)
        FdcClient.getDefault().start(
            SPUtil.getString(SPKeys.MIDDLE_IP),
            SPUtil.getString(SPKeys.MIDDLE_PORT).toInt(),
            GlobalData.serialNumber.get(),
            SPUtil.getString(SPKeys.MIDDLE_WORKSTATION_ID).toInt(),
            null
        )
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun close() {
        DFSLog.i("FusionManager is closing.")
        isInitialized = false
        isLogin = false
        isFetchPumpInfo = false

        val manager = this
        coroutineIO.launch {
            logout()
            FdcClient.getDefault().removeOnFdcClientStateChangedListeners(manager)
            FdcClient.getDefault().removeOnFdcServiceResponseReceivedListeners(manager)
            FdcClient.getDefault().removeOnFdcMessageReceivedListeners(manager)
            FdcClient.getDefault().stop()
        }
    }

    /**
     * Restart fusion client.
     *
     * @param immediate FusionManager has an internal restart loop when client remains disconnected. This restart loop will be broken as 'immediate' is true.
     */
    fun restart(immediate: Boolean = false) {
        DFSLog.d("Fusion client is restarting.")
        if (immediate) {
            isKeepDisconnected = false
            reconnectTime = 0
            if (jobRestart?.isActive == true) jobRestart?.cancel()
        }
        coroutineIO.launch {
            close()
            delay(timeoutMax.toLong() + 1000L)
            initialize()
        }
    }

    /**
     * Login to Fusion and retry if first login failed.
     */
    suspend fun loginToFusion(): Boolean = suspendCoroutine {
        coroutineIO.launch {
            isLogin = if (login()) true else {
                DFSLog.d("Fusion: retry login after logout")
                logout()
                login()
            }
            it.resume(isLogin)
        }
    }

    /**
     * Fetch all uncleared order.
     * @param pumpId -1 means all pumps, others mean specified pump.
     */
    suspend fun getAllUnsettledOrder(pumpId: Int = -1) = suspendCoroutine<DFSResult<List<DeviceClass>>> {
        ThreadUtil.io {
            val time1 = System.currentTimeMillis()
            FdcClient.getDefault().sendGetAvailableFuelSaleTrxs(pumpId, { _, response ->
                val time2 = System.currentTimeMillis()
                DFSLog.v(
                    "fetch uncleared orders of pump $pumpId (cost ${time2 - time1}ms)",
                    response
                )
                coroutineIO.launch {
                    if (response == null || response !is ServiceResponseGetAvailableFuelSaleTrxs) {
                        it.resume(DFSResult.fail(R.string.fail_get_order))
                    } else if (response.singleFdcData == null || response.singleDeviceClass == null) {
                        it.resume(DFSResult.success(emptyList()))
                    } else {
                        val unsettledList =
                            response.singleFdcData.deviceClasses.filter { !it.isClear }

//                        val resultDetailList = unsettledList
//                            .map { dc -> async { getOrderDetail(dc) } }
//                            .map { deferred -> deferred.await() }
//                            .filter { it.success }
//                            .mapNotNull {
//                                it.data?.productName =
//                                    nozzles.find { n -> n.pumpId == it.data?.pumpNo && it.data.nozzleNo == n.physicalId }?.barcodeName
//                                        ?: ""
//                                it.data
//                            }
                        val resultDetailList = arrayListOf<DeviceClass>()
                        unsettledList.forEach {
                            val detail = getOrderDetail(it)
                            if (detail.success) {
                                detail.data?.productName =
                                    nozzles.find { n -> n.pumpId == detail.data?.pumpNo && detail.data.nozzleNo == n.physicalId }?.barcodeName
                                        ?: ""
                                detail.data?.let { it1 -> resultDetailList.add(it1) }
                            }
                        }
                        val time3 = System.currentTimeMillis()
                        DFSLog.v("fetch uncleared orders detail cost ${time3 - time2}ms")
                        it.resume(DFSResult.success(resultDetailList))
                    }
                }
            }, 8000)
        }
    }

    /**
     * Fetch order detail.
     */
    private suspend fun getOrderDetail(deviceClass: DeviceClass) = suspendCoroutine<DFSResult<DeviceClass>> {
            getOrderDetailAsync(deviceClass) { asyncResult ->
                it.resume(asyncResult)
            }
        }

    /**
     * Fetch order detail.
     */
    private fun getOrderDetailAsync(
        dcRaw: DeviceClass,
        callback: (result: DFSResult<DeviceClass>) -> Unit
    ) {
        ThreadUtil.io {
            FdcClient.getDefault().sendGetFuelSalesTrxDetailsRequestAsync(
                dcRaw.pumpNo,
                dcRaw.transactionSeqNo,
                dcRaw.releaseTokenAttribute,
                { _, response ->
                    DFSLog.v(
                        "fetch order detail of ${dcRaw.pumpNo}-${dcRaw.transactionSeqNo}",
                        response
                    )
                    if (response == null || response !is ServiceResponseGetFuelSalesTrxDetails || response.fdcData.isEmpty() || response.fdcData[0].deviceClasses.isEmpty()) {
                        callback(DFSResult.fail(R.string.fail_get_order_detail))
                    } else {
                        val dc = response.fdcData[0].deviceClasses[0]
                        // 迷惑的数据需要 hack 一下
                        dc.releaseTokenAttribute = dcRaw.releaseTokenAttribute
                        val correctNozzle = nozzles.find {
                            it.pumpId == dc.pumpNo && it.logicId == dc.nozzleNo
                        }
                        correctNozzle?.let {
                            dc.nozzleNo = it.physicalId
                            dc.logicNo = it.logicId
                        }
                        callback(DFSResult.success(dc))
                    }
                },
                3000
            )
        }
    }

    /**
     * Clear order in server. Also remember mark order status as 'Cleared'.
     */
    suspend fun clearOrder(dc: DeviceClass): DFSResult<Boolean> {
        return if (dc.isClear) {
            DFSResult.success(true)
        } else if (dc.releaseTokenAttribute == null || !ValidateUtil.isPositiveInt(dc.releaseTokenAttribute)) {
            DFSResult.fail(R.string.wrong_order_info)
        } else {
            clearOrder(dc.pumpNo, dc.transactionSeqNo, dc.releaseTokenAttribute.toInt())
        }
    }

    suspend fun clearOrder(pumpId: Int, transactionNo: String, token: Int) = suspendCoroutine<DFSResult<Boolean>> {
            ThreadUtil.io {
                FdcClient.getDefault()
                    .sendClearFuelSaleTrxRequestAsync(pumpId, transactionNo, token, { _, response ->
                        if (response != null && response.singleDeviceClass != null && response.overallResult.equals(
                                "Success",
                                true
                            )
                        ) {
                            DFSLog.i("clear order[$pumpId-$transactionNo-$token] succeed")
                            it.resume(DFSResult.success(true))
                        } else {
                            DFSLog.e("FusionManager.clearOrder failed", response)
                            it.resume(DFSResult.fail(R.string.fail_lock_order))
                        }
                    }, 6000)
            }
        }

    /**
     * Lock or unlock order.
     */
    suspend fun lockOrder(dc: DeviceClass, lock: Boolean): DFSResult<Boolean> {
        return if (dc.isLock == lock) {
            DFSResult.success(true)
        } else if (dc.releaseTokenAttribute == null || !ValidateUtil.isPositiveInt(dc.releaseTokenAttribute)) {
            DFSResult.fail(R.string.wrong_order_info)
        } else {
            lockOrder(dc.pumpNo, dc.transactionSeqNo, dc.releaseTokenAttribute.toInt(), lock)
        }
    }

    suspend fun lockOrder(pumpId: Int, transactionNo: String, token: Int, lock: Boolean) = suspendCoroutine<DFSResult<Boolean>> {
            ThreadUtil.io {
                val callback = OnFdcServiceResponseReceivedListener { _, response ->
                    DFSLog.v("Fusion.lockOrder($pumpId, $transactionNo, $token, $lock)", response)
                    /**
                    when {
                        response != null && response.singleDeviceClass != null && response.overallResult.equals(
                            "Success",
                            true
                        ) -> {
                            it.resume(DFSResult.success(true))
                        }

                        response != null && response.singleDeviceClass != null && response.overallResult.equals(
                            "Failure",
                            true
                        ) -> {
                            if (lock && response.singleDeviceClass.lockingApplicationSender == GlobalData.workStationId.get()) {
                                it.resume(DFSResult.success(true))
                            } else {
                                it.resume(
                                    DFSResult.fail(
                                        response.overallResult ?: StringUtil.get(R.string.fail_operate)
                                    )
                                )
                            }
                        }

                        response == null -> {
                            it.resume(DFSResult.fail("响应超时"))
                        }
                    }
                    */

                    if (response != null && response.singleDeviceClass != null && response.overallResult.equals(
                            "Success",
                            true
                        )) {
                        it.resume(DFSResult.success(true))
                    } else {
                        it.resume(
                            DFSResult.fail(
                                response?.overallResult ?: StringUtil.get(R.string.fail_operate)
                            )
                        )
                    }

                }
                if (lock) {
                    FdcClient.getDefault().sendLockFuelSaleTrxRequestAsync(
                        pumpId,
                        transactionNo,
                        token,
                        callback,
                        6000
//                    10
                    )
                } else {
                    FdcClient.getDefault().sendUnLockFuelSaleTrxRequestAsync(
                        pumpId,
                        transactionNo,
                        token,
                        callback,
                        6000
//                        10
                    )
                }
            }
        }

    /**
     * Proxy method to get pump and nozzle data.
     */
    suspend fun getPumpInfo(): DFSResult<List<NozzleInfo>> {
        val resultNozzles = if (SPUtil.getBoolean(SPKeys.USE_FCC2)) {
            FuelInfoApi.requestPumpInfoFromFCC2()
        } else {
            FuelInfoApi.requestPumpInfo()
        }
        if (resultNozzles.success && resultNozzles.data != null) {
            isFetchPumpInfo = true
            if (nozzles.isNotEmpty()) nozzles.clear()
            nozzles.addAll(resultNozzles.data)
            nozzles.sortBy { it.physicalId }
        } else {
            isFetchPumpInfo = false
        }

        return resultNozzles
    }

    private suspend fun login() = suspendCoroutine<Boolean> {
        val port = SPUtil.getString(SPKeys.MIDDLE_PORT).toInt()
        FdcClient.getDefault().sendLogonRequestAsync(port, port, "00.07", { _, response ->
            DFSLog.v("Fusion: login response", response)
            if (response != null && response is ServiceResponseLogOn) {
                val status = response.singleFdcData.fdcStatus
                if (status.equals(FusionConstants.CODE_SUCCESS, true)) {
                    DFSLog.d("Fusion: login succeeded")
                    it.resume(true)
                } else {
                    DFSLog.e("Fusion: login failed")
                    it.resume(false)
                }
            }
        }, timeoutMax)
    }

    private suspend fun logout() = suspendCoroutine<Boolean> {
        isLogin = false
        FdcClient.getDefault().sendLogOffRequestAsync({ _, response ->
            val status = response?.singleFdcData?.fdcStatus
            DFSLog.v("Fusion: try logout response = $status")
            if (status != null && status.equals(FusionConstants.CODE_SUCCESS, true)) {
                DFSLog.i("Fusion: logout succeeded")
                it.resume(true)
            } else {
                DFSLog.e("Fusion: logout failed")
                it.resume(false)
            }
        }, timeoutMax)
    }
}