package xyz.cottongin.radio247.metadata

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import org.json.JSONObject
import java.net.URLEncoder

class AlbumArtResolver(
    private val client: OkHttpClient,
    private val artCache: ArtCache = ArtCache(),
    private val musicBrainzBaseUrl: String = "https://musicbrainz.org",
    private val coverArtBaseUrl: String = "https://coverartarchive.org",
    private val skipArtVerification: Boolean = false,
    private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) {
    private var lastMusicBrainzCall = 0L

    suspend fun resolve(
        artist: String?,
        title: String?,
        icyStreamUrl: String?,
        stationArtworkUrl: String?
    ): String? {
        val cacheKey = "${artist.orEmpty()}-${title.orEmpty()}"
        artCache.get(cacheKey)?.let { return it }

        // 1. MusicBrainz — only if artist AND title are present
        if (!artist.isNullOrBlank() && !title.isNullOrBlank()) {
            val artUrl = queryMusicBrainz(artist, title)
            if (artUrl != null) {
                artCache.put(cacheKey, artUrl)
                return artUrl
            }
        }

        // 2. ICY StreamUrl — if it looks like an image URL
        if (!icyStreamUrl.isNullOrBlank() && looksLikeImageUrl(icyStreamUrl)) {
            artCache.put(cacheKey, icyStreamUrl)
            return icyStreamUrl
        }

        // 3. Station default artwork (from #EXTIMG)
        if (!stationArtworkUrl.isNullOrBlank()) {
            return stationArtworkUrl
        }

        // 4. No art found
        return null
    }

    private suspend fun queryMusicBrainz(artist: String, title: String): String? {
        // Rate limit: 1 req/sec
        val now = System.currentTimeMillis()
        val elapsed = now - lastMusicBrainzCall
        if (elapsed < 1100) {
            delay(1100 - elapsed)
        }
        lastMusicBrainzCall = System.currentTimeMillis()

        return withContext(ioDispatcher) {
            try {
                val query = "artist:\"${artist}\" AND recording:\"${title}\""
                val encodedQuery = URLEncoder.encode(query, "UTF-8")
                val url = "$musicBrainzBaseUrl/ws/2/recording?query=$encodedQuery&fmt=json&limit=1"

                val request = Request.Builder()
                    .url(url)
                    .header("User-Agent", "Radio247/1.0 (personal use)")
                    .build()

                val response = client.newCall(request).execute()
                if (!response.isSuccessful) {
                    response.close()
                    return@withContext null
                }

                val json = JSONObject(response.body?.string() ?: return@withContext null)
                val recordings = json.optJSONArray("recordings") ?: return@withContext null
                if (recordings.length() == 0) return@withContext null

                val recording = recordings.getJSONObject(0)
                val releases = recording.optJSONArray("releases") ?: return@withContext null
                if (releases.length() == 0) return@withContext null

                val releaseId = releases.getJSONObject(0).getString("id")
                val artUrl = "$coverArtBaseUrl/release/$releaseId/front-250"

                // Verify the art URL returns 200 (Cover Art Archive returns 404 for missing art)
                if (skipArtVerification) return@withContext artUrl
                val artRequest = Request.Builder().url(artUrl).head().build()
                val artResponse = client.newCall(artRequest).execute()
                artResponse.close()
                if (artResponse.isSuccessful || artResponse.code == 307) artUrl else null
            } catch (e: Exception) {
                null
            }
        }
    }

    private fun looksLikeImageUrl(url: String): Boolean {
        val lower = url.lowercase()
        return lower.endsWith(".jpg") || lower.endsWith(".jpeg") ||
            lower.endsWith(".png") || lower.endsWith(".webp") ||
            lower.endsWith(".gif") || lower.contains("image")
    }
}
