package xyz.cottongin.radio247.data.logging

import android.content.Context
import android.net.Uri
import android.os.Environment
import xyz.cottongin.radio247.data.prefs.RadioPreferences
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileOutputStream
import java.io.OutputStream
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

class NowPlayingHistoryWriter(
    private val context: Context,
    private val preferences: RadioPreferences
) {
    private val mutex = Mutex()

    suspend fun append(station: String, artist: String?, title: String?, timestamp: Long) {
        val enabled = preferences.historyEnabled.first()
        if (!enabled) return

        val format = preferences.historyFormat.first()
        val line = formatLine(format, station, artist, title, timestamp)

        mutex.withLock {
            withContext(Dispatchers.IO) {
                val outputStream = openOutputStream(format)
                outputStream?.use { os ->
                    os.write(line.toByteArray())
                    os.write("\n".toByteArray())
                }
            }
        }
    }

    private suspend fun openOutputStream(format: String): OutputStream? {
        val dirUri = preferences.historyDirUri.first()
        val fileName = fileNameForFormat(format)

        return if (dirUri.isNotEmpty()) {
            try {
                val treeUri = Uri.parse(dirUri)
                val docUri = findOrCreateFile(treeUri, fileName)
                docUri?.let { context.contentResolver.openOutputStream(it, "wa") }
            } catch (_: Exception) {
                fallbackOutputStream(fileName)
            }
        } else {
            fallbackOutputStream(fileName)
        }
    }

    private fun fallbackOutputStream(fileName: String): OutputStream? {
        val dir = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) ?: return null
        val file = File(dir, fileName)
        return FileOutputStream(file, true)
    }

    private fun findOrCreateFile(treeUri: Uri, fileName: String): Uri? {
        val childrenUri = android.provider.DocumentsContract.buildChildDocumentsUriUsingTree(
            treeUri,
            android.provider.DocumentsContract.getTreeDocumentId(treeUri)
        )
        val cursor = context.contentResolver.query(
            childrenUri,
            arrayOf(
                android.provider.DocumentsContract.Document.COLUMN_DOCUMENT_ID,
                android.provider.DocumentsContract.Document.COLUMN_DISPLAY_NAME
            ),
            null, null, null
        )
        cursor?.use {
            while (it.moveToNext()) {
                val name = it.getString(1)
                if (name == fileName) {
                    val docId = it.getString(0)
                    return android.provider.DocumentsContract.buildDocumentUriUsingTree(treeUri, docId)
                }
            }
        }
        val parentDocId = android.provider.DocumentsContract.getTreeDocumentId(treeUri)
        val parentUri = android.provider.DocumentsContract.buildDocumentUriUsingTree(treeUri, parentDocId)
        val mimeType = when {
            fileName.endsWith(".csv") -> "text/csv"
            fileName.endsWith(".jsonl") -> "application/jsonl"
            else -> "text/plain"
        }
        return android.provider.DocumentsContract.createDocument(
            context.contentResolver, parentUri, mimeType, fileName
        )
    }

    companion object {
        private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US)
        private val isoFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US)

        fun fileNameForFormat(format: String): String = when (format) {
            "json" -> "now-playing-history.jsonl"
            "plain" -> "now-playing-history.txt"
            else -> "now-playing-history.csv"
        }

        fun formatLine(
            format: String,
            station: String,
            artist: String?,
            title: String?,
            timestamp: Long
        ): String {
            val date = Date(timestamp)
            return when (format) {
                "json" -> {
                    val ts = isoFormat.format(date)
                    val a = (artist ?: "").replace("\"", "\\\"")
                    val t = (title ?: "").replace("\"", "\\\"")
                    val s = station.replace("\"", "\\\"")
                    """{"timestamp":"$ts","station":"$s","artist":"$a","title":"$t"}"""
                }
                "plain" -> {
                    val ts = dateFormat.format(date)
                    val track = listOfNotNull(artist, title).joinToString(" - ").ifEmpty { "Unknown" }
                    "$ts | $station | $track"
                }
                else -> {
                    val ts = dateFormat.format(date)
                    "\"$ts\",\"${station.csvEscape()}\",\"${(artist ?: "").csvEscape()}\",\"${(title ?: "").csvEscape()}\""
                }
            }
        }

        private fun String.csvEscape(): String = replace("\"", "\"\"")
    }
}
