package com.ux.video.file.filerecovery.utils


import android.graphics.BitmapFactory
import android.util.Log
import com.ux.video.file.filerecovery.photo.ResultPhotos
import com.ux.video.file.filerecovery.photo.ResultPhotosFiles
import com.ux.video.file.filerecovery.result.ScanningActivity
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_audio
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_audio
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_documents
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_photo
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_video
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_documents
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_photo
import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_video
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File

object ScanManager {
    val IMAGE_FILE = listOf("jpg", "jpeg", "png", "gif")
    val AUDIO_FILE = listOf("mp3", "wav", "flac", "aac", "ogg", "m4a", "amr")
    val SHORT_AUDIO_FILE = listOf("mp3", "wav", "ogg")
    val VIDEO_FILE = listOf("webm", "mkv", "mp4")
    val DOCUMENT_FILE = listOf(
        "php",
        "txt",
        "zip",
        "stoutner",
        "pdf",
        "doc",
        "docx",
        "ppt",
        "pptx",
        "xls",
        "xlsx",
        "apk",
        "xapk"
    )

    /**
     * 扫描所有文件
     */
    fun scanAllDocuments(
        root: File, maxDepth: Int = 5,
        maxFiles: Int = 5000,
        type: Int
    ): Flow<ScanState> = flow {

        val result = mutableMapOf<String, MutableList<File>>()
        var fileCount = 0
        suspend fun scanDocuments(dir: File, depth: Int) {
            if (!dir.exists() || !dir.isDirectory) return
            if (depth > maxDepth || fileCount >= maxFiles) return
            dir.listFiles()?.forEach { file ->
                if (file.isDirectory) {
                    scanDocuments(file, depth + 1)
                } else {
                    var fileCheckBoolean: Boolean = false
                    when (type) {
                        VALUE_SCAN_TYPE_photo -> {
                            fileCheckBoolean = isFormatFile(file, IMAGE_FILE) && isValidImage(file)
                        }

                        VALUE_SCAN_TYPE_video -> {
                            fileCheckBoolean = isFormatFile(file, VIDEO_FILE)
                        }

                        VALUE_SCAN_TYPE_audio -> {
                            fileCheckBoolean = isFormatFile(file, AUDIO_FILE)
                        }

                        VALUE_SCAN_TYPE_documents -> {
                            fileCheckBoolean = file.isFile && isFormatFile(file, DOCUMENT_FILE)
                        }
                    }
                    if (fileCheckBoolean) {
                        val dirName = file.parentFile?.name ?: "Unknown"

                        val list = result.getOrPut(dirName) { mutableListOf() }
                        list.add(file)
                        fileCount++

                        emit(ScanState.Progress(fileCount, file.absolutePath))
                    }
                }
            }
        }

        scanDocuments(root, depth = 0)
        val map = result.map { (dir, files) ->
            val resultPhotosFilesList = files.map { file ->
                ResultPhotosFiles(
                    name = file.name,
                    path = file.absolutePath,
                    size = file.length(),
                    lastModified = file.lastModified(),
                    resolution = getImageSize(file).run {
                        "$first*$second"
                    }
                )
            }
            ResultPhotos(dir, ArrayList(resultPhotosFilesList))
        }
        emit(ScanState.Complete(ArrayList(map)))
    }
   private fun getImageSize(file: File): Pair<Int, Int> {
        val options = BitmapFactory.Options()
        options.inJustDecodeBounds = true
        BitmapFactory.decodeFile(file.absolutePath, options)
        val width = options.outWidth
        val height = options.outHeight
        return Pair(width, height)
    }

