Pith - shim
package com.vgmlr.shim

import android.content.Context
import android.net.Uri
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File

object ShimSave {
    private const val KEY_LOCATION = "backup_location" // legacy key
    private const val KEY_FORMAT = "save_format"

    const val FORMAT_DEFAULT = "db"
    val FORMATS = listOf("db", "csv", "txt", "json")
    private val STAMPED = Regex("""^shim\d+\.(${FORMATS.joinToString("|")})$""")

    fun formatLabel(ext: String): String = when (ext) {
        "csv" -> "Comma (.csv)"
        "txt" -> "Text (.txt)"
        "json" -> "JSON (.json)"
        else -> "SQL3 (.db)"
    }

    private fun mimeOf(ext: String): String = when (ext) {
        "csv" -> "text/csv"
        "txt" -> "text/plain"
        "json" -> "application/json"
        else -> "application/octet-stream" // keeps .db
    }

    private fun prefs(context: Context) =
        context.getSharedPreferences("shim_prefs", Context.MODE_PRIVATE)

    fun location(context: Context): String =
        prefs(context).getString(KEY_LOCATION, "") ?: ""

    fun format(context: Context): String =
        prefs(context).getString(KEY_FORMAT, FORMAT_DEFAULT)?.takeIf { it in FORMATS }
            ?: FORMAT_DEFAULT

    fun saveSettings(context: Context, location: String, format: String) {
        prefs(context).edit {
            putString(KEY_LOCATION, location)
            putString(KEY_FORMAT, format)
        }
    }

    private fun fileSafe(s: String): String =
        s.replace(Regex("""[\\/:*?"<>|#]"""), "").trim()

    suspend fun save(
        context: Context,
        db: ShimDatabase,
        shims: List<ShimClass>? = null,
        keyword: String? = null
    ): Boolean =
        withContext(Dispatchers.IO) {
            runCatching {
                val ext = format(context)
                val tag = keyword?.let { fileSafe(it) }.orEmpty()
                val name =
                    if (tag.isEmpty()) "shim${ShimOTCClock.getFileNameTimestamp()}.$ext" else "shim_$tag.$ext"
                val uri = target(context, name, mimeOf(ext)) ?: return@runCatching false
                val rows = shims ?: db.getAllShims()
                when (ext) {
                    "csv" -> db.exportCsv(context, uri, rows)
                    "txt" -> db.exportTxt(context, uri, rows)
                    "json" -> db.exportJson(context, uri, rows)
                    else -> db.exportDb(context, uri, rows)
                }
                if (tag.isEmpty()) runCatching { prune(context, uri) }
                true
            }.getOrDefault(false)
        }

    private fun target(context: Context, name: String, mime: String): Uri? {
        val loc = location(context)
        if (loc.isEmpty()) {
            val dir = context.getExternalFilesDir(null) ?: return null // app/
            return Uri.fromFile(File(dir, name))
        }
        val tree = DocumentFile.fromTreeUri(context, loc.toUri()) ?: return null
        return tree.createFile(mime, name)?.uri
    }

    private fun prune(context: Context, keep: Uri) {
        val loc = location(context)
        if (loc.isEmpty()) {
            val dir = context.getExternalFilesDir(null) ?: return
            dir.listFiles()
                ?.filter { STAMPED.matches(it.name) && Uri.fromFile(it) != keep }
                ?.forEach { it.delete() }
            return
        }
        val tree = DocumentFile.fromTreeUri(context, loc.toUri()) ?: return
        tree.listFiles()
            .filter { STAMPED.matches(it.name.orEmpty()) && it.uri != keep }
            .forEach { it.delete() }
    }
}