@file:Suppress("DEPRECATED_IDENTITY_EQUALS")

package com.gh.common.util

import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.app.Activity
import android.app.AppOpsManager
import android.app.AppOpsManager.MODE_ALLOWED
import android.app.AppOpsManager.OPSTR_GET_USAGE_STATS
import android.app.usage.UsageEvents
import android.app.usage.UsageStatsManager
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Process
import android.preference.PreferenceManager
import android.provider.Settings
import androidx.annotation.RequiresApi
import com.gh.common.util.UsageStatsHelper.getDataByUsageEvents
import com.gh.common.util.UsageStatsHelper.getDataByUsageStats
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.utils.debugOnly
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import com.lightgame.utils.Utils
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.ResponseBody
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.util.*

/**
 * 参考资料： https://developer.android.com/reference/android/app/usage/UsageStatsManager
 *
 * queryUsageStats 无法准确获取设定时间内的使用情况，所以以下统计有两个策略(但依然无法解决queryUsageStats初始取值问题)
 *
 * [getDataByUsageStats]:用于获取时间跨度较大的数据(至少跨度一天)
 * [getDataByUsageEvents]:用于获取时间跨度较小的数据(只用于获取当天数据)
 *
 * 详情可见：https://stackoverflow.com/questions/36238481/android-usagestatsmanager-not-returning-correct-daily-results
 *
 */
object UsageStatsHelper {

    const val USAGE_STATUS_REQUEST_CODE = 233
    const val USAGE_STATUS_SP_KEY = "usage_status_sp_key"

