package com.abbidot.tracker.vm

import android.app.Application
import android.content.Context
import android.net.Uri
import android.os.Build
import android.text.TextUtils
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.abbidot.baselibrary.util.LogUtil
import com.abbidot.tracker.R
import com.abbidot.tracker.bean.DFUStateBean
import com.abbidot.tracker.bean.FirmwareBean
import com.abbidot.tracker.constant.ConstantInt
import com.abbidot.tracker.retrofit2.NetworkApi
import com.abbidot.tracker.service.DfuService
import com.abbidot.tracker.util.FileUtil
import com.abbidot.tracker.util.bluetooth.SRBleUtil
import com.clj.fastble.BleManager
import com.clj.fastble.callback.BleScanCallback
import com.clj.fastble.data.BleDevice
import com.clj.fastble.data.BleScanState
import com.clj.fastble.scan.BleScanRuleConfig
import io.runtime.mcumgr.ble.McuMgrBleTransport
import io.runtime.mcumgr.dfu.FirmwareUpgradeCallback
import io.runtime.mcumgr.dfu.FirmwareUpgradeController
import io.runtime.mcumgr.dfu.mcuboot.FirmwareUpgradeManager
import io.runtime.mcumgr.exception.McuMgrException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import no.nordicsemi.android.dfu.DfuProgressListener
import no.nordicsemi.android.dfu.DfuServiceInitiator
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.InputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream

/**
 *Created  by .yzq on 2023/3/16/016.
 * @link
 * @description:
 */
class DeviceDFUViewModel(application: Application) : AndroidViewModel(application) {

    val mFirmwareInfoLiveData = MutableLiveData<Result<FirmwareBean>>()
    val mDfuStateLiveData = MutableLiveData<DFUStateBean>()

    //dfu成功
    val mDFUSuccessCode = 101

    //dfu失败
    val mDFUFailCode = -2

    //记下更新时间，不用频繁更新
    private var mUpdatePostTimeMillis = System.currentTimeMillis()


    /**
     * 获取固件信息
     */
    fun getFirmware(deviceType: Int) {
//        activity.showLoading(isShowLoading)
        viewModelScope.launch(Dispatchers.IO) {
            val deviceName = if (deviceType == ConstantInt.Type2) "g40"
            else "GPS_Tracker_01"
            val result = NetworkApi.getFirmware(deviceName)
            mFirmwareInfoLiveData.postValue(result)
        }
    }

    /**
     * 对比版本号是否可以更新
     */
    fun checkVersionUpdate(localVersion: String, serviceVersion: String): Boolean {
        if (TextUtils.isEmpty(localVersion)) return false
        val localVersionInt = (localVersion.replace(".", "").substring(1, 4)).toInt()
        val serviceVersionInt = serviceVersion.replace(".", "").toInt()
        LogUtil.e("对比固件版本localVersionInt=$localVersionInt,serviceVersionInt=$serviceVersionInt")
        return serviceVersionInt > localVersionInt
    }


    fun startG40DFU(context: Context, bleDevice: BleDevice, path: String) {
        val transport = McuMgrBleTransport(context, bleDevice.device)
        val dfuManager = FirmwareUpgradeManager(
            transport, object : FirmwareUpgradeCallback<FirmwareUpgradeManager.State?> {
                override fun onUpgradeStarted(controller: FirmwareUpgradeController?) {
                    val stateBean = DFUStateBean(context.getString(R.string.txt_upgrade_start))
                    mDfuStateLiveData.value = stateBean
                    LogUtil.e("DfuProgressListener -->>  onDfuProcessStarting")
                }

                override fun onUpgradeCompleted() {
                    val stateBean = DFUStateBean(context.getString(R.string.txt_upgrade_success))
                    mDfuStateLiveData.value = stateBean
                    LogUtil.e("DfuProgressListener -->>  onUpgradeCompleted")
                    val state = DFUStateBean(
                        context.getString(R.string.txt_reconnect_device), mDFUSuccessCode
                    )
                    mDfuStateLiveData.value = state
                }

                override fun onUploadProgressChanged(
                    bytesSent: Int, imageSize: Int, timestamp: Long
                ) {
                    val currentTimeMillis = System.currentTimeMillis()
                    //500ms更新发送数据，不用频繁更新
                    if (currentTimeMillis - mUpdatePostTimeMillis > 500 || bytesSent == imageSize) {
                        val progress = (bytesSent / imageSize.toFloat())
                        mUpdatePostTimeMillis = currentTimeMillis
                        val dFUStateBean = DFUStateBean(
                            context.getString(R.string.txt_upgrading) + " ",
                            (progress * 100).toInt()
                        )
                        mDfuStateLiveData.value = dFUStateBean
                    }
                }

                override fun onUpgradeCanceled(state: FirmwareUpgradeManager.State?) {
                    LogUtil.e("DfuProgressListener -->>  onUpgradeCanceled")
                }

                override fun onUpgradeFailed(
                    state: FirmwareUpgradeManager.State?, error: McuMgrException?
                ) {
                    LogUtil.e("DfuProgressListener -->>  onError${error.toString()}")
                    val stateBean =
                        DFUStateBean(context.getString(R.string.txt_upgrade_fail), mDFUFailCode)
                    mDfuStateLiveData.value = stateBean
                }

                override fun onStateChanged(
                    prevState: FirmwareUpgradeManager.State?,
                    newState: FirmwareUpgradeManager.State?
                ) {
                    LogUtil.e("DfuProgressListener -->>  onStateChanged")
                }
            })

        /*
         * 常用设置：
         * estimatedSwapTime: 设备重启后 MCUboot 交换镜像的大致时间
         * windowCapacity: 窗口上传并发数；设备端支持时可以提速
         * eraseAppSettings: 是否擦除应用 settings
         *
         * 这里先给一个稳妥配置：
         * - estimatedSwapTime = 20s
         * - windowCapacity = 1 （最稳，兼容性最好）
         */
        val settings = FirmwareUpgradeManager.Settings.Builder().setEstimatedSwapTime(20000)
            .setWindowCapacity(1).setMemoryAlignment(4).setEraseAppSettings(false).build()

        dfuManager.setMode(FirmwareUpgradeManager.Mode.CONFIRM_ONLY)
        try {
            val file = File(path)
            //  读取 zip，并提取单镜像 .bin
            val imageData = extractSingleBinFromZip(context, FileUtil.getUri(context, file))
            dfuManager.start(imageData!!, settings)
        } catch (e: Exception) {
            val stateBean = DFUStateBean(context.getString(R.string.txt_upgrade_fail), mDFUFailCode)
            mDfuStateLiveData.value = stateBean
            LogUtil.e("G40DFU 失败:${e.message}")
        }
    }

