package com.doverfuelingsolutions.issp.api

import android.util.Base64
import com.doverfuelingsolutions.issp.BuildConfig
import com.doverfuelingsolutions.issp.DFSApplication
import com.doverfuelingsolutions.issp.R
import com.doverfuelingsolutions.issp.api.basic.RetrofitUtil
import com.doverfuelingsolutions.issp.api.dto.*
import com.doverfuelingsolutions.issp.api.service.ServiceBase
import com.doverfuelingsolutions.issp.api.service.ServiceConfig
import com.doverfuelingsolutions.issp.api.service.ServiceLogin
import com.doverfuelingsolutions.issp.api.service.ServiceTrx
import com.doverfuelingsolutions.issp.data.GlobalData
import com.doverfuelingsolutions.issp.utils.StringUtil
import com.doverfuelingsolutions.issp.utils.thread.ThreadUtil
import com.doverfuelingsolutions.issp.utils.log.DFSLog
import com.doverfuelingsolutions.issp.utils.sp.SPUtil
import com.wayne.www.waynelib.fdc.message.DeviceClass
import com.wayne.www.waynelib.webservice.entity.*
import org.json.JSONArray
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.io.File
import java.math.BigDecimal
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

object SystemApi {

    private var serviceLogin = makeLoginService()
    private var serviceBase = makeBaseService()
    private var serviceTrx = makeTrxService()
    private var serviceConfig = makeConfigService()

    /**
     * 登录 + 获取油站信息
     */
    suspend fun login(accountName: String, password: String): DFSResult<*> {
        val resultLogin = loginOnly(accountName, password)
        return if (resultLogin.success) {
            // 通知服务器（不明白其实际意义，但照做就是了）
            notifyTrx(PosTrxType.LogOn)
        } else {
            resultLogin
        }
    }

    /**
     * 根据油品代码查询油品名称
     */
    suspend fun barcodeName(id: Int) = suspendCoroutine<DFSResult<ResultBarcode>> {
        serviceBase.barcodeInfo(id).enqueue(object : Callback<ResultBarcode> {
            override fun onResponse(call: Call<ResultBarcode>, response: Response<ResultBarcode>) {
                val code = response.code()
                val body = response.body()
                if (code == 200 && body != null) {
                    it.resume(DFSResult.success(body))
                    return
                }
                it.resume(DFSResult.fail(R.string.req_fail))
            }

            override fun onFailure(call: Call<ResultBarcode>, t: Throwable) {
                DFSLog.e(t)
                it.resume(DFSResult.fail(t.message.toString()))
            }
        })
    }

    /**
     * 获取油站的支付配置信息
     */
    suspend fun paymentParam() = suspendCoroutine<DFSResult<ResultPaymentParam>> {
        serviceTrx.paymentParam().enqueue(object : Callback<ResultObj<List<ResultPaymentParam>>> {
            override fun onResponse(call: Call<ResultObj<List<ResultPaymentParam>>>, response: Response<ResultObj<List<ResultPaymentParam>>>) {
                val code = response.code()
                val body = response.body()
                if (code == 200 && body != null && body.StatusCode == 200 && body.Data.isNotEmpty()) {
                    it.resume(DFSResult.success(body.Data[0]))
                } else {
                    it.resume(DFSResult.fail(R.string.fail_get_pay_param))
                }
            }

            override fun onFailure(call: Call<ResultObj<List<ResultPaymentParam>>>, t: Throwable) {
                DFSLog.e(t)
                it.resume(DFSResult.fail(R.string.fail_get_pay_param))
            }
        })
    }

    /**
     * 向服务器备案即将进行支付的订单
     */
    suspend fun generateOrder(dc: DeviceClass) = suspendCoroutine<DFSResult<PosTrx>> {
        val clientPosTrx = ClientPosTrx(PosTrxType.Sale)
        val clientRingUpPosItem = ClientRingUpPosItem()
        SPUtil.getBarcodeInfo(dc.productNo1.toInt())?.let { info -> clientRingUpPosItem.posItemUniqueId = info.Id }
        clientRingUpPosItem.setFuelItemSoldOnPumpId(dc.pumpNo)
        clientRingUpPosItem.setFuelItemClientSidePrice(dc.unitPrice)
        clientRingUpPosItem.fuelItemFdcTransactionSeqNo = dc.transactionSeqNo
        clientRingUpPosItem.setFuelItemPumpTotalizerMoney(dc.amountTotalizer)
        clientRingUpPosItem.setFuelItemPumpTotalizerVolume(dc.volumeTotalizer)
        clientRingUpPosItem.setFuelItemTransactionEndTime(dc.saleEndTime)
        clientRingUpPosItem.setFuelItemFdcReleaseTokenAttribute(dc.releaseTokenAttribute.toInt())
        clientRingUpPosItem.setFuelItemOriginalGrossAmount(if (BuildConfig.DEBUG) BigDecimal("0.01") else dc.amount)
        clientPosTrx.addClientRingUpPosItem(clientRingUpPosItem)

        serviceTrx.reportPaymentOrder(clientPosTrx).enqueue(object : Callback<PosTrx> {
            override fun onResponse(call: Call<PosTrx>, response: Response<PosTrx>) {
                val code = response.code()
                val body = response.body()
                if (code == 200 && body is PosTrx) {
                    it.resume(DFSResult.success(body))
                } else {
                    it.resume(DFSResult.fail(R.string.response_null))
                }
            }

            override fun onFailure(call: Call<PosTrx>, t: Throwable) {
                DFSLog.e(t)
                it.resume(DFSResult.fail(t.message.toString()))
            }
        })
    }

