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


import android.content.ContentUris
import android.content.Context
import android.graphics.BitmapFactory
import android.media.MediaMetadataRetriever
import android.net.Uri
import android.provider.MediaStore
import android.provider.OpenableColumns
import android.text.format.Formatter
import android.util.Log
import com.ux.video.file.filerecovery.db.ResultData
import com.ux.video.file.filerecovery.db.ResultDataFiles
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
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(
        context: Context,
        root: File, maxDepth: Int = 5,
        maxFiles: Int = 5000,
        fileType: FileType
    ): Flow<ScanState> = flow {

        val result = mutableMapOf<String, MutableList<File>>()
        var fileCount = 0
        suspend fun scanDocuments(dir: File, depth: Int) {
            val context = currentCoroutineContext()

            if (!dir.exists() || !dir.isDirectory) return
            if (depth > maxDepth || fileCount >= maxFiles) return
            dir.listFiles()?.forEach { file ->
                context.ensureActive()
                if (file.isDirectory) {
                    scanDocuments(file, depth + 1)
                } else {
                    val fileCheckBoolean: Boolean = checkFileFormat(file,fileType)
                    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 resultDataFilesList = files.map { file ->
                ResultDataFiles(
                    name = file.name,
                    path = file.absolutePath,
                    size = file.length(),
                    sizeString = Formatter.formatFileSize(
                        context,
                        file.length()
                    ),
                    lastModified = file.lastModified(),
                    fileType = fileType
                )
            }
            ResultData(dir, ArrayList(resultDataFilesList))
        }
        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)
    }


    fun getVideoResolution(filePath: String): Pair<Int, Int> {
        val retriever = MediaMetadataRetriever()
        return try {
            retriever.setDataSource(filePath)
            val width = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)
                ?.toIntOrNull() ?: 0
            val height = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)
                ?.toIntOrNull() ?: 0
            width to height
        } catch (e: Exception) {
            e.printStackTrace()
            0 to 0
        } finally {
            retriever.release()
        }
    }


    /**
     * 递归扫描隐藏目录下的有效图片(删除的图片)
     * @param maxDepth  // 最大递归深度
     * @param maxFiles   // 最大收集的文件数
     */
    fun scanHiddenPhotoAsync(
        context: Context,
        root: File, maxDepth: Int = 5,
        maxFiles: Int = 5000,     fileType: FileType
    ): Flow<ScanState> = flow {
//        scanRecycler(context)
        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
            val skipDirs = setOf(
                "Android",
                "obb",
                "data",
                ".thumbnails",
                ".cache"
            )

            if (skipDirs.contains(dir.name)) 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 {
                    val fileCheckBoolean: Boolean = checkFileFormat(file,fileType)
                    if (insideHidden) {
                        if (fileCheckBoolean) {
                            val dirName = file.parentFile?.name ?: "Unknown"
                            ScanManager.showLog("HiddenScan", "${dirName}  22222  ${file.name}")
                            val list = result.getOrPut(dirName) { mutableListOf() }
                            list.add(file)
                            fileCount++
                            emit(ScanState.Progress(fileCount, file.absolutePath))
                        }
                    } else {
                        if (file.name.startsWith(".") && fileCheckBoolean) {
                            val dirName = file.parentFile?.name ?: "Unknown"
                            ScanManager.showLog("HiddenScan", "${dirName}  33333  ${file.name}")
                            val list = result.getOrPut(dirName) { mutableListOf() }
                            list.add(file)
                            fileCount++
                            emit(ScanState.Progress(fileCount, file.absolutePath))
                        }

                    }
                }
            }
        }




        scanDir(root, depth = 0)
        ScanManager.showLog("HiddenScan", "    44444444")
        val map = result.map { (dir, files) ->
            val resultDataFilesList = files.map { file ->
                ResultDataFiles(
                    name = file.name,
                    path = file.absolutePath,
                    size = file.length(),
                    sizeString = Formatter.formatFileSize(
                        context,
                        file.length()
                    ),
                    lastModified = file.lastModified(),
                    fileType = fileType
                )

            }
            ResultData(dir, ArrayList(resultDataFilesList))
        }
        emit(ScanState.Complete(ArrayList(map)))
    }


    private fun checkFileFormat(file: File, fileType: FileType): Boolean {
        return when (fileType) {
            FileType.PHOTO -> {
                isFormatFile(file, IMAGE_FILE)
            }

            FileType.VIDEO -> {
                isFormatFile(file, VIDEO_FILE)
            }

            FileType.AUDIO -> {
                isFormatFile(file, AUDIO_FILE)
            }

            FileType.DOCUMENT -> {
                file.isFile && isFormatFile(file, DOCUMENT_FILE)
            }
        }
    }

    private fun scanRecycler(context: Context) {
        val projection = arrayOf(
            MediaStore.Images.Media._ID,
            MediaStore.Images.Media.DISPLAY_NAME
        )

        val selection = "${MediaStore.Images.Media.IS_TRASHED} = ?"
        val selectionArgs = arrayOf("1")

        val cursor = context.contentResolver.query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            projection,
            selection,
            selectionArgs,
            null
        )
        Log.d("Trash", "cursor=$cursor ")
        cursor?.use {
            Log.d("Trash", "11111111")
            while (it.moveToNext()) {
                Log.d("Trash", "2222222222")
                val id = it.getLong(0)
                val name = it.getString(1)

                val uri = ContentUris.withAppendedId(
                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    id
                )

                // 这是“可恢复”的文件
                Log.d("Trash", "name=$name uri=$uri")
            }
        }

    }



    private fun getFileSizeByMediaStore(context: Context, file: File): Long {
        val uri = Uri.fromFile(file)
        context.contentResolver.query(uri, arrayOf(OpenableColumns.SIZE), null, null, null)
            ?.use { cursor ->
                val index = cursor.getColumnIndex(OpenableColumns.SIZE)
                if (cursor.moveToFirst()) {
                    return cursor.getLong(index)
                }
            }
        return file.length() // fallback
    }

//    private fun getResolution(type: Int, file: File): String {
//        return when (type) {
//            VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo -> {
//                getImageSize(file).run {
//                    "$first*$second"
//                }
//            }
//
//            VALUE_SCAN_TYPE_video, VALUE_SCAN_TYPE_deleted_video -> getVideoResolution(file.path).run {
//                "$first*$second"
//            }
//
//            else -> ""
//        }
//    }

    private fun isFormatFile(file: File, types: List<String>): Boolean {
        if (!file.exists() || file.length() <= 0) return false
        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<ResultDataFiles>,
        rootDir: File = Common.rootDir,
        folder: String,
        onProgress: (currentCounts: Int, data: ResultDataFiles, fileName: String, success: Boolean) -> Unit,
        onComplete: (currentCounts: Int) -> Unit
    ) {
        launch(Dispatchers.IO) {
            var recoveryCount = 0
            val targetDir = File(rootDir, folder)
            if (!targetDir.exists()) targetDir.mkdirs()
            selectedSet.forEachIndexed { index, resultPhotosFiles ->
                val srcFile = File(resultPhotosFiles.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
                        recoveryCount++
                        withContext(Dispatchers.Main) {
                            onProgress(index + 1, resultPhotosFiles, srcFile.name, success)
                        }
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }

                }

            }
            withContext(Dispatchers.Main) {
                onComplete(recoveryCount)
            }
        }
    }


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

    }


}