    /**
     * 从 zip 中提取“单镜像”的 .bin 文件。
     *
     * 适用于：
     * - g40_app.zip 里只有一个主要 app bin
     *
     * 如果 zip 中有多个 .bin，当前实现会优先返回：
     * 1. 文件名包含 app 的
     * 2. 否则返回第一个 .bin
     */
    private fun extractSingleBinFromZip(context: Context, zipUri: Uri): ByteArray? {
        context.contentResolver.openInputStream(zipUri).use { input ->
            requireNotNull(input) { "无法打开 zip 文件: $zipUri" }

            val bins = mutableListOf<Pair<String, ByteArray>>()

            ZipInputStream(input.buffered()).use { zis ->
                var entry: ZipEntry? = zis.nextEntry
                while (entry != null) {
                    if (!entry.isDirectory) {
                        val name = entry.name
                        if (name.endsWith(".bin", ignoreCase = true)) {
                            bins.add(name to zis.readAllBytesCompat())
                        }
                    }
                    zis.closeEntry()
                    entry = zis.nextEntry
                }
            }

            if (bins.isEmpty()) return null
            if (bins.size == 1) return bins.first().second

            // 多个 bin 时，优先选名字里带 app 的
            bins.firstOrNull { it.first.contains("app", ignoreCase = true) }?.let {
                return it.second
            }

            // 否则退化成第一个
            return bins.first().second
        }
    }

    private fun InputStream.readAllBytesCompat(): ByteArray {
        val buffer = ByteArray(4096)
        val bos = ByteArrayOutputStream()
        while (true) {
            val len = this.read(buffer)
            if (len <= 0) break
            bos.write(buffer, 0, len)
        }
        return bos.toByteArray()
    }


    fun startG30DFU(context: Context, device: BleDevice, path: String) {
        viewModelScope.launch {
            val dFUStateBean = DFUStateBean(context.getString(R.string.txt_dfu_model))
            mDfuStateLiveData.value = dFUStateBean
            //是否扫描到
            var isScan = false
            val dfuCmd = byteArrayOf(0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66)
            SRBleUtil.instance.writeData(device, dfuCmd)
            if (BleScanState.STATE_IDLE != BleManager.getInstance().scanSate) BleManager.getInstance()
                .cancelScan()
            // 扫描超时时间，可选，默认10秒
            val scanRuleConfig = BleScanRuleConfig.Builder().setScanTimeOut(60000).build()
            BleManager.getInstance().initScanRule(scanRuleConfig)
            BleManager.getInstance().scan(object : BleScanCallback() {
                override fun onScanStarted(success: Boolean) {
                    val stateBean = DFUStateBean(context.getString(R.string.txt_search_dfu))
                    mDfuStateLiveData.value = stateBean
                    LogUtil.e("开始搜索DUF设备。。。。")
                }

                override fun onScanning(bleDevice: BleDevice?) {
                    bleDevice?.apply {
                        LogUtil.e("搜索DUF设备中。。。已搜索到$name")
                        val deviceDFUName = "DfuTarg"
//                        val deviceDFUName = "xxx"
                        if (!TextUtils.isEmpty(name) && name.contains(deviceDFUName)) {
                            isScan = true
                            BleManager.getInstance().cancelScan()
                            dfu(context, bleDevice, path)
                        }
                    }
                }

                override fun onScanFinished(scanResultList: MutableList<BleDevice>?) {
                    LogUtil.e("搜索DUF设备结束。")
                    if (!isScan) {
                        val stateBean =
                            DFUStateBean(context.getString(R.string.txt_no_dfu), mDFUFailCode)
                        mDfuStateLiveData.value = stateBean
                    }
                }
            })
        }
    }


