package com.convenient.android.common.media.scan

import android.content.ContentResolver
import android.content.ContentUris
import android.database.Cursor
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import androidx.annotation.RequiresApi
import com.convenient.android.common.config.MyPdfBaseModule
import com.convenient.android.common.extension.toFile
import com.convenient.android.common.media.MediaBean
import com.convenient.android.common.media.basic.Query
import com.convenient.android.common.media.config.MediaQueryConfig
import com.convenient.android.common.media.config.MediaSortOrder
import com.convenient.android.common.media.config.MediaSortType
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

/**
 * @classname:
 * @author: LiuXiaoLong
 * @date: 2022/8/3
 * description: 通过系统MediaStore 扫描存储中的文件内容
 * MediaStore.query(dir : String, config : MediaQueryConfig) : List<MediaBean>
 */
object MediaStore : Query {

    /**
     * 要查询的数据
     */
    private val projection = arrayOf(
        MediaStore.Files.FileColumns.DATA,
        MediaStore.Files.FileColumns.MIME_TYPE,
        MediaStore.Files.FileColumns.SIZE,
        MediaStore.Files.FileColumns.DATE_MODIFIED,
        MediaStore.Files.FileColumns._ID,
        MediaStore.Files.FileColumns.DISPLAY_NAME
    )

    override suspend fun query(dir: String, config: MediaQueryConfig.() -> Unit): List<MediaBean> {
        return query(dir, MediaQueryConfig().also(config))
    }


    /**
     * 查询媒体文件
     */
    override suspend fun query(dir: String, config: MediaQueryConfig): List<MediaBean> = withContext(Dispatchers.IO) {
        try {
            val cursor: Cursor? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                getQueryCursorWithMoreThanTheApi26(dir, config)
            } else {
                getQueryCursorWithLessThanApi26(dir, config)
            }
            val list = mutableListOf<MediaBean>()
            cursor?.apply {
                while (moveToNext()) {
                    val path = getString(getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA))
                    val mediaFile = path.toFile()
                    if (mediaFile?.isFile?.not() == true) {
                        continue
                    }

                    val id = getLong(getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID))
                    val name = getString(getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME))
                    val contentUri = ContentUris.withAppendedId(MediaStore.Files.getContentUri("external"), id)