    private val mApi by lazy { RetrofitManager.getInstance().api }
    private val mPreference by lazy { PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance().application) }

    /**
     * 一般用于获取时间跨度较大的数据
     *
     * 当天数据获取是有问题的
     * 例如: 设置起始时间为上午10点，当时返回的数据还是包括更前的数据（例如9点）
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
    private fun getDataByUsageStats(lastPostTime: Long): JSONArray? {
        val usageStatsManager = HaloApp
            .getInstance().application
            .getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager

        val usageStatsMap = usageStatsManager.queryAndAggregateUsageStats(
            lastPostTime,
            System.currentTimeMillis()
        ) ?: return null

        val postBody = JSONArray()
        for (entry in usageStatsMap) {
            if (entry.value.totalTimeInForeground != 0L) {
                val jsonObject = JSONObject()
                jsonObject.put("package", entry.key)
                jsonObject.put("duration", (entry.value.totalTimeInForeground / 1000))
                postBody.put(jsonObject)

                debugOnly {
                    Utils.log("UsageStats post log==>" + entry.key + ":" + (entry.value.totalTimeInForeground / 1000))
                }
            }
        }

        return postBody
    }

    /**
     * 一般用于获取时间跨度较小的数据
     * 在此功能只用来获取当天数据(queryEvents 获取的数据只保留几天内的数据)
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
    private fun getDataByUsageEvents(startTime: Long): JSONArray? {

        val mUsageStatsManager = HaloApp
            .getInstance()
            .application
            .getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager

        val usageEvents = mUsageStatsManager.queryEvents(startTime, System.currentTimeMillis())
        val allEvents = ArrayList<UsageEvents.Event>()
        var currentEvent: UsageEvents.Event



        while (usageEvents.hasNextEvent()) {
            currentEvent = UsageEvents.Event()
            usageEvents.getNextEvent(currentEvent)
            if (currentEvent.eventType == UsageEvents.Event.MOVE_TO_FOREGROUND
                || currentEvent.eventType == UsageEvents.Event.MOVE_TO_BACKGROUND
            ) {
                allEvents.add(currentEvent)
            }
        }

        val pakAndTime = HashMap<String, Long>()
        for (i in 0 until allEvents.size - 1) {
            val curEvent = allEvents[i]
            val nextEvent = allEvents[i + 1]

            if (curEvent.eventType == UsageEvents.Event.MOVE_TO_FOREGROUND
                && nextEvent.eventType == UsageEvents.Event.MOVE_TO_BACKGROUND
                && curEvent.className == nextEvent.className
            ) {
                val diff = nextEvent.timeStamp - curEvent.timeStamp
                val packageName = curEvent.packageName ?: continue

                if (pakAndTime[packageName] == null) {
                    pakAndTime[packageName] = diff
                } else {
                    pakAndTime[packageName] = pakAndTime[packageName]!! + diff
                }
            }
        }

        val postBody = JSONArray()
        for (pkg in pakAndTime.keys) {
            val totalTime = pakAndTime[pkg] ?: return null
            val jsonObject = JSONObject()
            jsonObject.put("package", pkg)
            jsonObject.put("duration", (totalTime / 1000))
            postBody.put(jsonObject)
            Utils.log("UsageStats getDataByUsageEvents->" + pkg + " :" + (totalTime / 1000))
        }

        return postBody
    }

    /**
     * 第一次统计默认起始时间
     */
    private fun getDefaultBeginTime(): Long {
        val calendar = Calendar.getInstance()
        calendar.add(Calendar.YEAR, -5)
        return calendar.timeInMillis
    }

    @SuppressLint("CheckResult")
    @TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
    private fun postUsageStats(beginTime: Long) {
        debugOnly {
            Utils.log("UsageStats->beginTime:$beginTime endTime:" + System.currentTimeMillis())
        }

        val postBody = if (isSameDay(beginTime, System.currentTimeMillis())) {
            getDataByUsageEvents(beginTime)
        } else {
            getDataByUsageStats(beginTime)
        }

        if (postBody == null || postBody.length() == 0) {
            debugOnly {
                Utils.log("UsageStats: 没有可上传的数据")
            }
            return
        }

        val body = postBody.toString().toRequestBody("application/json".toMediaTypeOrNull())
        mApi.postUsageStatus(body, UserManager.getInstance().userId)
            .subscribe(object : BiResponse<ResponseBody>() {
                override fun onSuccess(data: ResponseBody) {
                    debugOnly {
                        Utils.log("UsageStats: 数据上传成功")
                    }
                }
            })
    }

    @JvmStatic
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
    fun checkForPermission(): Boolean {
        val appOps = HaloApp
            .getInstance().application
            .getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
        val mode = appOps.checkOpNoThrow(
            OPSTR_GET_USAGE_STATS,
            Process.myUid(),
            HaloApp.getInstance().application.packageName
        )

        return mode == MODE_ALLOWED
    }

    @JvmStatic
    @SuppressLint("CheckResult")
    fun checkAndPostUsageStats() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
            debugOnly {
                Utils.log("UsageStats checkAndPostUsageStats Android 版本小于22")
            }
            return
        }
        if (!checkForPermission()) {
            debugOnly {
                Utils.log("UsageStats checkAndPostUsageStats 没有授予权限")
            }
            return
        }
        if (!mPreference.getBoolean(USAGE_STATUS_SP_KEY, true)) {
            debugOnly {
                Utils.log("UsageStats checkAndPostUsageStats 有权限,但用户在设置关闭统计")
            }
            return
        }

        mApi.getUsageStatusUpdateTime(HaloApp.getInstance().gid)
            .subscribe(object : BiResponse<ResponseBody>() {
                override fun onSuccess(data: ResponseBody) {
                    try {
                        val body = JSONObject(data.string())
                        val lastPostTime = body.getLong("update_time") * 1000

                        val beginTime = if (lastPostTime == 0L) {
                            getDefaultBeginTime()
                        } else {
                            lastPostTime
                        }
                        postUsageStats(beginTime)
                    } catch (e: JSONException) {
                        Utils.log("UsageStats: 获取上次上传时间失败，错误信息：${e.message}")
                    }
                }
            })
    }

    private fun isSameDay(timeA: Long, timeB: Long): Boolean {
        if (timeA == timeB) {
            return true
        }

        val calendarB = Calendar.getInstance()
        calendarB.time = Date(timeB)

        val calendarA = Calendar.getInstance()
        calendarA.time = Date(timeA)

        return (calendarB.get(Calendar.YEAR) === calendarA.get(Calendar.YEAR)
                && calendarB.get(Calendar.MONTH) === calendarA.get(Calendar.MONTH)
                && calendarB.get(Calendar.DATE) === calendarA.get(Calendar.DATE))
    }

    @JvmStatic
    fun skipToUsageStats(context: Context, requestCode: Int = -1) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            try {
                if (context is Activity && requestCode != -1) {
                    context.startActivityForResult(Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS), requestCode)
                } else {
                    context.startActivity(Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS))
                }
            } catch (e: ActivityNotFoundException) {
                Utils.toast(context, "当前设备不支持直接跳转查看应用使用情况页面，请手动进入并打开")
                e.printStackTrace()
            }
        } else {
            Utils.toast(context, "当前设备不支持查看应用使用情况")
        }
    }
}