package com.adealink.weparty.commonui.mediapicker.imageselect.viewmodel

import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import android.provider.MediaStore.MediaColumns
import android.text.TextUtils
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.adealink.frame.aab.util.getCompatString
import com.adealink.frame.coroutine.dispatcher.Dispatcher
import com.adealink.frame.log.Log
import com.adealink.frame.mvvm.viewmodel.BaseViewModel
import com.adealink.frame.util.AppUtil
import com.adealink.frame.util.FileProvider7
import com.adealink.weparty.R
import com.adealink.weparty.commonui.mediapicker.imageselect.constant.TAG_IMAGE_SELECT_LOAD
import com.adealink.weparty.commonui.mediapicker.imageselect.model.LoadMediaConfig
import com.adealink.weparty.commonui.mediapicker.imageselect.model.LocalMedia
import com.adealink.weparty.commonui.mediapicker.imageselect.model.LocalMediaFolder
import com.adealink.weparty.commonui.mediapicker.imageselect.model.MediaType
import com.adealink.weparty.commonui.mediapicker.imageselect.util.PictureMimeType
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.util.Collections
import java.util.Locale
import kotlin.math.min

class MediaSelectViewModel : BaseViewModel() {

    val foldersLD: LiveData<List<LocalMediaFolder>> = MutableLiveData()
    val selectFolderLD: LiveData<LocalMediaFolder> = MutableLiveData()

    private var defaultFolderName = getCompatString(R.string.common_all)
    private val isAndroidQ = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q

    fun loadMedia(loadMediaConfig: LoadMediaConfig) {
        viewModelScope.launch {
            val imageFolders: MutableList<LocalMediaFolder> = ArrayList()
            withContext(Dispatcher.WENEXT_THREAD_POOL) {
                val selectedUri2LocalMediaMap = HashMap<String, LocalMedia>()
                if (loadMediaConfig.selectedMediaList != null) {
                    loadMediaConfig.selectedMediaList.forEachIndexed { index, localMedia ->
                        localMedia.uri?.let {
                            selectedUri2LocalMediaMap[it] = localMedia
                        }
                    }
                }
                val cursor = AppUtil.appContext.contentResolver.query(
                    QUERY_URI,
                    PROJECTION,
                    getSelection(loadMediaConfig),
                    getSelectArgs(loadMediaConfig),
                    ORDER_BY_LAST_MODIFY
                )
                try {
                    if (cursor == null) {
                        Log.e(TAG_IMAGE_SELECT_LOAD, "loadImages, cursor is null")
                        return@withContext
                    }
                    val count = cursor.count
                    val allImageFolder = LocalMediaFolder()
                    val latelyImages: ArrayList<LocalMedia> = ArrayList()
                    if (count <= 0) {
                        Log.e(TAG_IMAGE_SELECT_LOAD, "loadImages, count is 0")
                        foldersLD.send(listOf())
                        return@withContext
                    }
                    cursor.moveToFirst()
                    do {
                        val id =
                            cursor.getLong(
                                cursor.getColumnIndexOrThrow(
                                    PROJECTION[0]
                                )
                            )

                        val path = cursor.getString(
                            cursor.getColumnIndexOrThrow(
                                PROJECTION[1]
                            )
                        )

                        val uriStr = if (isAndroidQ) {
                            getRealPathAndroidQ(id)
                        } else {
                            FileProvider7.getUriForFile(AppUtil.appContext, File(path))?.toString()
                        }

                        if (uriStr.isNullOrEmpty()) {
                            continue
                        }

                        val mimeType =
                            cursor.getString(
                                cursor.getColumnIndexOrThrow(
                                    PROJECTION[2]
                                )
                            )

                        val width =
                            cursor.getInt(
                                cursor.getColumnIndexOrThrow(
                                    PROJECTION[3]
                                )
                            )

                        val height =
                            cursor.getInt(
                                cursor.getColumnIndexOrThrow(
                                    PROJECTION[4]
                                )
                            )

                        val duration =
                            cursor.getLong(
                                cursor.getColumnIndexOrThrow(
                                    PROJECTION[5]
                                )
                            )

                        val size =
                            cursor.getLong(
                                cursor.getColumnIndexOrThrow(
                                    PROJECTION[6]
                                )
                            )

                        if (!loadMediaConfig.filterGift && PictureMimeType.isGif(mimeType)) {
                            if (size > loadMediaConfig.giftSizeLimit) {
                                continue
                            }
                        }

                        var folderName =
                            cursor.getString(
                                cursor.getColumnIndexOrThrow(
                                    PROJECTION[7]
                                )
                            )

                        if (folderName == null) {
                            folderName = defaultFolderName
                        }

                        if (PictureMimeType.eqVideo(mimeType)) {
                            if (duration == 0L) {
                                // 时长如果为0，就当做损坏的视频处理过滤掉
                                continue
                            }
                            if (size <= 0) {
                                // 视频大小为0过滤掉
                                continue
                            }
                        }

                        var image =
                            LocalMedia(
                                uriStr,
                                path,
                                duration,
                                ofAllType = loadMediaConfig.mediaType.type,
                                mimeType = mimeType,
                                width = width,
                                height = height,
                                size = size,
                            )
                        val selectedImage = selectedUri2LocalMediaMap[uriStr]
                        if (selectedImage != null) {
                            image = selectedImage
                        }

                        val folder =
                            getImageFolder(
                                path, uriStr, folderName, imageFolders
                            )
                        val images = folder.getImages()
                        images.add(image)
                        folder.imageNum += 1
                        latelyImages.add(image)
                        val imageNum = allImageFolder.imageNum
                        allImageFolder.imageNum = imageNum + 1
                    } while (cursor.moveToNext())

                    if (latelyImages.size > 0) {
                        sortFolder(imageFolders)
                        imageFolders.add(0, allImageFolder)
                        allImageFolder.firstImagePath = latelyImages[0].path
                        allImageFolder.firstImageUri = latelyImages[0].uri
                        allImageFolder.name = defaultFolderName
                        allImageFolder.ofAllType = loadMediaConfig.mediaType.type
                        allImageFolder.isCameraFolder = true
                        allImageFolder.setImages(latelyImages)
                    }
                } catch (e: Exception) {
                    Log.e(TAG_IMAGE_SELECT_LOAD, "loadMedia, exception: ${e.message}")
                } finally {
                    // Cursor需要关闭
                    cursor?.close()
                }
            }

            foldersLD.send(imageFolders)
            if (imageFolders.isNotEmpty()) {
                selectFolderLD.send(imageFolders[0])
            }
        }
    }

