package com.doverfuelingsolutions.issp.utils.sp

import android.content.Context
import android.content.SharedPreferences
import android.util.Base64
import androidx.annotation.NonNull
import androidx.preference.PreferenceManager
import com.doverfuelingsolutions.issp.api.WayneApiConfig
import com.doverfuelingsolutions.issp.api.dto.ResultBarcode
import com.doverfuelingsolutions.issp.data.entity.WayneOrderRecord
import com.doverfuelingsolutions.issp.utils.DeviceUtil
import com.doverfuelingsolutions.issp.utils.thread.ThreadUtil
import com.doverfuelingsolutions.issp.utils.log.DFSLog
import com.wayne.www.waynelib.fdc.message.DeviceClass
import com.wayne.www.waynelib.webservice.entity.PosTrx
import java.io.*

object SPUtil {

    private lateinit var instance: SharedPreferences

    fun init(context: Context) {
        instance = PreferenceManager.getDefaultSharedPreferences(context)
        setDefaultValue()
    }

    fun getString(key: String, defaultValue: String = "") = instance.getString(key, defaultValue) ?: ""
    fun putString(key: String, value: String) {
        instance.edit().run {
            putString(key, value)
            apply()
        }
    }

    private fun <T : Serializable> setObject(key: String, @NonNull obj: T) {
        ThreadUtil.io {
            try {
                if (key.isBlank()) throw IllegalArgumentException("SP key must not be blank or empty.")

                val byteArrayOutputStream = ByteArrayOutputStream()
                val objectOutputStream = ObjectOutputStream(byteArrayOutputStream)
                objectOutputStream.writeObject(obj)
                val string = String(Base64.encode(byteArrayOutputStream.toByteArray(), Base64.DEFAULT))
                byteArrayOutputStream.close()
                objectOutputStream.close()
                putString(key, string)

            } catch (e: Exception) {
                DFSLog.e(e)
            }
        }
    }

    private inline fun <reified T> getObject(key: String): T? {
        val string = getString(key)
        if (string.isBlank()) return null

        val byteArrayInputStream = ByteArrayInputStream(Base64.decode(string.toByteArray(), Base64.DEFAULT))
        val objectInputStream = ObjectInputStream(byteArrayInputStream)
        val obj = objectInputStream.readObject()
        return if (obj is T) obj else null
    }

    private fun setDefaultValue() {
        val firstLaunch = instance.getBoolean(SPKeys.FIRST_LAUNCH, true)
        if (!firstLaunch) return

        instance.edit().run {
            putBoolean(SPKeys.FIRST_LAUNCH, false)

            putString(SPKeys.SN, DeviceUtil.generateSerialNumber())
            putString(SPKeys.SN_RAW, DeviceUtil.hardwareSerialNumber())

            putString(SPKeys.SERVER_DOMAIN, WayneApiConfig.DOMAIN_DEFAULT)
            putString(SPKeys.SERVER_PORT_BASE, WayneApiConfig.PORT_BASE_DEFAULT)
            putString(SPKeys.SERVER_PORT_TRX, WayneApiConfig.PORT_TRX_DEFAULT)
            putString(SPKeys.SERVER_PORT_CONFIG, WayneApiConfig.PORT_CONFIG_DEFAULT)

            putString(SPKeys.MIDDLE_IP, WayneApiConfig.MIDDLE_IP_DEFAULT)
            putString(SPKeys.MIDDLE_PORT, WayneApiConfig.MIDDLE_PORT_DEFAULT)
            putString(SPKeys.MIDDLE_WORKSTATION_ID, WayneApiConfig.MIDDLE_WORKSTATION_ID)

            putString(SPKeys.FUEL_IP, WayneApiConfig.FUEL_IP_DEFAULT)
            putString(SPKeys.FUEL_PORT, WayneApiConfig.FUEL_PORT_DEFAULT)

            commit()
        }
    }

    /***** 业务相关部分 *****/
    /**
     * DC 类的唯一标识，用于标记订单（而非存下整个 DC），目的是节省存储空间和提高速度
     */
    private fun getDeviceClassSymbol(dc: DeviceClass): String = "${dc.pumpNo}-${dc.transactionSeqNo}-${dc.releaseTokenAttribute}-${dc.releaseTokenElement}"

    /**
     * 订单列表：读取
     */
    fun getOrderListByPump(pumpId: Int): SerializableArrayList<DeviceClass> {
        return getObject<SerializableArrayList<DeviceClass>>("${SPKeys.ORDER_UNSETTLED}-$pumpId") ?: SerializableArrayList()
    }
    /**
     * 订单列表：存储
     */
    fun setOrderListByPump(pumpId: Int, orderList: List<DeviceClass>) {
        setObject("${SPKeys.ORDER_UNSETTLED}-$pumpId", SerializableArrayList(orderList))
    }
    /**
     * 订单列表：分 pump 存储
     */
    fun setAllOrderList(orderList: List<DeviceClass>) {
        val map = HashMap<Int, ArrayList<DeviceClass>>()
        orderList.forEach {
            val pumpId = it.pumpNo
            if (!map.containsKey(pumpId)) {
                map[pumpId] = arrayListOf(it)
            } else {
                map[pumpId]?.add(it)
            }
        }
        map.forEach {
            setObject("${SPKeys.ORDER_UNSETTLED}-${it.key}", SerializableArrayList(it.value))
        }
    }

