package com.rc.core.util

import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Environment
import android.util.Log
import java.io.*
import java.text.SimpleDateFormat
import java.util.*

/**
 * info
 *
 * @author ReiChin_
 */
object CrashHandler : Thread.UncaughtExceptionHandler {

    private const val TAG = "LAB_Crash"

    private var mContext: Context? = null

    // 系统默认的 UncaughtException 处理类
    private var mDefaultHandler: Thread.UncaughtExceptionHandler? = null

    // 用来存储设备信息和异常信息
    private val infos = HashMap<String, String>()

    // 用于格式化日期,作为日志文件名的一部分
    private val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())

    /**
     * 初始化
     */
    fun initCrash(context: Context/*,  dstClass: Class<? out Activity>*/) {
        mContext = context
//        this.dstClass = dstClass;
        // 获取系统默认的 UncaughtException 处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler()
        // 设置该 CrashHandler 为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this)
    }

    override fun uncaughtException(thread: Thread, ex: Throwable) {
        if (!handleException(ex) && mDefaultHandler != null) {
            // 如果用户没有处理则让系统默认的异常处理器来处理
            mDefaultHandler!!.uncaughtException(thread, ex)
        } else {
            try {
                Thread.sleep(3000)
            } catch (e: InterruptedException) {
            }

            // 退出程序,注释下面的重启启动程序代码
//            SystemUtil.restartThroughIntentCompatMakeRestartActivityTask(
//                ContextUtil.getContext(),
//                dstClass
//            );
        }
    }

    /**
     * 自定义错误处理，收集错误信息，发送错误报告等操作均在此完成
     *
     * @param ex
     * @return true：如果处理了该异常信息；否则返回 false
     */
    private fun handleException(ex: Throwable?): Boolean {
        if (ex == null) return false
        Log.e(TAG, "", ex)

        // 收集设备参数信息
        mContext?.let {
            collectDeviceInfo(it)
        }

        // 读取Error信息
        readThrowableInfo(ex)?.let { error ->
            mContext?.let {
                // 保存日志文件
                saveCrashInfo2File(error, it.cacheDir)
            }
        }
        return true
    }

    /**
     * 收集设备参数信息
     * @param ctx
     */
    private fun collectDeviceInfo(ctx: Context) {
        try {
            ctx.packageManager.getPackageInfo(ctx.packageName, PackageManager.GET_ACTIVITIES)
                .let { packageInfo ->
                    infos["versionName"] = packageInfo.versionName ?: "null"
                    infos["versionCode"] = packageInfo.versionCode.toString()
                }
        } catch (e: PackageManager.NameNotFoundException) {
            Log.e(TAG, "an error occured when collect package info", e)
        }

        Build::class.java.declaredFields.forEach { field ->
            try {
                field.isAccessible = true
                infos[field.name] = field.get(null).toString()
                Log.d(TAG, field.name + " : " + field.get(null))
            } catch (e: Exception) {
                Log.e(TAG, "an error occured when collect crash info", e)
            }
        }
    }

    /**
     * 保存错误信息到文件中
     *
     * @param error
     * @return 返回文件名称, 便于将文件传送到服务器
     */
    private fun saveCrashInfo2File(error: String, cacheDir: File): String? {
        val time = formatter.format(Date())
        val sb = StringBuffer()
        sb.append(time + "\n")
        infos.entries.forEach { entry ->
            sb.append("${entry.key} = ${entry.value} \n")
        }

        var fos: FileOutputStream? = null
        try {
            sb.append(error)
            sb.append("\n\n\n")

            saveErrorLogToSp(sb.toString())

            val fileName = "error.log"
            if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
                val errorFile = File(cacheDir, fileName)
                if (!errorFile.exists()) {
                    errorFile.createNewFile()
                }
                fos = FileOutputStream(errorFile)
                fos.write(sb.toString().toByteArray())
                fos.close()
            }

            return fileName
        } catch (e: Exception) {
            Log.e(TAG, "an error occured while writing file...", e)
        } finally {
            try {
                fos?.let {
                    it.close()
                    fos = null
                }
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }

        return null
    }

    /**
     * 读取Throwable中的错误日志
     *
     * @param ex
     * @return
     */
    private fun readThrowableInfo(ex: Throwable): String? {
        var result: String? = null
        var writer: Writer? = null
        var printWriter: PrintWriter? = null
        try {
            writer = StringWriter()
            printWriter = PrintWriter(writer)
            ex.printStackTrace(printWriter)
            var cause = ex.cause
            while (cause != null) {
                cause.printStackTrace(printWriter)
                cause = cause.cause
            }
            result = writer.toString()
        } catch (e: Exception) {
            Log.e(TAG, "read throwable error....", e)
        } finally {
            printWriter?.let {
                it.close()
                printWriter = null
            }
            try {
                writer?.let {
                    it.close()
                    writer = null
                }
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
        return result
    }

    private fun saveErrorLogToSp(errorInfo: String) {
        mContext?.let { context ->
            val sp = context.getSharedPreferences("error-log", Context.MODE_PRIVATE)
            sp.edit().putString("error", errorInfo).apply()
        }
    }

}