    /**
     * 扫码支付
     *
     * code=412：交易ID不存在
     * code=409：交易已经支付过
     * code=406：优惠券核销不通过
     * code=400：支付失败
     */
    suspend fun pay(posTrx: PosTrx, code: String) = suspendCoroutine<DFSResult<ResultPayment>> {
        val posTrxMop = PosTrxMop().apply {
            setPayBack(0f)
            authCode = code
            posMopPaymentId = GlobalData.paymentId.get()
            paid = if (BuildConfig.DEBUG) {
                0.01f
            } else {
                posTrx.netAmount.toFloat()
            }
        }
        serviceTrx.pay(posTrx.id, posTrxMop).enqueue(object : Callback<ResultPayment> {
            override fun onResponse(call: Call<ResultPayment>, response: Response<ResultPayment>) {
                val body = response.body()
                val codeResponse = response.code()
                when {
                    codeResponse == 200 && body is ResultPayment -> {
                        it.resume(DFSResult.success(body))
                    }
                    codeResponse == 412 -> {
                        it.resume(DFSResult.fail("订单交易 ID 不存在"))
                    }
                    codeResponse == 409 -> {
                        it.resume(DFSResult.fail("此订单已支付，请勿重复支付"))
                    }
                    codeResponse == 406 -> {
                        it.resume(DFSResult.fail("优惠券核销不通过"))
                    }
                    else -> {
                        it.resume(DFSResult.fail(R.string.fail_pay))
                    }
                }
            }

            override fun onFailure(call: Call<ResultPayment>, t: Throwable) {
                DFSLog.e(t)
                it.resume(DFSResult.fail(""))
            }
        })
    }

    /**
     * 查询油站联系电话
     */
    suspend fun sessionInfo() = suspendCoroutine<DFSResult<ResultDeviceSessionInfo>> {
        serviceBase.deviceSessionInfo(GlobalData.serialNumber.get()).enqueue(object : Callback<ResultDeviceSessionInfo> {
            override fun onResponse(call: Call<ResultDeviceSessionInfo>, response: Response<ResultDeviceSessionInfo>) {
                val code = response.code()
                val body = response.body()
                if (code != 200 || body == null) {
                    it.resume(DFSResult.fail(R.string.return_data_error))
                } else {
                    it.resume(DFSResult.success(body))
                }
            }

            override fun onFailure(call: Call<ResultDeviceSessionInfo>, t: Throwable) {
                DFSLog.e(t)
                it.resume(DFSResult.fail(R.string.return_data_error))
            }
        })
    }

    /**
     * 获取一些油站信息（LOGO）
     */
    suspend fun stationLogo(): DFSResult<String> {
        val resultConfig = config(WayneApiConfig.CONFIG_LOGO)
        if (resultConfig.success && resultConfig.data!!.id.isNotEmpty()) {
            val resultFile = file(resultConfig.data.id)
            if (resultFile.success && resultFile.data != null) {
                return DFSResult.success(resultFile.data)
            }
        }
        return DFSResult.fail(R.string.req_fail)
    }





    /**
     * 单纯的登录操作
     */
    private suspend fun loginOnly(accountName: String, password: String) = suspendCoroutine<DFSResult<ResultLogin>> {
        serviceLogin.login(accountName, password).enqueue(object : Callback<ResultLogin> {

            override fun onResponse(call: Call<ResultLogin>, response: Response<ResultLogin>) {
                val code = response.code()
                val body = response.body()
                if (code != 200) {
                    it.resume(DFSResult.fail(R.string.account_name_password_error))
                    return
                } else if (body != null) {
                    GlobalData.accountName.set(accountName)
                    GlobalData.password.set(password)
                    GlobalData.accessToken.set(body.access_token)
                    GlobalData.accessTokenExpire.set((System.currentTimeMillis() + body.expires_in.toLong() * 1000))
                    // [{\"Id\":\"dfc5cc2d-a5ef-4bc4-b1b0-4aa0c82a73de\",\"Name\":\"GlobalUnit\"}]
                    if (body.BusinessUnits.isNotBlank()) {
                        val jsonArray = JSONArray(body.BusinessUnits)
                        if (jsonArray.length() > 0) {
                            val obj = jsonArray.getJSONObject(0)
                            if (!obj.isNull("Id") && !obj.isNull("Name")) {
                                val buId = obj.optString("Id")
                                val buName = obj.optString("Name")
                                GlobalData.businessId.set(buId)
                                GlobalData.businessName.set(buName)
                                it.resume(DFSResult.success(body))
                                return
                            }
                        }
                    }
                }
                it.resume(DFSResult.fail(R.string.return_data_error))
            }

            override fun onFailure(call: Call<ResultLogin>, t: Throwable) {
                DFSLog.e(t)
                it.resume(DFSResult.fail(t.message.toString()))
            }
        })
    }