    /**
     * 文件夹数量进行排序
     *
     * @param imageFolders
     */
    private fun sortFolder(imageFolders: List<LocalMediaFolder>) {
        // 文件夹按图片数量排序
        Collections.sort(
            imageFolders,
            object : Comparator<LocalMediaFolder> {
                override fun compare(lhs: LocalMediaFolder, rhs: LocalMediaFolder): Int {
                    val lsize = lhs.imageNum
                    val rsize = rhs.imageNum
                    return if (lsize == rsize) 0 else (if (lsize < rsize) 1 else -1)
                }
            })
    }

    /**
     * 创建相应文件夹
     *
     * @param path
     * @param imageFolders
     * @param folderName
     * @return
     */
    private fun getImageFolder(
        path: String, uri: String, folderName: String, imageFolders: MutableList<LocalMediaFolder>
    ): LocalMediaFolder {
        for (folder in imageFolders) {
            // 同一个文件夹下，返回自己，否则创建新文件夹
            val name = folder.name
            if (TextUtils.isEmpty(name)) {
                continue
            }
            if (name == folderName) {
                return folder
            }
        }
        val newFolder = LocalMediaFolder()
        newFolder.name = folderName
        newFolder.firstImagePath = path
        newFolder.firstImageUri = uri
        imageFolders.add(newFolder)
        return newFolder
    }

    /**
     * 适配Android Q
     *
     * @param id
     * @return
     */
    private fun getRealPathAndroidQ(id: Long): String {
        return QUERY_URI.buildUpon().appendPath(id.toString()).build().toString()
    }

    fun selectFolder(folderInfo: LocalMediaFolder) {
        selectFolderLD.send(folderInfo)
    }

    private fun getSelection(loadMediaConfig: LoadMediaConfig): String {
        when (loadMediaConfig.mediaType) {
            MediaType.ALL -> {
                // 获取全部，不包括音频
                return getSelectionArgsForAllMediaCondition(
                    getDurationCondition(0, 0), loadMediaConfig.filterGift
                )
            }
            MediaType.IMAGE -> {
                return if (loadMediaConfig.filterGift) SELECTION_NOT_GIF else SELECTION
            }
            MediaType.VIDEO -> {
                return selectionArgsForSingleMediaCondition
            }
        }
    }

