package com.joe.camera.webcam.data

import android.content.ContentUris
import android.content.ContentValues
import android.content.Context
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import com.joe.camera.webcam.model.MediaItem
import com.joe.camera.webcam.model.MediaKind
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

class MediaRepository(private val context: Context) {
    companion object {
        val RELATIVE_DIR = "${Environment.DIRECTORY_DOWNLOADS}/jf"
        const val IMAGE_MIME = "image/jpeg"
        const val VIDEO_MIME = "video/mp4"
        const val DISGUISED_VIDEO_MIME = "application/octet-stream"
        fun timestamp(): String = SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.US).format(Date())
    }

    fun imageValues() = contentValues("${timestamp()}.jp", IMAGE_MIME)
    fun videoValues() = contentValues("${timestamp()}.jv", DISGUISED_VIDEO_MIME)

    fun importRecordedVideo(source: File): Boolean {
        val values = videoValues().apply {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) put(MediaStore.MediaColumns.IS_PENDING, 1)
        }
        val resolver = context.contentResolver
        val collection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            MediaStore.Downloads.EXTERNAL_CONTENT_URI
        } else {
            MediaStore.Files.getContentUri("external")
        }
        val uri = runCatching { resolver.insert(collection, values) }.getOrNull() ?: run {
            source.delete()
            return false
        }
        return try {
            resolver.openOutputStream(uri, "w")?.use { output -> source.inputStream().use { it.copyTo(output) } }
                ?: throw IllegalStateException("无法打开媒体输出流")
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                resolver.update(uri, ContentValues().apply { put(MediaStore.MediaColumns.IS_PENDING, 0) }, null, null)
            }
            true
        } catch (_: Exception) {
            resolver.delete(uri, null, null)
            false
        } finally {
            source.delete()
        }
    }

    private fun contentValues(name: String, mime: String) = ContentValues().apply {
        put(MediaStore.MediaColumns.DISPLAY_NAME, name)
        put(MediaStore.MediaColumns.MIME_TYPE, mime)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            put(MediaStore.MediaColumns.RELATIVE_PATH, RELATIVE_DIR)
        } else {
            @Suppress("DEPRECATION")
            put(MediaStore.MediaColumns.DATA, File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "jf/$name").absolutePath)
        }
    }

    fun loadMedia(): List<MediaItem> {
        val result = mutableListOf<MediaItem>()
        queryCollection(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaKind.IMAGE, result)
        queryCollection(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, MediaKind.VIDEO, result)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            queryCollection(MediaStore.Downloads.EXTERNAL_CONTENT_URI, null, result)
        }
        return result
            .distinctBy { "${it.kind}:${it.displayName}:${it.modifiedSeconds}" }
            .sortedByDescending(MediaItem::modifiedSeconds)
    }

    private fun queryCollection(collection: Uri, expectedKind: MediaKind?, output: MutableList<MediaItem>) {
        val projection = arrayOf(MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DISPLAY_NAME, MediaStore.MediaColumns.DATE_MODIFIED)
        val selection: String
        val args: Array<String>
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            selection = "${MediaStore.MediaColumns.RELATIVE_PATH}=? AND (${MediaStore.MediaColumns.DISPLAY_NAME} LIKE ? OR ${MediaStore.MediaColumns.DISPLAY_NAME} LIKE ?)"
            args = arrayOf("$RELATIVE_DIR/", "%.jp", "%.jv")
        } else {
            @Suppress("DEPRECATION")
            selection = "${MediaStore.MediaColumns.DATA} LIKE ? AND (${MediaStore.MediaColumns.DISPLAY_NAME} LIKE ? OR ${MediaStore.MediaColumns.DISPLAY_NAME} LIKE ?)"
            args = arrayOf("%/Download/jf/%", "%.jp", "%.jv")
        }
        runCatching {
            context.contentResolver.query(collection, projection, selection, args, "${MediaStore.MediaColumns.DATE_MODIFIED} DESC")?.use { cursor ->
                val idColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID)
                val nameColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)
                val dateColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED)
                while (cursor.moveToNext()) {
                    val name = cursor.getString(nameColumn)
                    val actualKind = if (name.endsWith(".jp", true)) MediaKind.IMAGE else MediaKind.VIDEO
                    if (expectedKind == null || actualKind == expectedKind) {
                        output += MediaItem(cursor.getLong(idColumn), ContentUris.withAppendedId(collection, cursor.getLong(idColumn)), name, actualKind, cursor.getLong(dateColumn))
                    }
                }
            }
        }
    }

    fun delete(item: MediaItem): Boolean = runCatching { context.contentResolver.delete(item.uri, null, null) > 0 }.getOrDefault(false)
}
