package com.test.basd.smartjunkcleaner.activity

import android.annotation.SuppressLint
import android.app.AppOpsManager
import android.app.usage.UsageStats
import android.app.usage.UsageStatsManager
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.util.Log
import android.view.View
import androidx.activity.addCallback
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.test.basd.smartjunkcleaner.R
import com.test.basd.smartjunkcleaner.bean.AppBean
import com.test.basd.smartjunkcleaner.databinding.ActivityAppManagerBinding
import com.test.basd.smartjunkcleaner.fragment.AppListFragment
import com.test.basd.smartjunkcleaner.fragment.AppListFragment.Companion.APP_LIST_TYPE_INSTALL
import com.test.basd.smartjunkcleaner.fragment.AppListFragment.Companion.APP_LIST_TYPE_LAST_USE
import com.test.basd.smartjunkcleaner.fragment.AppListFragment.Companion.APP_LIST_TYPE_NAME
import com.test.basd.smartjunkcleaner.fragment.AppListFragment.Companion.APP_LIST_TYPE_SIZE
import com.test.basd.smartjunkcleaner.helps.ActivityLauncher
import com.test.basd.smartjunkcleaner.helps.AppSizeUtils
import com.test.basd.smartjunkcleaner.helps.BaseActivity
import com.test.basd.smartjunkcleaner.helps.LogEx
import com.test.basd.smartjunkcleaner.helps.TimeUtils.THIRTY_DAYS_QUERY
import com.test.basd.smartjunkcleaner.helps.TimeUtils.timePair
import com.test.basd.smartjunkcleaner.helps.ads.AdmobUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONObject
import java.util.concurrent.LinkedBlockingDeque


/**
 * 所需权限:
 * 使用情况 last use
 * android.permission.PACKAGE_USAGE_STATS
 * 卸载
 * android.permission.REQUEST_DELETE_PACKAGES
 */
class AppManagerActivity : BaseActivity<ActivityAppManagerBinding>() {
    override val isLightMode = true
    private lateinit var launcher: ActivityLauncher
    private val dataList = ArrayList<AppBean>()
    private val pages = arrayListOf<Fragment>()
    private lateinit var pagerAdapter: ScreenSlidePagerAdapter
    private val TAG: String = "AppManager2Activity"
    private var currentPosition: Int = 0
    private lateinit var context: Context
    var animationFinish: Boolean = false

    override val binding: ActivityAppManagerBinding by lazy {
        ActivityAppManagerBinding.inflate(layoutInflater)
    }

    override fun initView() {
        context = this
        launcher = ActivityLauncher(this)
        setContentView(binding.root)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }

        initViewPager()
        playLottie()
        CoroutineScope(Dispatchers.Main).launch {

            initAppData()

            if (checkUsageAccessSettings()) {
                async(Dispatchers.IO) {
                    setAppBeanSize()
                    setAppLastUseTime()
                }.await()
            }

            pages.forEachIndexed { index, fragment ->
                val appListFragment = (fragment as AppListFragment)
                appListFragment.refreshFragmentData(dataList, index == currentPosition)
            }
        }