    private fun getSelectArgs(loadMediaConfig: LoadMediaConfig): Array<String> {
        when(loadMediaConfig.mediaType) {
            MediaType.ALL -> {
                // 获取全部，不包括音频
                return SELECTION_ALL_ARGS
            }
            MediaType.IMAGE -> {
                return getSelectionArgsForSingleMediaType(
                    MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE
                )
            }
            MediaType.VIDEO -> {
                return getSelectionArgsForSingleMediaType(
                    MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO
                )
            }
        }
    }

    /**
     * 获取视频(最长或最小时间)
     *
     * @param exMaxLimit
     * @param exMinLimit
     * @return
     */
    private fun getDurationCondition(exMaxLimit: Long, exMinLimit: Long): String {
        var maxS = Long.MAX_VALUE
        if (exMaxLimit != 0L) {
            maxS = min(maxS.toDouble(), exMaxLimit.toDouble()).toLong()
        }
        return String.format(
            Locale.ROOT,
            "%d <%s "
                    + MediaColumns.DURATION
                    + " and "
                    + MediaColumns.DURATION
                    + " <= %d",
            exMinLimit,
            if (exMinLimit == 0L) "" else "=",
            maxS
        )
    }

    /**
     * 是否是视频
     *
     * @param mimeType
     * @return
     */
    fun eqVideo(mimeType: String?): Boolean {
        return mimeType != null && mimeType.startsWith(MIME_TYPE_PREFIX_VIDEO)
    }

    companion object {
        private val QUERY_URI: Uri = MediaStore.Files.getContentUri("external")
        private const val ORDER_BY_ID = MediaStore.Files.FileColumns._ID + " DESC"
        private const val ORDER_BY_LAST_MODIFY = MediaStore.Files.FileColumns.DATE_MODIFIED + " DESC"
        private const val NOT_GIF = "!='image/gif'"
        const val MIME_TYPE_PREFIX_VIDEO: String = "video"

        /** 媒体文件数据库字段  */
        private val PROJECTION: Array<String> = arrayOf(
            MediaStore.Files.FileColumns._ID,
            MediaColumns.DATA,
            MediaColumns.MIME_TYPE,
            MediaColumns.WIDTH,
            MediaColumns.HEIGHT,
            MediaColumns.DURATION,
            MediaColumns.SIZE,
            MediaColumns.BUCKET_DISPLAY_NAME,
            MediaColumns.DATE_MODIFIED
        )

        private const val SELECTION = (MediaStore.Files.FileColumns.MEDIA_TYPE
                + "=?"
                + " AND "
                + MediaColumns.SIZE
                + ">0")

        private const val SELECTION_NOT_GIF = (MediaStore.Files.FileColumns.MEDIA_TYPE
                + "=?"
                + " AND "
                + MediaColumns.SIZE
                + ">0"
                + " AND "
                + MediaColumns.MIME_TYPE
                + NOT_GIF)

        private val selectionArgsForSingleMediaCondition: String
            get() = (MediaStore.Files.FileColumns.MEDIA_TYPE
                    + "=?"
                    + " AND "
                    + MediaColumns.SIZE
                    + ">0")

        /**
         * 全部模式下条件
         *
         * @param timeCondition 时间条件
         * @param filterGift 是否过滤gif
         * @return
         */
        private fun getSelectionArgsForAllMediaCondition(
            timeCondition: String, filterGift: Boolean
        ): String {
            val condition =
                ("("
                        + MediaStore.Files.FileColumns.MEDIA_TYPE
                        + "=?"
                        + (if (filterGift) " AND " + MediaColumns.MIME_TYPE + NOT_GIF else "")
                        + " OR "
                        + (MediaStore.Files.FileColumns.MEDIA_TYPE + "=? AND " + timeCondition)
                        + ")"
                        + " AND "
                        + MediaColumns.SIZE
                        + ">0")
            return condition
        }

        /** 图片和视频  */
        private val SELECTION_ALL_ARGS = arrayOf(
            MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE.toString(),
            MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString(),
        )

        private fun getSelectionArgsForSingleMediaType(mediaType: Int): Array<String> {
            return arrayOf(mediaType.toString())
        }
    }
}