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.entity.NozzleInfo
import com.doverfuelingsolutions.issp.data.DataStore
import com.doverfuelingsolutions.issp.fusion.callback.OnFusionEvent
import com.doverfuelingsolutions.issp.utils.StringUtil
import com.doverfuelingsolutions.issp.utils.log.DFSLog
import com.doverfuelingsolutions.issp.utils.sp.SPKeys
import com.doverfuelingsolutions.issp.utils.sp.SPUtil
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.FdcMessage
import com.wayne.www.waynelib.fdc.message.ServiceResponse
import com.wayne.www.waynelib.fdc.message.ServiceResponseLogOn
import kotlinx.coroutines.*
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

object FusionManager : LifecycleObserver, OnFdcClientStateChangedListener,
    OnFdcServiceResponseReceivedListener,
    OnFdcMessageReceivedListener {

    private var mStateFusion: FdcClient.FdcClientState = FdcClient.FdcClientState.Stopped
    private var mIsLogin = false
    private val nozzleList = arrayListOf<NozzleInfo>()

    private val coroutineIO = CoroutineScope(Dispatchers.IO)

    val stateFusion: FdcClient.FdcClientState get() = mStateFusion
    val isLogin: Boolean get() = mIsLogin
    var onFusionEvent: OnFusionEvent? = null

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

        mStateFusion = state
        when (state) {
            FdcClient.FdcClientState.Connected -> loginFetchInfo()
            else -> {}
        }
    }

    override fun onServiceResponseReceived(sender: FdcClient?, serviceResponse: ServiceResponse?) {
        if (sender == null || serviceResponse == null) return

        // DFSLog.v("onServiceResponseReceived")
    }

    override fun onFdcMessageReceived(sender: FdcClient?, fdcMessage: FdcMessage?) {
        if (sender == null || fdcMessage == null) return

        // DFSLog.v("onFdcMessageReceived")
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    fun initialize() {
        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(),
            DataStore.sn,
            SPUtil.getString(SPKeys.MIDDLE_WORKSTATION_ID).toInt(),
            null
        )

        coroutineIO.launch {
            delay(12000)
            if (mStateFusion == FdcClient.FdcClientState.Stopped && !mIsLogin) {
                onFusionEvent?.onFusionInit(FusionError.Timeout, StringUtil.get(R.string.connect_timeout))
            }
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun close() {
        val manager = this
        coroutineIO.launch {
            logout()
            FdcClient.getDefault().removeOnFdcClientStateChangedListeners(manager)
            FdcClient.getDefault().removeOnFdcServiceResponseReceivedListeners(manager)
            FdcClient.getDefault().removeOnFdcMessageReceivedListeners(manager)
            FdcClient.getDefault().stop()
        }
    }

    private fun loginFetchInfo() {
        coroutineIO.launch {
            // retry if failed
            val success = if (login()) true else {
                DFSLog.d("Fusion: retry login after logout")
                logout()
                delay(7000)
                login()
            }
            if (!success) {
                mIsLogin = false
                onFusionEvent?.onFusionInit(FusionError.Login, StringUtil.get(R.string.login_fail))
                return@launch
            } else {
                mIsLogin = true
            }

            val resultNozzles = FuelInfoApi.getNozzleInfo()
            if (resultNozzles.success && resultNozzles.data != null) {
                if (nozzleList.isNotEmpty()) nozzleList.clear()
                nozzleList.addAll(resultNozzles.data)
                onFusionEvent?.onFusionInit(FusionError.Success)
            } else {
                onFusionEvent?.onFusionInit(FusionError.GetNozzleInfo, StringUtil.get(R.string.fail_get_nozzle))
            }
        }
    }

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

    private suspend fun logout() = suspendCoroutine<Boolean> {
        FdcClient.getDefault().sendLogOffRequestAsync({ _, response ->
            val status = response.singleFdcData.fdcStatus
            DFSLog.d("Fusion: try logout response = $status")
            if (status.equals(FusionConstants.CODE_SUCCESS, true)) {
                DFSLog.d("Fusion: logout succeeded")
                it.resume(true)
            } else {
                DFSLog.d("Fusion: logout failed")
                it.resume(false)
            }
        }, 10000)
    }
}