package com.qimai.log

import com.blankj.utilcode.util.*
import com.dianping.logan.Logan
import com.dianping.logan.LoganConfig
import com.qimai.log.bean.LogUpBean
import com.qimai.log.ui.LogUpFloatView
import kotlinx.coroutines.*
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileInputStream

/**
 * 上传日志相关操作
 */
object QLog {

    //region ------ 日志类型 ------
    //调试信息
    const val D: Int = 2

    //提示性的消息
    const val I: Int = 3

    //错误信息
    const val E: Int = 4

    //警告信息
    const val W: Int = 5
    //endregion

    //ase加密Key
    private const val encryptKey16 = "0123456789012345"

    //aes加密IV
    private const val encryptIv16 = "0123456789012345"

    // 到天的日期格式
    private const val formatDay = "yyyy-MM-dd"

    //每天的毫秒数
    private const val millisDay = 86_400_000

    //零点字符串
    private const val zero = "00:00:00"

    //log 文件路径
    private var filePath: String? = null

    /**
     * 初始化
     */
    fun init(debug: Boolean = false) {
        filePath =
            "${Utils.getApp().getExternalFilesDir(null)?.absolutePath}${File.separator}Logan_v1"
        val config: LoganConfig = LoganConfig.Builder()
            .setCachePath(Utils.getApp().applicationContext.filesDir.absolutePath)
            .setEncryptKey16(encryptKey16.toByteArray())
            .setEncryptIV16(encryptIv16.toByteArray())
            .setPath(filePath)
            .build()
        Logan.init(config)
        Logan.setDebug(debug)
        Logan.setOnLoganProtocolStatus { cmd, code ->
            LogUtils.d("clogan > cmd : $cmd | code : $code")
        }

        GlobalScope.launch(Dispatchers.IO) {
            delay(1_000)
            withContext(Dispatchers.Main) {
                LogUpFloatView.show()
            }
        }
    }

    /**
     * 写入日志
     * @param log String - 日志内容
     * @param tag Int - 日志类型
     * @param now Boolean - 是否立即写入
     */
    fun write(log: String, tag: Int = D, now: Boolean = false) {
        Logan.w(log, tag)
        if (now) Logan.f()
    }

    /**
     * 立即写入
     */
    fun f() = Logan.f()

    /**
     * 获取日志文件保存路径
     */
    fun getLogFilePath(): String? = filePath

    /**
     * 按日期获取日志文件,日期格式 yyyy-MM-dd
     */
    fun getLogFile(dateStr: String): File? {
        val timeStamp = TimeUtils.string2Millis("$dateStr $zero")
        return FileUtils.listFilesInDir(filePath).find { it.name == "$timeStamp" }
    }

    /**
     * 获取一段时间内所有日志文件
     *
     * @param startDate String 开始时间，格式为：yyyy-MM-dd
     * @param endDate String 结束时间，格式为：yyyy-MM-dd
     * @return MutableList<File> 文件列表
     */
    fun getLogFileByPeriod(startDate: String, endDate: String): MutableList<File> {
        val dateList = getPeriodDateList(startDate, endDate)
        return FileUtils.listFilesInDirWithFilter(filePath) {
            dateList.any { a -> "${TimeUtils.string2Millis("$a $zero")}" == it.name }
        }
    }

    /**
     *获取所有日志文件
     */
    fun getAllLogFile(): MutableList<File> = FileUtils.listFilesInDir(filePath)

    /**
     * 按日期获取日志文件内容,日期格式 yyyy-MM-dd
     */
    fun getLogFileContent(dateStr: String): String {
        val file = getLogFile(dateStr)
        return if (file != null) getLogFileContent(file) else ""
    }

    /**
     * 获取一天内一段时间内的日志内容
     *
     * @param startDate String 开始时间，格式为 yyyy-MM-dd HH:mm:ss
     * @param endDate String 结束时间，格式为 yyyy-MM-dd HH:mm:ss
     * @return List<LogUpBean> 日志内容
     */
    fun getLogListByPeriod(startDate: String, endDate: String): List<LogUpBean?> {
        //开始时间戳
        val startMillis: Long = TimeUtils.string2Millis(startDate)
        //结束时间戳
        val endMillis: Long = TimeUtils.string2Millis(endDate)
        //当天时间字符串，用于查找文件，格式：yyyy-MM-dd
        val dateStr = TimeUtils.millis2String(startMillis, formatDay)
        return getLogFileContent(dateStr)
            .trim()
            .split("\n")
            .map { GsonUtils.fromJson(it, LogUpBean::class.java) ?: null }
            .filter { it?.l in startMillis..endMillis }
            .sortedByDescending { it?.l }
    }

    /**
     * 获取一天内一段时间内的日志内容
     *
     * @param startDate String 开始时间，格式为 yyyy-MM-dd HH:mm:ss
     * @param endDate String 结束时间，格式为 yyyy-MM-dd HH:mm:ss
     * @return String 日志内容
     */
    fun getLogContentByPeriod(startDate: String, endDate: String): String {
        val list = getLogListByPeriod(startDate, endDate)
        return GsonUtils.toJson(list)
    }

    /**
     * 获取所有文件内容
     */
    fun getAllLogFileContent(): String {
        return getAllLogFile()
            .fold("") { total, next ->
                val nextContent = getLogFileContent(next)
                return if (total.isEmpty()) nextContent else "${total}\n${nextContent}"
            }
    }

    /**
     * 解析日志文件内容
     */
    fun getLogFileContent(file: File): String {
        val uncompressBytesArray = ByteArrayOutputStream()
        val loganParser = LoganParser(encryptKey16.toByteArray(), encryptIv16.toByteArray())
        loganParser.parse(FileInputStream(file), uncompressBytesArray)
        return String(uncompressBytesArray.toByteArray())
    }

    /**
     * 获取一段时间内时间列表
     *
     * @param startDate String 开始时间，格式为 yyyy-MM-dd
     * @param endDate String 结束时间，格式为 yyyy-MM-dd
     * @return MutableList<String> 时间列表 [yyyy-MM-dd,yyyy-MM-dd]
     */
    private fun getPeriodDateList(startDate: String, endDate: String): MutableList<String> {
        val start: Long = TimeUtils.string2Millis("$startDate $zero")
        var end: Long = TimeUtils.string2Millis("$endDate $zero")
        val dateList = mutableListOf<String>()
        while (end >= start) {
            dateList.add(TimeUtils.millis2String(end, formatDay))
            end -= millisDay
        }
        return dateList
    }

    /**
     * 按名称删除文件
     */
    fun deleteLogByName(name: String) {
        FileUtils.delete("$filePath/$name")
    }

    /**
     * 按日期删除日志
     * @param dateStr String
     */
    fun deleteLogByDate(dateStr: String) {
        getLogFile(dateStr)?.run { FileUtils.delete(this) }
    }

    /**
     * 获取所有日志文件信息
     */
    fun getAllFileInfo(): Map<String, Long> {
        return Logan.getAllFilesInfo()
    }

    /**
     * 清空所有日志文件
     */
    fun clear() {
        FileUtils.deleteAllInDir(filePath)
    }
}