package com.gh.common.xapk

import android.content.Context
import com.gh.gamecenter.core.AppExecutor
import com.gh.common.util.*
import com.gh.download.DownloadManager
import com.gh.gamecenter.common.utils.debugOnly
import com.gh.gamecenter.common.utils.getExtension
import com.gh.gamecenter.common.utils.throwExceptionInDebug
import com.gh.gamecenter.common.utils.tryCatchInRelease
import com.gh.gamecenter.core.utils.SentryHelper
import com.halo.assistant.HaloApp
import com.lightgame.download.DataChanger
import com.lightgame.download.DownloadEntity
import com.lightgame.utils.Utils
import java.text.DecimalFormat
import java.util.*

/**
 * 目前已知的Xapk内容是:只有一个apk包和一个或做多个obb数据包(多余文件不解压,如果存在多个apk包,则以下安装无效)
 *
 * 每次安装都会重新跑一遍解压安装流程
 * 目前检验是否已解压的根据是存在相同文件名且大小一致(暂时想不到校验MD5值的方法)
 *
 * obb文件直接解压至根目录即可,无需操作文件
 * apk文件这解压的gh-files文件夹中
 */
object XapkInstaller : IXapkUnzipListener {
    private const val XAPK_PACKAGE_PATH_TAG = "xapk_package_path"

    // private const val XAPK_DATA_PATH_TAG = "xapk_data_path"
    const val XAPK_EXTENSION_NAME = "xapk"

    // 通过解压过程存放于 DownloadEntity meta
    const val XAPK_UNZIP_PERCENT = "unzip_percent"
    const val XAPK_UNZIP_STATUS = "unzip_status"

    const val XAPK_DATA_EXTENSION_NAME = "obb"
    const val PACKAGE_EXTENSION_NAME = "apk"

    private var mContext = HaloApp.getInstance().application.applicationContext

    // 是否需要开启特定线程处理
    private val mXapkUnzipThreadMap = Collections.synchronizedMap(HashMap<String, XapkUnzipThread>())

    // 按并行解压
    @JvmStatic
    fun install(context: Context, downloadEntity: DownloadEntity, showUnzipToast: Boolean = false) {
        this.mContext = context

        val filePath = downloadEntity.path
        if (XAPK_EXTENSION_NAME == filePath.getExtension()) {
            unzipXapkFile(downloadEntity)
            if (showUnzipToast) {
                Utils.toast(mContext, "解压过程请勿退出光环助手！")
            }
        } else {
            throwExceptionInDebug("如果是Apk包请使用PackageInstaller进行安装")
            PackageInstaller.install(mContext, downloadEntity)
        }
    }

    private fun unzipXapkFile(downloadEntity: DownloadEntity) {
        if (mXapkUnzipThreadMap[downloadEntity.path] == null) {
            val xapkUnzipThread = XapkUnzipThread(downloadEntity, this)
            xapkUnzipThread.start()
            mXapkUnzipThreadMap[downloadEntity.path] = xapkUnzipThread
        } else {
            debugOnly {
                Utils.log("unzip", "重复解压,该文件解压已在队列中")
            }
        }
    }

    override fun onProgress(downloadEntity: DownloadEntity, unzipPath: String, unzipSize: Long, unzipProgress: Long) {
        AppExecutor.uiExecutor.execute {
            val df = DecimalFormat("#.0")
            var percent = 0.0
            tryCatchInRelease {
                percent = df.format((unzipProgress / unzipSize.toFloat()) * 100).toDouble()
            }
            downloadEntity.meta[XAPK_UNZIP_PERCENT] = percent.toString()
            downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.UNZIPPING.name
            DataChanger.notifyDataChanged(downloadEntity)
        }

        debugOnly {
            Utils.log("unzip", "onProgress->" + (unzipProgress / unzipSize.toFloat()))
        }
    }

    override fun onNext(downloadEntity: DownloadEntity, unzipPath: String) {
        if (PACKAGE_EXTENSION_NAME == unzipPath.getExtension()) {
            downloadEntity.meta[XAPK_PACKAGE_PATH_TAG] = unzipPath
            DownloadManager.getInstance().updateDownloadEntity(downloadEntity)
        }

        debugOnly {
            Utils.log("unzip", "onNext->$unzipPath")
        }
    }

    /**
     * 取消解压回调
     *
     * 取消后的表现与下载完成一致
     */
    override fun onCancel(downloadEntity: DownloadEntity) {
        mXapkUnzipThreadMap.remove(downloadEntity.path)

        AppExecutor.uiExecutor.execute {
            downloadEntity.meta[XAPK_UNZIP_PERCENT] = "0.0"
            downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.CANCEL.name
            DataChanger.notifyDataChanged(downloadEntity)
            DownloadManager.getInstance().updateDownloadEntity(downloadEntity)
        }
    }

    override fun onFailure(downloadEntity: DownloadEntity, exception: Exception) {
        mXapkUnzipThreadMap.remove(downloadEntity.path)

        AppExecutor.uiExecutor.execute {
            downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.FAILURE.name
            DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity)
            DataChanger.notifyDataChanged(downloadEntity)
            DownloadManager.getInstance().updateDownloadEntity(downloadEntity)
        }

        // 仅官网渠道上报 XAPK 异常信息
        if (HaloApp.getInstance().channel == "GH_206") {
            SentryHelper.onEvent(
                "XAPK_UNZIP_ERROR",
                "gameName", downloadEntity.name,
                "errorDigest", exception.localizedMessage
            )
        }

        debugOnly {
            Utils.log("unzip", "onFailure->$exception")
        }
    }

    override fun onSuccess(downloadEntity: DownloadEntity) {
        mXapkUnzipThreadMap.remove(downloadEntity.path)

        AppExecutor.uiExecutor.execute {
            val pkgPath = downloadEntity.meta[XAPK_PACKAGE_PATH_TAG]

            if (pkgPath == null) {
                Utils.toast(mContext, "下载出错，请重新下载！")

                return@execute
            }

            PackageInstaller.install(mContext, downloadEntity.isPlugin, pkgPath)

            downloadEntity.meta[XAPK_UNZIP_PERCENT] = "100.0"
            downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.SUCCESS.name
            DataChanger.notifyDataChanged(downloadEntity)
            DownloadManager.getInstance().updateDownloadEntity(downloadEntity)
        }

        debugOnly {
            Utils.log("unzip", "onSuccess->${downloadEntity.path}")
        }
    }

    @JvmStatic
    fun cancelUnzipTask(downloadEntity: DownloadEntity) {
        val xapkUnzipThread = mXapkUnzipThreadMap[downloadEntity.path]
        if (xapkUnzipThread != null) {
            xapkUnzipThread.canceled = true
        } else {
            onCancel(downloadEntity) // 刷新页面
        }
    }
}

enum class XapkUnzipStatus(status: String) {
    UNZIPPING("unzipping"),
    SUCCESS("success"),
    CANCEL("cancel"),
    FAILURE("failure");
}