package com.gh.vspace

import android.content.Context
import androidx.annotation.WorkerThread
import com.gh.download.simple.DownloadMessageHandler
import com.gh.download.simple.SimpleDownloadManager
import com.gh.gamecenter.common.utils.tryWithDefaultCatch
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.core.utils.MD5Utils
import com.gh.gamecenter.entity.ArchiveEntity
import com.gh.vspace.db.VArchiveEntity
import com.gh.vspace.db.VGameDatabase
import com.halo.assistant.HaloApp
import com.lg.download.DownloadStatus
import com.lg.download.httpclient.DefaultHttpClient
import com.lg.ndownload.DownloadConfigBuilder
import com.lg.vspace.VirtualAppManager
import com.lightgame.utils.Utils
import java.io.File

object VArchiveHelper {

    private val mVArchiveDao by lazy { VGameDatabase.instance.vArchiveDao() }

    private var mLatestArchiveFile: File? = null // 最近一个用于保存的存档文件
    private var mSaveArchiveListener: ((VArchiveEntity) -> Unit)? = null
    private var mApplyArchiveListener: ((String, Boolean) -> Unit)? = null

    private var mVArchiveEntityList: ArrayList<VArchiveEntity> = arrayListOf()
    private var mVArchiveMd5Set: HashSet<String> = hashSetOf()

    private var mLatestDownloadingArchiveEntity: ArchiveEntity? = null // 最近一个正在下载的存档实例

    // 本地存档列表的 LiveData
    val vArchiveEntityListLiveData by lazy { mVArchiveDao.getAllLiveData() }

    // 本地存档存放的位置
    val archivePath by lazy { HaloApp.getInstance().filesDir.absolutePath + File.separator }

    fun init() {
        DownloadMessageHandler.registerGlobalStatusChangedListener { simpleDownloadEntity, downloadStatus ->
            when (downloadStatus) {
                DownloadStatus.COMPLETED -> {
                    mLatestDownloadingArchiveEntity?.let {
                        Utils.log(VHelper.LOG_TAG, "新的存档 ${mLatestDownloadingArchiveEntity?.id} 下载完成")
                        if (it.id == simpleDownloadEntity.id) {
                            val vArchiveEntity = VArchiveEntity(
                                id = it.id,
                                gameId = it.gameId,
                                descContent = it.desc,
                                name = it.name,
                                md5 = it.md5,
                                type = 1,
                                filePath = simpleDownloadEntity.dirPath + simpleDownloadEntity.fileName,
                                gameVersion = it.gameVersion,
                                time = it.time.update
                            )

                            Utils.log(VHelper.LOG_TAG, "新的存档 ${it.id} 保存至数据库中")
                            mVArchiveDao.insert(vArchiveEntity)
                        }
                        mLatestDownloadingArchiveEntity = null
                    }
                }
                else -> {
                    // do nothing
                }
            }
        }

        vArchiveEntityListLiveData.observeForever {
            // 更新已拥有的存档数据
            val archiveMd5Set = hashSetOf<String>()
            mVArchiveEntityList = ArrayList(it)
            for (vArchiveEntity in mVArchiveEntityList) {
                archiveMd5Set.add(vArchiveEntity.md5)
            }
            mVArchiveMd5Set = archiveMd5Set
        }
    }

    /**
     * 下载新的存档
     */
    fun downloadArchive(archive: ArchiveEntity) {
        Utils.log(VHelper.LOG_TAG, "下载新的存档 ${archive.id}")

        mLatestDownloadingArchiveEntity = archive

        // 因为没有下载管理，所以每次下载前都需要清理旧的下载，避免无法下载
        SimpleDownloadManager.cancel(archive.id)
        SimpleDownloadManager.download(
            DownloadConfigBuilder()
                .setUniqueId(archive.id)
                .setFileName(archive.id + ".zip")
                .setUrl(archive.url)
                .setPathToStore(archivePath)
                .setHttpClient(DefaultHttpClient())
                .setDownloadThreadSize(2)
                .setDownloadListener(DownloadMessageHandler)
                .setDownloadExecutor(AppExecutor.downloadExecutor)
                .build()
        )
    }