    // TODO 需要一个刷新全部数据的机制
    /**
     * 油品代码信息：读取
     */
    fun getBarcodeInfo(barcode: Int): ResultBarcode? = getObject<ResultBarcode>(SPKeys.FUEL_BARCODE + barcode)
    /**
     * 油品代码信息：写入
     */
    fun setBarcodeInfo(barcode: Int, obj: ResultBarcode) = setObject(SPKeys.FUEL_BARCODE + barcode, obj)

    /**
     * 云订单：读取本地列表
     */
    private fun getWayneOrderList(): SerializableArrayList<WayneOrderRecord> {
        return getObject<SerializableArrayList<WayneOrderRecord>>(SPKeys.ORDER_GENERATED) ?: SerializableArrayList()
    }
    /**
     * 云订单：添加到本地
     */
    fun addWayneOrder(dc: DeviceClass, posTrx: PosTrx) {
        val orderList = getWayneOrderList()
        orderList.add(WayneOrderRecord(
            getDeviceClassSymbol(dc),
            posTrx,
            System.currentTimeMillis()
        ))
        setObject(SPKeys.ORDER_GENERATED, orderList)
    }
    /**
     * 云订单：从本地列表移除
     */
    fun removeWayneOrder(dc: DeviceClass) {
        val symbol = getDeviceClassSymbol(dc)
        val orderList = getWayneOrderList()
        val order = orderList.find { it.dc == symbol }
        if (order != null) {
            orderList.remove(order)
            setObject(SPKeys.ORDER_GENERATED, orderList)
        }
    }
    /**
     * 云订单：从本地列表查找
     */
    fun findWayneOrder(dc: DeviceClass): PosTrx? {
        val symbol = getDeviceClassSymbol(dc)
        val orderList = getWayneOrderList()
        val order = orderList.find { it.dc == symbol }
        if (order != null) {
            return order.posTrx
        }
        return null
    }
    /**
     * 云订单：本地列表移除过期的
     */
    fun removeExpiredWayneOrder() {
        val orderList = getWayneOrderList()
        if (orderList.isEmpty()) return

        val notExpireList = SerializableArrayList<WayneOrderRecord>()
        orderList.forEach {
            if (!it.isExpire()) {
                notExpireList.add(it)
            }
        }
        if (orderList.size != notExpireList.size) {
            setObject(SPKeys.ORDER_GENERATED, notExpireList)
        }
    }

    /**
     * 订单锁定：获取在本设备上锁定的订单列表
     */
    fun getLockOrderList(): SerializableArrayList<String> {
        return getObject<SerializableArrayList<String>>(SPKeys.ORDER_LOCK) ?: SerializableArrayList()
    }
    /**
     * 订单锁定：锁定一笔订单
     */
    fun addLockOrder(dc: DeviceClass) {
        val symbol = getDeviceClassSymbol(dc)
        val list = getLockOrderList()
        if (!list.contains(symbol)) {
            list.add(symbol)
            setObject(SPKeys.ORDER_LOCK, list)
        }
    }
    /**
     * 订单锁定：解锁一笔订单
     */
    fun removeLockOrder(dc: DeviceClass) = removeLockOrder(getDeviceClassSymbol(dc))
    private fun removeLockOrder(symbol: String) {
        val list = getLockOrderList()
        if (list.isNotEmpty() && list.contains(symbol)) {
            list.remove(symbol)
            setObject(SPKeys.ORDER_LOCK, list)
        }
    }
    /**
     * 订单锁定：判断订单是否被当前设备锁定的
     */
    fun isLockByThis(dc: DeviceClass): Boolean {
        val list = getLockOrderList()
        val symbol = getDeviceClassSymbol(dc)
        return list.contains(symbol)
    }
    /**
     * 订单锁定：新的锁定订单列表
     */
    fun replaceLockOrderList(orderList: List<String>) {
        setObject(SPKeys.ORDER_LOCK, SerializableArrayList(orderList))
    }
    fun replaceLockOrderListByDC(orderList: List<DeviceClass>) {
        val sList = orderList.map { getDeviceClassSymbol(it) }
        replaceLockOrderList(sList)
    }

    /**
     * 消除订单：待消除的订单列表
     */
    fun getUnclearedOrderList(): SerializableArrayList<String> {
        return getObject<SerializableArrayList<String>>(SPKeys.ORDER_UNCLEARED) ?: SerializableArrayList()
    }
    /**
     * 消除订单：添加
     */
    fun addUnclearedOrder(dc: DeviceClass) {
        val orderList = getUnclearedOrderList()
        val symbol = getDeviceClassSymbol(dc)
        if (!orderList.contains(symbol)) {
            orderList.add(symbol)
            setObject(SPKeys.ORDER_UNCLEARED, orderList)
        }
    }
    /**
     * 消除订单：移除
     */
    fun removeUnclearedOrder(dc: DeviceClass) = removeUnclearedOrder(getDeviceClassSymbol(dc))
    private fun removeUnclearedOrder(symbol: String) {
        val list = getUnclearedOrderList()
        if (list.isNotEmpty() && list.contains(symbol)) {
            list.remove(symbol)
            setObject(SPKeys.ORDER_UNCLEARED, list)
        }
    }
    /**
     * 消除订单：新的锁定订单列表
     */
    fun replaceUnclearedOrderList(orderList: List<String>) {
        setObject(SPKeys.ORDER_UNCLEARED, SerializableArrayList(orderList))
    }
}