    /**
     * 可能需要告知云端发生的事务？？
     */
    private suspend fun notifyTrx(type: Int) = suspendCoroutine<DFSResult<Boolean>> {
        val clientPosTrx = ClientPosTrx(type)
        serviceTrx.report(clientPosTrx).enqueue(object : Callback<Any> {
            override fun onResponse(call: Call<Any>, response: Response<Any>) {
                it.resume(if (response.code() == 200) {
                    DFSResult.success(true)
                } else {
                    DFSResult.fail(StringUtil.get(R.string.login_fail, response.errorBody()?.string() ?: response.message()))
                })
            }

            override fun onFailure(call: Call<Any>, t: Throwable) {
                DFSLog.e(t)
                it.resume(DFSResult.fail(t.message.toString()))
            }
        })
    }

    /**
     * 查询配置信息
     */
    private suspend fun config(type: String) = suspendCoroutine<DFSResult<ResultConfigDescItem>> {
        serviceConfig.config(GlobalData.businessId.get(), type).enqueue(object : Callback<Array<ResultConfig>> {
            override fun onResponse(call: Call<Array<ResultConfig>>, response: Response<Array<ResultConfig>>) {
                val code = response.code()
                val body = response.body()
                if (code == 200 && !body.isNullOrEmpty() && body[0].configFileDescriptions.isNotEmpty()) {
                    if (body[0].configFileDescriptions.isNotEmpty()) {
                        val obj = body[0].configFileDescriptions[0]
                        it.resume(DFSResult.success(ResultConfigDescItem(obj.id, obj.name)))
                        return
                    }
                }
                it.resume(DFSResult.fail(R.string.return_data_error))
            }

            override fun onFailure(call: Call<Array<ResultConfig>>, t: Throwable) {
                DFSLog.e("WayneApi.serviceConfig.config.onFailure", t)
                it.resume(DFSResult.fail(R.string.req_fail))
            }
        })
    }

    /**
     * 下载文件
     */
    private suspend fun file(id: String) = suspendCoroutine<DFSResult<String>> {
        serviceConfig.file(id).enqueue(object : Callback<Array<ResultFile>> {
            override fun onResponse(call: Call<Array<ResultFile>>, response: Response<Array<ResultFile>>) {
                val code = response.code()
                val body = response.body()
                if (code != 200 || body.isNullOrEmpty() || body[0].value.isBlank()) {
                    it.resume(DFSResult.fail(R.string.return_data_error))
                } else {
                    ThreadUtil.io {
                        try {
                            val file = File(DFSApplication.instance.applicationContext.filesDir, body[0].name)
                            if (file.isFile) file.delete()
                            file.createNewFile()
                            file.writeBytes(Base64.decode(body[0].value, Base64.DEFAULT))
                            it.resume(DFSResult.success(body[0].name))
                        } catch (e: Exception) {
                            DFSLog.e("WayneApi.file.save", e)
                            it.resume(DFSResult.fail(R.string.fail_save_file))
                        }
                    }
                }
            }

            override fun onFailure(call: Call<Array<ResultFile>>, t: Throwable) {
                DFSLog.e("WayneApi.serviceConfig.file", t)
                it.resume(DFSResult.fail(R.string.req_fail))
            }
        })
    }

    /**
     * 在设置中修改后需要重新生成 baseUrl
     */
    fun regenerateAllService() {
        serviceLogin = makeLoginService()
        serviceBase = makeBaseService()
        serviceTrx = makeTrxService()
        serviceConfig = makeConfigService()
    }

    private fun makeLoginService(): ServiceLogin {
        return RetrofitUtil.getGsonBuilder()
            .baseUrl(WayneApiConfig.HOST_BASE)
            .build()
            .create(ServiceLogin::class.java)
    }

    private fun makeBaseService(): ServiceBase {
        return RetrofitUtil.getAuthBuilder()
            .baseUrl(WayneApiConfig.HOST_BASE)
            .build()
            .create(ServiceBase::class.java)
    }

    private fun makeTrxService(): ServiceTrx {
        return RetrofitUtil.getAuthBuilder()
            .baseUrl(WayneApiConfig.HOST_TRX)
            .build()
            .create(ServiceTrx::class.java)
    }

    private fun makeConfigService(): ServiceConfig {
        return RetrofitUtil.getAuthBuilder()
            .baseUrl(WayneApiConfig.HOST_CONFIG)
            .build()
            .create(ServiceConfig::class.java)
    }
}