    /**
     * 创建游戏存档
     * @param packageName 游戏包名
     * @param config 该游戏对应的配置 (不是 url，是 config 的内容)
     * @param archiveFile 游戏存档需要保存的 file (用 MD5 哈希当前时间戳来当文件名)
     */
    fun saveGameArchive(
        context: Context,
        packageName: String,
        config: String,
        archiveFile: File,
        saveArchiveListener: ((VArchiveEntity) -> Unit)? = null
    ) {
        mSaveArchiveListener = saveArchiveListener
        // 记录最近一次创建的存档文件
        mLatestArchiveFile = archiveFile
        val intent = VirtualAppManager.getGenerateArchiveIntent(context, config, packageName, archiveFile)
        context.startActivity(intent)
    }

    /**
     * 使用游戏存档
     * @param packageName 游戏包名
     * @param archiveFile 游戏存档的 file
     */
    fun applyGameArchive(
        context: Context,
        packageName: String,
        archiveFile: File,
        applyArchiveListener: ((String, Boolean) -> Unit)? = null
    ) {
        mApplyArchiveListener = applyArchiveListener
        val intent = VirtualAppManager.getUseArchiveIntent(context, packageName, archiveFile)
        context.startActivity(intent)
    }

    /**
     * 创建游戏存档结束回调
     * @param packageName 游戏包名
     * @param isSuccess 保存存档是否成功
     */
    fun onSaveGameArchiveFinished(packageName: String, isSuccess: Boolean) {
        if (isSuccess) {
            val vGameEntity = VHelper.getVGameSnapshot(packageName = packageName)
            runOnIoThread {
                val fileMd5 = MD5Utils.calculateMD5(mLatestArchiveFile)
                val vArchiveEntity = VArchiveEntity(
                    id = fileMd5,
                    gameId = vGameEntity?.downloadEntity?.gameId ?: "",
                    md5 = fileMd5,
                    filePath = mLatestArchiveFile?.absolutePath ?: "",
                    time = System.currentTimeMillis(),
                    type = 0,
                    gameVersion = vGameEntity?.downloadEntity?.versionName ?: "",
                )

                mVArchiveDao.insert(vArchiveEntity)

                runOnUiThread {
                    mSaveArchiveListener?.invoke(vArchiveEntity)
                }

                mLatestArchiveFile = null
            }
        }
    }

    /**
     * 使用游戏存档结束回调
     * @param packageName 游戏包名
     * @param isSuccess 应用存档是否成功
     */
    fun onApplyGameArchiveFinished(packageName: String, isSuccess: Boolean) {
        mApplyArchiveListener?.invoke(packageName, isSuccess)
    }

    /**
     * 本地是否已经拥有该 md5 对应的存档
     */
    fun isArchiveDownloaded(md5: String): Boolean = mVArchiveMd5Set.contains(md5)

    /**
     * 根据 md5 获取对应的存档文件 (file)
     */
    fun getArchiveFile(md5: String): File? {
        for (vArchiveEntity in mVArchiveEntityList) {
            if (md5 == vArchiveEntity.md5) {
                return File(vArchiveEntity.filePath)
            }
        }
        return null
    }

    /**
     * 根据 md5 获取对应的存档文件路径
     */
    fun getArchiveFilePath(md5: String): String {
        for (vArchiveEntity in mVArchiveEntityList) {
            if (md5 == vArchiveEntity.md5) {
                return vArchiveEntity.filePath
            }
        }
        return "";
    }

    /**
     * 根据 md5 删除对应的存档文件
     */
    fun deleteArchive(md5: String) {
        for (vArchiveEntity in mVArchiveEntityList) {
            if (md5 == vArchiveEntity.md5) {
                runOnIoThread {
                    mVArchiveDao.delete(vArchiveEntity)
                    tryWithDefaultCatch {
                        File(vArchiveEntity.filePath).delete()
                    }
                }
                break
            }
        }
    }

    /**
     * 清空存档文件
     */
    @WorkerThread
    fun clearAllArchives() {
        mVArchiveDao.clearAllArchives()
    }

    /**
     * 设置创建游戏存档成功结果监听
     */
    fun setSaveArchiveListener(listener: (VArchiveEntity) -> Unit) {
        mSaveArchiveListener = listener
    }

    /**
     * 移除创建游戏存档成功结果监听
     */
    fun clearSaveArchiveListener() {
        mSaveArchiveListener = null
    }

    /**
     * 设置使用游戏存档成功结果监听
     */
    fun setApplyArchiveListener(listener: (String, Boolean) -> Unit) {
        mApplyArchiveListener = listener
    }

    /**
     * 移除使用游戏存档成功结果监听
     */
    fun clearApplyArchiveListener() {
        mApplyArchiveListener = null
    }
}