    /**
     * 递归扫描隐藏目录下的有效图片(删除的图片)
     * @param maxDepth  // 最大递归深度
     * @param maxFiles   // 最大收集的文件数
     */
    fun scanHiddenPhotoAsync(
        root: File, maxDepth: Int = 5,
        maxFiles: Int = 5000, type: Int
    ): Flow<ScanState> = flow {

        val result = mutableMapOf<String, MutableList<File>>()
        var fileCount = 0
        suspend fun scanDir(dir: File, depth: Int, insideHidden: Boolean = false) {
            if (!dir.exists() || !dir.isDirectory) return
            if (depth > maxDepth || fileCount >= maxFiles) return
            showLog("HiddenScan", "${dir.name}  111111")
            dir.listFiles()?.forEach { file ->
                if (file.isDirectory) {
                    val isHidden = file.name.startsWith(".")
                    scanDir(file, depth + 1, insideHidden = insideHidden || isHidden)
                } else {
                    if (insideHidden) {
                        var fileCheckBoolean: Boolean = false
                        when (type) {
                            VALUE_SCAN_TYPE_deleted_photo -> {
                                fileCheckBoolean =
                                    isFormatFile(file, IMAGE_FILE) && isValidImage(file)
                            }

                            VALUE_SCAN_TYPE_deleted_video -> {
                                fileCheckBoolean = isFormatFile(file, VIDEO_FILE)
                            }

                            VALUE_SCAN_TYPE_deleted_audio -> {
                                fileCheckBoolean = isFormatFile(file, AUDIO_FILE)
                            }

                            VALUE_SCAN_TYPE_deleted_documents -> {
                                fileCheckBoolean = file.isFile && isFormatFile(file, DOCUMENT_FILE)
                            }
                        }
                        if (fileCheckBoolean) {
                            val dirName = file.parentFile?.name ?: "Unknown"
                            ScanManager.showLog("HiddenScan", "${dirName}  22222")
                            val list = result.getOrPut(dirName) { mutableListOf() }
                            list.add(file)
                            fileCount++
                            emit(ScanState.Progress(fileCount, file.absolutePath))
                        }
                    }
                }
            }
        }
        scanDir(root, depth = 0)

        ScanManager.showLog("HiddenScan", "    3333")
        val map = result.map { (dir, files) ->
            val resultPhotosFilesList = files.map { file ->
                ResultPhotosFiles(
                    name = file.name,
                    path = file.absolutePath,
                    size = file.length(),
                    lastModified = file.lastModified(),
                    resolution = getImageSize(file).run {
                        "$first*$second"
                    }
                )
            }
            ResultPhotos(dir, ArrayList(resultPhotosFilesList))
        }
        emit(ScanState.Complete(ArrayList(map)))
    }


    private fun isFormatFile(file: File, types: List<String>): Boolean {
        val ext = file.extension.lowercase()
        return types.contains(ext)
    }

    private fun isNumericDir(name: String): Boolean {
        return name.matches(Regex("^\\d+$"))
    }

    fun showLog(tag: String, msg: String) {
        Log.d(tag, msg)
    }

    fun isValidImage(file: File): Boolean {
        if (!file.exists() || file.length() <= 0) return false
        return try {
            BitmapFactory.decodeFile(file.absolutePath)?.let {
                it.width > 0 && it.height > 0
            } == true
        } catch (e: Exception) {
            false
        }
    }


    /**
     * 做批量恢复
     * @param folder "AllRecovery/Photo"
     */
    fun CoroutineScope.copySelectedFilesAsync(
        selectedSet: Set<String>,
        rootDir: File = Common.rootDir,
        folder: String,
        onProgress: (currentCounts: Int, fileName: String, success: Boolean) -> Unit,
        onComplete: (currentCounts: Int) -> Unit
    ) {
        launch(Dispatchers.IO) {
            val targetDir = File(rootDir, folder)
            if (!targetDir.exists()) targetDir.mkdirs()
            selectedSet.forEachIndexed { index, path ->
                val srcFile = File(path)
                if (srcFile.exists() && srcFile.isFile) {
                    val destFile = File(targetDir, srcFile.name)
                    var success = false
                    try {
                        srcFile.inputStream().use { input ->
                            destFile.outputStream().use { output ->
                                input.copyTo(output)
                            }
                        }
                        success = true
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }
                    withContext(Dispatchers.Main) {
                        onProgress(index + 1, srcFile.name, success)
                    }
                }

            }
            withContext(Dispatchers.Main) {
                onComplete(selectedSet.size)
            }
        }
    }


    /**
     * 做批量删除文件
     *
     */
    fun CoroutineScope.deleteFilesAsync(
        selectedSet: Set<String>,
        onProgress: (currentCounts: Int, path: String, success: Boolean) -> Unit,
        onComplete: (currentCounts: Int) -> Unit
    ) {
        launch(Dispatchers.IO) {
            var deletedCount = 0
            selectedSet.forEachIndexed { index, path ->
                try {
                    val file = File(path)
                    if (file.exists() && file.delete()) {
                        deletedCount++
                    }
                    withContext(Dispatchers.Main) {
                        onProgress(index + 1, path, true)
                    }
                } catch (e: Exception) {
                    onProgress(index + 1, path, false)
                }
            }
            withContext(Dispatchers.Main) {
                onComplete(selectedSet.size)
            }
        }

    }
}
