package com.gh.common.util

import android.app.Activity
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider
import com.gh.common.dialog.InstallPermissionDialogFragment
import com.gh.common.xapk.XapkInstallReceiver
import com.gh.common.xapk.XapkInstaller
import com.gh.download.server.BrowserInstallHelper
import com.gh.gamecenter.BuildConfig
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.MD5Utils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.vspace.VHelper
import com.halo.assistant.HaloApp
import com.lightgame.download.DownloadEntity
import com.lightgame.utils.AppManager
import com.lightgame.utils.Utils
import java.io.File

// TODO 将弹窗改成以责任链模式来处理
object PackageInstaller {

    private val listeners = mutableListOf<OnInstallListener>()

    private val packageInstallerPackageName = arrayOf(
        "com.android.packageinstaller",
        "com.miui.packageinstaller",
        "com.google.android.packageinstaller",
        "com.samsung.android.packageinstaller",
        "com.lenovo",
        "com.zhuoyi"
    )

    /**
     * 为了兼容java代码
     */
    @JvmStatic
    fun install(context: Context, downloadEntity: DownloadEntity?) {
        downloadEntity?.let { install(context, downloadEntity, showUnzipToast = true, ignoreAsVGame = false) }
    }

    /**
     * 启动安装应用程序并取消通知栏安装消息
     *
     * 兼容所有包安装(apk,xapk),后续可加上apks
     *
     * 主动点击安装如果是xapk则需要toast提示
     *
     * @param ignoreAsVGame 是否不需要以畅玩形式安装
     *
     *
     */
    @JvmStatic
    fun install(context: Context,
                downloadEntity: DownloadEntity,
                showUnzipToast: Boolean,
                ignoreAsVGame: Boolean,
    ) {
        val isDownloadAsVGame = downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE) == Constants.VGAME
                || downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE) == Constants.DUAL_DOWNLOAD_VGAME

        val properContext = if (context is AppCompatActivity && !context.isFinishing) {
            context
        } else {
            AppManager.getInstance().currentActivity()
        }

        properContext ?: return

        if (!ignoreAsVGame && isDownloadAsVGame) {
            VHelper.install(properContext, downloadEntity)
            return
        }

        // 已知问题
        // 1. 此处可能遇到 activity 是 WXEntryActivity，因为 WXEntryActivity 不是 AppCompatActivity 调不起弹窗
        // 2. 当 activity 全部出栈，但是应用还在下载游戏，下载完会唤不起安装
        if (properContext is AppCompatActivity && !properContext.isFinishing) {
            InstallPermissionDialogFragment.show(properContext, downloadEntity) { isFromPermissionGrantedCallback ->
                // 取消状态栏下载完成的通知，若存在
                downloadEntity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION] = "YES"
                DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity)

                // 安卓12后的设备，需要重启应用来继续解压 XAPK 文件
                if (isFromPermissionGrantedCallback && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                    val pm = context.packageManager
                    val intent = pm.getLaunchIntentForPackage(context.packageName)
                    val mainIntent = Intent.makeRestartActivityTask(intent!!.component)
                    context.startActivity(mainIntent)
                    Runtime.getRuntime().exit(0)
                } else {
                    if (XapkInstaller.isXapk(downloadEntity)) {
                        XapkInstaller.install(context, downloadEntity, showUnzipToast)
                    } else {
                        install(context, downloadEntity.isPlugin, downloadEntity.path, downloadEntity)
                    }
                }
            }
        }
    }

    /**
     * 启动安装应用程序
     *
     * 除非特殊情况,请勿使用此方法进行应用安装操作
     * 除非你已经确定该文件一定是Apk
     *
     * @param downloadEntity 仅用来记录日志用，非必须可为空
     */
    @JvmStatic
    fun install(context: Context, isPluggin: Boolean = false, pkgPath: String?, downloadEntity: DownloadEntity? = null) {
        if (pkgPath.isNullOrEmpty()) {
            ToastUtils.toast("下载文件异常")
            return
        }

        val packageName = downloadEntity?.packageName ?: PackageUtils.getPackageNameByPath(context, pkgPath)

        packageName?.let {
            PackageChangeHelper.addInstallPendingPackage(packageName)
        }

        try {
            // 判断是否需要使用浏览器来进行安装
            if (BrowserInstallHelper.isUseBrowserToInstallEnabled()
                && BrowserInstallHelper.shouldUseBrowserToInstall()
            ) {
                BrowserInstallHelper.downloadFile(pkgPath)
                return
            }

            if (PackageUtils.isCanLaunchSetup(context, pkgPath)) {
                installWithPureModeHandled(
                    context,
                    pkgPath,
                    downloadEntity?.packageName,
                    downloadEntity?.gameId ?: "unknown",
                    downloadEntity?.name ?: "unknown",
                    downloadEntity?.categoryChinese ?: "unknown"
                )
            } else {
                if (isPluggin) {
                    DialogHelper.showPluginDialog(
                        context,
                        pluginDesc = downloadEntity?.pluginDesc,
                        gameId = downloadEntity?.gameId ?: "",
                        gameName = downloadEntity?.name ?: "",
                        gameType = downloadEntity?.categoryChinese ?: "",
                        platform = downloadEntity?.platform ?: ""
                    ) {
                        uninstall(context, pkgPath)
                    }
                } else {
                    // 非插件化的同包名不同签名冲突
                    DialogHelper.showSignatureConflictDialog(
                        context,
                        gameId = downloadEntity?.gameId ?: "",
                        gameName = downloadEntity?.name ?: "",
                        gameType = downloadEntity?.categoryChinese ?: ""
                    ) {
                        uninstall(context, pkgPath)
                    }
                }
            }
        } catch (e: Exception) {
            Utils.toast(context, e.message)
        }
    }

    /**
     * 处理纯净模式后的安装
     */
    private fun installWithPureModeHandled(
        context: Context,
        pkgPath: String,
        pkgName: String?,
        gameId: String,
        gameName: String,
        gameType: String,
    ) {
        PureModeHelper.handlePureModeIfNeeded(context, gameId, gameName, gameType) {
            install(context, pkgPath, pkgName)
        }
    }

    /**
     * 最终执行安装的方法
     */
    private fun install(context: Context, pkgPath: String, pkgName: String?) {
        HaloApp.put(Constants.LAST_INSTALL_GAME, pkgPath)

        val installIntent = getInstallIntent(context, pkgPath)
        context.startActivity(installIntent)

        dispatchOnInstallListener(pkgPath, pkgName)
    }

    fun installMultiple(
        context: Context,
        pkgName: String,
        pkgPath: String,
        sessionId: Int = -1
    ) {
        val installer = context.packageManager.packageInstaller
        val session = installer.openSession(sessionId)
        // 监听安装回调的组件，可以是Activity、Service或者是BroadcastReceiver
        val intent = Intent(context, XapkInstallReceiver::class.java)
            .also {
                it.putExtra(XapkInstallReceiver.KEY_PACKAGE_PATH, pkgPath)
            }

        val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
        } else {
            PendingIntent.FLAG_CANCEL_CURRENT
        }
        val pendingIntent = PendingIntent.getActivity(context, sessionId, intent, flags)
        // 提交数据流并执行安装
        session.commit(pendingIntent.intentSender)

        dispatchOnInstallListener(pkgName, pkgPath)
    }

    fun registerOnInstallListener(listener: OnInstallListener) {
        if (!listeners.contains(listener)) {
            listeners.add(listener)
        }
    }

    fun unregisterOnInstallListener(listener: OnInstallListener) {
        listeners.remove(listener)
    }

    private fun dispatchOnInstallListener(pkgPath: String, pkgName: String?) {
        for (listener in listeners) {
            listener.onInstalling(pkgName, pkgPath)
        }
    }

    /**
     * 获取启动安装意图
     *
     * 除非特殊情况,请勿使用此方法进行应用安装操作
     * 除非你已经确定该文件一定是Apk且该次安装无需检查签名情况
     */
    @JvmStatic
    fun getInstallIntent(context: Context, path: String): Intent {
        var uri = Uri.fromFile(File(path))
        val installIntent =
            if (BrowserInstallHelper.isUseBrowserToInstallEnabledWithPackageMatched()) {
                Intent(Intent.ACTION_INSTALL_PACKAGE)
            } else {
                Intent(Intent.ACTION_VIEW)
            }
        if ("smartisan" == Build.MANUFACTURER) {
            installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID, File(path))
            installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            installIntent.setDataAndType(uri, "application/vnd.android.package-archive")
            installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        } else {
            // 应用内更新不加 FLAG_ACTIVITY_NEW_TASK 在模拟器上会出现安装完成后安装界面也一并消失的类似闪退的表现
            // Application 上下文就更不用说了
            val pkgName = PackageUtils.getPackageNameByPath(context, path)
            if (pkgName == context.packageName || context !is Activity) {
                installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
            }
            installIntent.setDataAndType(uri, "application/vnd.android.package-archive")
        }

        // 优选系统的安装器（遇到 Exception 就回落到正常的安装器）
        try {
            updateSystemInstallerIfAvailable(context, installIntent)
        } catch (ignored: Exception) {
            // ignored
        }

        InstallUtils.getInstance()
            .addInstall(PackageUtils.getPackageNameByPath(context, path))
        return installIntent
    }

    /**
     * 查找并设置系统安装器作为安装 intent 的包名
     */
    private fun updateSystemInstallerIfAvailable(context: Context, intent: Intent) {
        for (rf in context.packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)) {
            val targetPackageName = packageInstallerPackageName.firstOrNull { rf.activityInfo.packageName.contains(it) }
            if (!targetPackageName.isNullOrEmpty()) {
                intent.setPackage(targetPackageName)
                break
            }
        }
    }

    /**
     * 卸载应用
     */
    @JvmStatic
    fun uninstall(context: Context, path: String) {
        // TODO 根据 path 获取包名可能出现获取不到的情况，看能不能改成直接用包名！
        val packageName = PackageUtils.getPackageNameByPath(context, path)
        uninstallForPackageName(context, packageName)
    }

    /**
     * 根据包名卸载应用
     */
    @JvmStatic
    fun uninstallForPackageName(context: Context, pkn: String?) {
        if (pkn.isNullOrEmpty()) return

        PackageChangeHelper.addUninstallPendingPackage(pkn)

        val uninstallIntent = Intent()
        uninstallIntent.action = Intent.ACTION_DELETE
        uninstallIntent.addCategory(Intent.CATEGORY_DEFAULT)
        if (context !is Activity) {
            uninstallIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        }
        uninstallIntent.data = Uri.parse("package:$pkn")
        InstallUtils.getInstance().addUninstall(pkn)
        context.startActivity(uninstallIntent)
    }

    @JvmStatic
    fun getDownloadPathWithId(id: String, format: String?): String {
        return FileUtils.getDownloadPath(
            HaloApp.getInstance().application,
            id + "." + getFileSuffixByFormat(format)
        )
    }

    private fun getFileSuffixByFormat(format: String?): String {
        return if (format == Constants.XAPK_FORMAT
            || format == Constants.XAPK_APKS_FORMAT) {
            XapkInstaller.XAPK_EXTENSION_NAME
        } else {
            "apk"
        }
    }

    @JvmStatic
    fun createDownloadId(gameName: String?): String {
        return MD5Utils.getContentMD5(gameName + "_" + System.currentTimeMillis())
    }

    interface OnInstallListener {
        fun onInstalling(packageName: String?, packagePath: String)
    }

}