    private fun dfu(context: Context, bleDevice: BleDevice, path: String) {
        val dfuServiceInitiator = DfuServiceInitiator(bleDevice.mac)
        dfuServiceInitiator.setDeviceName(bleDevice.name)
        dfuServiceInitiator.setKeepBond(false).setPacketsReceiptNotificationsEnabled(false)
            .setPacketsReceiptNotificationsValue(10)
            .setUnsafeExperimentalButtonlessServiceInSecureDfuEnabled(true).setZip(path)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //Android O,通知栏问题
            DfuServiceInitiator.createDfuNotificationChannel(context)
        }
        dfuServiceInitiator.start(context, DfuService::class.java)
    }

    val mDfuProgressListener = object : DfuProgressListener {

        private val context = getApplication<Application>()

        /**设备正在连接 */
        override fun onDeviceConnecting(deviress: String) {
            val stateBean = DFUStateBean(context.getString(R.string.txt_connect_device))
            mDfuStateLiveData.value = stateBean
            LogUtil.e("DfuProgressListener -->>  onDeviceConnecting,dfu开始连接设备")
        }

        /**
         * 设备连接了  1
         * @param deviceAddress
         */
        override fun onDeviceConnected(deviceAddress: String) {
            val stateBean = DFUStateBean(context.getString(R.string.txt_device_connect_success))
            mDfuStateLiveData.value = stateBean
            LogUtil.e("DfuProgressListener -->> dfu连接设备成功 onDeviceConnected")
        }

        /**升级准备开始
         * 2
         */
        override fun onDfuProcessStarting(deviceAddress: String) {
            val stateBean = DFUStateBean(context.getString(R.string.txt_upgrade_start))
            mDfuStateLiveData.value = stateBean
            LogUtil.e("DfuProgressListener -->>  onDfuProcessStarting")
        }

        /**更新进度开始
         * 3
         */
        override fun onDfuProcessStarted(deviceAddress: String) {
            LogUtil.e("DfuProgressListener -->>  onDfuProcessStarted")
        }

        override fun onEnablingDfuMode(deviceAddress: String) {
            LogUtil.e("DfuProgressListener -->>  onEnablingDfuMode")
        }

        /**升级中的回调 */
        override fun onProgressChanged(
            deviceAddress: String,
            percent: Int,
            speed: Float,
            avgSpeed: Float,
            currentPart: Int,
            partsTotal: Int
        ) {
            val currentTimeMillis = System.currentTimeMillis()
            //500ms更新发送数据，不用频繁更新
            if (currentTimeMillis - mUpdatePostTimeMillis > 500 || percent == 100) {
                mUpdatePostTimeMillis = currentTimeMillis
                val dFUStateBean =
                    DFUStateBean(context.getString(R.string.txt_upgrading) + " ", percent)
                mDfuStateLiveData.value = dFUStateBean
            }
        }

        /**固件验证-未用 */
        override fun onFirmwareValidating(deviceAddress: String) {
            LogUtil.e("DfuProgressListener -->>  onFirmwareValidating")
        }

        /**设备正在断开
         * 4
         */
        override fun onDeviceDisconnecting(deviceAddress: String) {
            val dFUStateBean = DFUStateBean(context.getString(R.string.txt_unconnect_no))
            mDfuStateLiveData.value = dFUStateBean
            LogUtil.e("DfuProgressListener -->>  onDeviceDisconnecting")
        }

        /**设备断开了
         * 5
         */
        override fun onDeviceDisconnected(deviceAddress: String) {
            val stateBean = DFUStateBean(context.getString(R.string.txt_unconnect))
            mDfuStateLiveData.value = stateBean
            LogUtil.e("DfuProgressListener -->>  onDeviceDisconnected")
        }

        /**升级完成
         * 6
         */
        override fun onDfuCompleted(deviceAddress: String) {
            val stateBean = DFUStateBean(context.getString(R.string.txt_upgrade_success))
            mDfuStateLiveData.value = stateBean
            LogUtil.e("DfuProgressListener -->>  onDfuCompleted")
            val state =
                DFUStateBean(context.getString(R.string.txt_reconnect_device), mDFUSuccessCode)
            mDfuStateLiveData.value = state
        }

        override fun onDfuAborted(deviceAddress: String) {
            LogUtil.e("DfuProgressListener -->>  onDfuAborted")
        }

        /**升级失败 */
        override fun onError(deviceAddress: String, error: Int, errorType: Int, message: String) {
            LogUtil.e("DfuProgressListener -->>  onError")
            val stateBean = DFUStateBean(context.getString(R.string.txt_upgrade_fail), mDFUFailCode)
            mDfuStateLiveData.value = stateBean
        }
    }
}