        binding.llName.setOnClickListener {
            if (currentPosition == 0) {
                val fragment = (pages[0] as AppListFragment)
                fragment.reverseOrder()
                val isAsc = fragment.isAsc
                changeAsc(0, isAsc)
            }
            binding.viewpager2.setCurrentItem(0, false)
        }
        binding.llInstall.setOnClickListener {
            if (currentPosition == 1) {
                val fragment = (pages[1] as AppListFragment)
                fragment.reverseOrder()
                val isAsc = fragment.isAsc
                changeAsc(1, isAsc)
            }
            binding.viewpager2.setCurrentItem(1, false)
        }
        binding.llSize.setOnClickListener {
            if (currentPosition == 2) {
                val fragment = (pages[2] as AppListFragment)
                fragment.reverseOrder()
                val isAsc = fragment.isAsc
                changeAsc(2, isAsc)
            }
            binding.viewpager2.setCurrentItem(2, false)
        }
        binding.llLastUse.setOnClickListener {
            if (currentPosition == 3) {
                val fragment = (pages[3] as AppListFragment)
                fragment.reverseOrder()
                val isAsc = fragment.isAsc
                changeAsc(3, isAsc)
            }
            binding.viewpager2.setCurrentItem(3, false)
        }
        binding.flBack.setOnClickListener {
            AdmobUtils.showInterstitialAd(this) {
                finishToMain()
            }
        }
        onBackPressedDispatcher.addCallback {
            AdmobUtils.showInterstitialAd(this@AppManagerActivity) {
                finishToMain()
            }
        }

    }

    private fun playLottie() {
        LogEx.logDebug(TAG, "playLottie")
        binding.llLottie.isVisible = true
        binding.lottie.imageAssetsFolder = "guan_sjunk_li/images/"
        binding.lottie.setAnimation("guan_sjunk_li/data.json")
        binding.lottie.playAnimation()
        binding.root.postDelayed({
            AdmobUtils.showInterstitialAd(this) {
                animationFinish = true
                binding.llLottie.isVisible = false
            }
        }, 6000)
    }

    fun refreshUsageAccessData() {
        val obj = JSONObject()
        obj.put("activity", javaClass.simpleName)

        if (checkUsageAccessSettings()) {

            pages.forEachIndexed { index, fragment ->
                val appListFragment = (fragment as AppListFragment)
                appListFragment.showContent(index == currentPosition)
            }
            lifecycleScope.launch(Dispatchers.IO) {

                setAppBeanSize()
                setAppLastUseTime()

                launch(Dispatchers.Main) {
                    pages.forEachIndexed { index, fragment ->
                        val appListFragment = (fragment as AppListFragment)
                        appListFragment.refreshFragmentData(dataList, index == currentPosition, index)
                    }
                }
            }
        }
    }

    private fun changeAsc(position: Int, isAsc: Boolean) {
        when (position) {
            0 -> {
                binding.arrowInstall.setAscOrder(false)
                binding.arrowSize.setAscOrder(false)
                binding.arrowLastUse.setAscOrder(false)

                binding.arrowName.setAscOrder(true, isAsc)
            }

            1 -> {
                binding.arrowName.setAscOrder(false)
                binding.arrowSize.setAscOrder(false)
                binding.arrowLastUse.setAscOrder(false)

                binding.arrowInstall.setAscOrder(true, isAsc)
            }

            2 -> {
                binding.arrowName.setAscOrder(false)
                binding.arrowInstall.setAscOrder(false)
                binding.arrowLastUse.setAscOrder(false)

                binding.arrowSize.setAscOrder(true, isAsc)
            }

            3 -> {
                binding.arrowName.setAscOrder(false)
                binding.arrowInstall.setAscOrder(false)
                binding.arrowSize.setAscOrder(false)

                binding.arrowLastUse.setAscOrder(true, isAsc)
            }
        }
    }


    private fun changeIndicator(position: Int) {
        when (position) {
            0 -> {
                binding.vName.visibility = View.VISIBLE
                binding.vInstall.visibility = View.INVISIBLE
                binding.vSize.visibility = View.INVISIBLE
                binding.vLastUse.visibility = View.INVISIBLE
            }

            1 -> {
                binding.vName.visibility = View.INVISIBLE
                binding.vInstall.visibility = View.VISIBLE
                binding.vSize.visibility = View.INVISIBLE
                binding.vLastUse.visibility = View.INVISIBLE
            }

            2 -> {
                binding.vName.visibility = View.INVISIBLE
                binding.vInstall.visibility = View.INVISIBLE
                binding.vSize.visibility = View.VISIBLE
                binding.vLastUse.visibility = View.INVISIBLE
            }

            3 -> {
                binding.vName.visibility = View.INVISIBLE
                binding.vInstall.visibility = View.INVISIBLE
                binding.vSize.visibility = View.INVISIBLE
                binding.vLastUse.visibility = View.VISIBLE
            }
        }
    }

    private fun initViewPager() {
        pages.add(AppListFragment().apply { setInitData(launcher, APP_LIST_TYPE_NAME, true) })
        pages.add(AppListFragment().apply { setInitData(launcher, APP_LIST_TYPE_INSTALL, false) })
        pages.add(AppListFragment().apply { setInitData(launcher, APP_LIST_TYPE_SIZE, false, needPermission = true) })
        pages.add(AppListFragment().apply { setInitData(launcher, APP_LIST_TYPE_LAST_USE, false, needPermission = true) })


        pagerAdapter = ScreenSlidePagerAdapter(this@AppManagerActivity)

        binding.viewpager2.adapter = pagerAdapter

        binding.viewpager2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
                super.onPageScrolled(position, positionOffset, positionOffsetPixels)
                currentPosition = position
                changeIndicator(position)
                val fragment = (pages[position] as AppListFragment)
                fragment.setDataRefresh(dataList)
                changeAsc(position, fragment.isAsc)
            }
        })
    }


    @SuppressLint("QueryPermissionsNeeded")
    private fun initAppData() {
        dataList.clear()

        val pm = packageManager
        val packages = pm.getInstalledPackages(0)

        packages.forEach { app ->
            if (isLaunchApp(context, app)) {

                val appBean = AppBean(
                    app.applicationInfo.loadIcon(pm),
                    app.applicationInfo.loadLabel(pm).toString(),
                    app.applicationInfo.packageName,
                    false
                )

                //安装时间
                setAppInstallTime(appBean, context)
                dataList.add(appBean)
            }
        }
        LogEx.logDebug(TAG, "dataList.size=${dataList.size}")
    }

    private fun setAppBeanSize() {
        dataList.forEach { app ->
            val size = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                AppSizeUtils.instance?.getAppSizeO(context, app.pkg)
            } else {
                AppSizeUtils.instance?.getAppSize(context, app.pkg)
            }
            app.appSize = size ?: 0
        }
    }

    private fun setAppLastUseTime(
        queryTime: Int = THIRTY_DAYS_QUERY
    ) {
        val pair = timePair(queryTime)
        val startTime: Long = pair.first
        val endTime: Long = pair.second

        val usageList = getUsageList(context, startTime, endTime)

        dataList.forEach { app ->
            val filterList = usageList.filter { it.packageName == app.pkg }
            app.usageStats = filterList
            app.lastUsedTime = if (filterList.isNotEmpty()) filterList.maxOf { it.lastTimeUsed } else -2//默认30天没数据
        }
    }

    @SuppressLint("NotifyDataSetChanged")
    fun otherPageRemove(appBean: AppBean, fragment: AppListFragment) {
        dataList.remove(appBean)
        pages.forEach {
            if (it != fragment) {
                (it as AppListFragment).setData(dataList)
            }
        }
    }


    private fun allPageRemove(unInstalled: List<AppBean>) {
        Log.e(TAG, "unInstalled=$unInstalled")
        dataList.removeAll(unInstalled.toSet())

        pages.forEachIndexed { index, fragment ->
            val fg = fragment as AppListFragment
            if (index == currentPosition) {
                Log.e(TAG, "dataList=$dataList currentPosition=$currentPosition")
                lifecycleScope.launch(Dispatchers.Main) {
                    delay(1500)
                    fg.setDataRefresh(dataList)
                }
            } else {
                Log.e(TAG, "dataList=$dataList index=$index")
                fg.setData(dataList)
            }
        }
    }

    @SuppressLint("StringFormatMatches", "SetTextI18n")
    fun showUnInstall(selectList: List<AppBean>) {

        if (selectList.isNotEmpty()) {
            binding.tvUninstall.isVisible = true
            binding.tvUninstall.text = "Uninstall(${selectList.size})"
            binding.tvUninstall.setOnClickListener {
                binding.tvUninstall.isVisible = false
                binding.tvUninstall.setOnClickListener { }
                unInstallApps(selectList) { unInstalled ->
                    allPageRemove(unInstalled)
                }
            }
        } else {
            binding.tvUninstall.isVisible = false
            binding.tvUninstall.setOnClickListener { }
        }


    }

    private fun unInstallApps(selectList: List<AppBean>, finishUnInstall: (unInstalled: List<AppBean>) -> Unit) =
        lifecycleScope.launch(Dispatchers.IO) {

            val unInstall = arrayListOf<AppBean>()//待卸载列表
            unInstall.addAll(selectList)

            val unInstalled = arrayListOf<AppBean>()//已卸载列表

            val queue = LinkedBlockingDeque<AppBean>()

            val first = unInstall[0]
            queue.put(first)

            while (isActive) {
                val appBean = withContext(Dispatchers.IO) {
                    queue.take()
                }
                delay(250)
                LogEx.logDebug(TAG, "循环。。。。。。。。。。。。。。。。。。。。。。。。")
                val intent = Intent(Intent.ACTION_DELETE, Uri.parse("package:${appBean.pkg}"))

                launcher.launch(intent) {

                    val flag = isInstalled(
                        this@AppManagerActivity,
                        appBean.pkg,
                        error = {
                            unInstall.remove(appBean)
                            unInstalled.add(appBean)
                        })

                    if (!flag) {
                        unInstalled.add(appBean)
                    }
                    unInstall.remove(appBean)

                    if (unInstall.isNotEmpty()) {
                        val next = unInstall[0]
                        LogEx.logDebug(TAG, "卸载回调 next=$next")
                        queue.put(next)
                    } else {
                        LogEx.logDebug(TAG, "卸载完成回调 finishUnInstall unInstalled=$unInstalled")
                        finishUnInstall.invoke(unInstalled)
                        cancel()
                    }
                }
            }

        }

    inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
        override fun getItemCount(): Int = pages.size
        override fun createFragment(position: Int): Fragment = pages[position]
    }


    fun checkUsageAccessSettings(): Boolean {

        val appOpsManager = getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager

        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            appOpsManager.unsafeCheckOpNoThrow(
                AppOpsManager.OPSTR_GET_USAGE_STATS,
                android.os.Process.myUid(),
                this.packageName
            ) == AppOpsManager.MODE_ALLOWED
        } else {
            appOpsManager.checkOpNoThrow(
                AppOpsManager.OPSTR_GET_USAGE_STATS,
                android.os.Process.myUid(),
                this.packageName
            ) == AppOpsManager.MODE_ALLOWED
        }
    }

    fun isLaunchApp(context: Context, app: PackageInfo, filterSystem: Boolean = true): Boolean {

        val flagSystem = (app.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM) < 1
        val flag = if (filterSystem) flagSystem else true

        return flag && app.applicationInfo.packageName != context.packageName
    }

    fun setAppInstallTime(appBean: AppBean, context: Context) {
        //安装时间
        var firstInstallTime = 0L
        try {
            val packageManager = context.packageManager
            val packageInfo: PackageInfo = packageManager.getPackageInfo(appBean.pkg, 0)
            //应用装时间
            firstInstallTime = packageInfo.firstInstallTime
            //应用最后一次更新时间
            val lastUpdateTime = packageInfo.lastUpdateTime
//                        LogEx.logDebug(TAG, "lastUpdateTime=$lastUpdateTime")
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
        }

        appBean.installTime = firstInstallTime
    }

    /**
     * 前台运行App+后台运行App=总的启动App
     */
    private fun getUsageList(context: Context, startTime: Long, endTime: Long): ArrayList<UsageStats> {
        val list = arrayListOf<UsageStats>()
        val mUsmManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
        val map = mUsmManager.queryAndAggregateUsageStats(startTime, endTime)
        map.values.forEach { stats ->
            if (stats.totalTimeInForeground > 0) {
                list.add(stats)
            } else {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    if (stats.totalTimeForegroundServiceUsed > 0) {
                        list.add(stats)
                    }
                }
            }

        }
        return list
    }

    private fun isInstalled(context: Context, pkg: String, error: (() -> Unit)? = null): Boolean {
        return try {
            context.packageManager.getPackageInfo(pkg, 0)
            true
        } catch (e: Exception) {
            error?.invoke()
            false
        }
    }


    companion object {

    }
}