                    list.add(
                        MediaBean(
                            mediaPath = path,
                            name = if (name.isNullOrEmpty()) mediaFile?.name ?: "" else name,
                            extension = mediaFile?.extension ?: "",
                            isFile = true,
                            lastModified = mediaFile?.lastModified() ?: 0,
                            parentPath = mediaFile?.parent ?: "",
                            length = mediaFile?.length(),
                            uri = contentUri
                        )
                    )
                }
            }

            cursor?.close()
            list
        } catch (e: Exception) {
            e.printStackTrace()
            emptyList<MediaBean>()
        }
    }

    private fun getQueryWhere(dir: String, config: MediaQueryConfig): String {
        val selectBuilder = StringBuilder()
        val selectionList = mutableListOf<String>()
        //文件夹查询限制
        val dirSelection = getQueryDirSubStatement(dir)
        if (dirSelection.isNotEmpty()) {
            selectionList.add(dirSelection)
        }

        if (config.ignoreChildFiles.isNotEmpty()) {
            //要忽略的文件
            val files = config.ignoreChildFiles
            val ignoreSelect = StringBuilder()
            files.forEachIndexed { index, file ->
                when {
                    index == 0 && files.size != 1 -> ignoreSelect.append("(${MediaStore.Files.FileColumns.DATA} NOT LIKE '%${file.absolutePath}%'")
                    index == files.lastIndex && files.size != 1 -> ignoreSelect.append(" and ${MediaStore.Files.FileColumns.DATA} not LIKE '%${file.absolutePath}%' )")
                    files.size == 1 -> ignoreSelect.append("(${MediaStore.Files.FileColumns.DATA} not LIKE '%${file.absolutePath}%')")
                    else -> ignoreSelect.append(" and ${MediaStore.Files.FileColumns.DATA} not LIKE '%${file.absolutePath}%'")
                }
            }
            if (ignoreSelect.isNotEmpty()) {
                selectionList.add(ignoreSelect.toString())
            }
        }
        selectionList.add(" (${MediaStore.Files.FileColumns.MIME_TYPE} not like 'null' ) ")
        //添加查询的文件格式限制
        val mimeTypesSelection = getMimeTypesSelection(config.supportMimeTypes)
        if (mimeTypesSelection.isNotEmpty()) {
            selectionList.add(mimeTypesSelection)
        }

        selectionList.forEachIndexed { index, s ->
            if (index != selectionList.lastIndex) {
                selectBuilder.append(s).append(" and ")
            } else {
                selectBuilder.append(s)
            }
        }
        return selectBuilder.toString()
    }

    /**
     * 获取小于API26的查询游标
     */
    private fun getQueryCursorWithLessThanApi26(dir: String, config: MediaQueryConfig): Cursor? {

        //排序子语句
        var sort = getSortSubStatement(config)
        if (config.page.pageSize != Int.MAX_VALUE) {
            //如果设置的不是最大数量，则需要分页查询
            sort += " LIMIT ${config.page.pageSize} OFFSET ${(config.page.pageNum - 1) * config.page.pageSize}"
        }

        val selectBuilder = getQueryWhere(dir, config)
        return MyPdfBaseModule.getAppContext()?.contentResolver?.query(
            MediaStore.Files.getContentUri("external"),
            projection,
            selectBuilder,
            null,
            sort
        )
    }

    /**
     * 获取高于(包含)Api26的查询游标
     */
    @RequiresApi(Build.VERSION_CODES.O)
    private fun getQueryCursorWithMoreThanTheApi26(dir: String, config: MediaQueryConfig): Cursor? {
        val queryArgs = Bundle().apply {
            if (config.page.pageSize != Int.MAX_VALUE) {
                putInt(ContentResolver.QUERY_ARG_LIMIT, config.page.pageSize)
                putInt(ContentResolver.QUERY_ARG_OFFSET, (config.page.pageNum - 1) * config.page.pageSize)
            }
            putInt(
                ContentResolver.QUERY_ARG_SORT_DIRECTION,
                if (config.order == MediaSortOrder.DESC) ContentResolver.QUERY_SORT_DIRECTION_DESCENDING else ContentResolver.QUERY_SORT_DIRECTION_ASCENDING
            )
            putStringArray(
                ContentResolver.QUERY_ARG_SORT_COLUMNS, arrayOf(
                    when (config.sortType) {
                        MediaSortType.NAME -> MediaStore.Files.FileColumns.DISPLAY_NAME
                        MediaSortType.DATE -> MediaStore.Files.FileColumns.DATE_MODIFIED
                        MediaSortType.SIZE -> MediaStore.Files.FileColumns.SIZE
                    }
                )
            )
            val where = getQueryWhere(dir, config)
            if (where.isNotEmpty()) {
                Log.e("MediaStore", "查询语句:$where")
                putString(ContentResolver.QUERY_ARG_SQL_SELECTION, where)
            }
        }
        return MyPdfBaseModule.getAppContext()?.contentResolver?.query(
            MediaStore.Files.getContentUri("external"),
            projection, queryArgs, null
        )
    }


    /**
     * 查询指定目录的子语句
     * ( _data LIKE '/storage/emulated/0/DCIM/%' )
     */
    private fun getQueryDirSubStatement(dir: String): String {
        if (dir.isEmpty()) return ""
        val tempDir = if (dir.endsWith("/")) dir else "${dir}/"
        return "( ${MediaStore.Files.FileColumns.DATA} LIKE '${tempDir}%' )"
    }

    /**
     * 查询文件格式限制的子语句
     * (_data LIKE '%pdf')
     */
    private fun getMimeTypesSelection(mimeTypes: List<String>): String {
        if (mimeTypes.isEmpty()) return ""
        val select = StringBuilder()
        mimeTypes.forEachIndexed { index, s ->
            when {
                index == 0 && mimeTypes.size != 1 -> select.append("(${MediaStore.Files.FileColumns.DATA} LIKE '%${s}'")
                index == mimeTypes.lastIndex && mimeTypes.size != 1 -> select.append(" or ${MediaStore.Files.FileColumns.DATA} LIKE '%${s}' )")
                mimeTypes.size == 1 -> select.append("(${MediaStore.Files.FileColumns.DATA} LIKE '%${s}')")
                else -> select.append(" or ${MediaStore.Files.FileColumns.DATA} LIKE '%${s}'")
            }
        }
        return select.toString()
    }

    /**
     * 排序的子语句， 根据名称、时间、大小排序
     * _display_name desc or asc
     * date_modified desc or asc
     * _size desc or asc
     */
    private fun getSortSubStatement(config: MediaQueryConfig): String {
        return when (config.sortType) {
            MediaSortType.NAME -> "${MediaStore.Files.FileColumns.DISPLAY_NAME} ${config.order.name}"
            MediaSortType.DATE -> "${MediaStore.Files.FileColumns.DATE_MODIFIED} ${config.order.name}"
            MediaSortType.SIZE -> "${MediaStore.Files.FileColumns.SIZE} ${config.order.name}"
        }
    }


}