package com.lazy.emulate.data

import android.content.Context
import android.util.Log
import com.lazy.emulate.emulation.ConsoleType
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import java.net.HttpURLConnection
import java.net.URL
import java.net.URLEncoder

class CoverArtManager(private val context: Context) {

    private val cacheDir = File(context.filesDir, "cover_art")

    init {
        cacheDir.mkdirs()
    }

    fun getCachedPath(gameTitle: String, consoleType: ConsoleType): String? {
        val file = cacheFile(gameTitle, consoleType)
        return if (file.exists()) file.absolutePath else null
    }

    suspend fun fetchCoverArt(gameTitle: String, consoleType: ConsoleType): String? {
        val cached = getCachedPath(gameTitle, consoleType)
        if (cached != null) return cached

        if (consoleType.libretroThumbnailRepo.isEmpty()) return null

        val dest = cacheFile(gameTitle, consoleType)

        // Build the list of candidate filenames to try, in priority order:
        //   1. Title as-is
        //   2. Title with GoodTools-style region tags expanded into LibRetro forms
        //      (e.g. "(U)" -> "(USA)", "(USA, Europe)", "(World)")
        //   3. Fully cleaned title (no parens at all) — rare hit
        //   4. Cleaned title with each common LibRetro region tag appended — this catches
        //      cases like "Super Mario Bros. (U)" where LibRetro stores "(World)" but our
        //      region table didn't know that this specific game used the World tag.
        val namesToTry = buildList {
            add(gameTitle)
            val expanded = expandRegions(gameTitle)
            expanded.filter { it != gameTitle }.forEach { add(it) }
            val cleaned = cleanTitle(gameTitle)
            if (cleaned != gameTitle && cleaned !in expanded) add(cleaned)
            // Append fallback region tags to the bare cleaned title
            for (region in COMMON_LIBRETRO_REGIONS) {
                val candidate = "$cleaned $region"
                if (candidate !in this) add(candidate)
            }
        }

        for (name in namesToTry) {
            val encoded = URLEncoder.encode(name, "UTF-8")
                .replace("+", "%20")
            val url = "$BASE_URL/${consoleType.libretroThumbnailRepo}/master/Named_Boxarts/$encoded.png"
            if (downloadFile(url, dest)) {
                Log.d(TAG, "Cover art hit for '$gameTitle' via '$name'")
                return dest.absolutePath
            }
        }

        Log.d(TAG, "No cover art match for '$gameTitle' after ${namesToTry.size} attempts")
        return null
    }

    private fun cacheFile(gameTitle: String, consoleType: ConsoleType): File {
        val safeTitle = gameTitle.replace(Regex("[^a-zA-Z0-9._\\- ]"), "_")
        val dir = File(cacheDir, consoleType.name)
        dir.mkdirs()
        return File(dir, "$safeTitle.png")
    }

    private suspend fun downloadFile(urlStr: String, dest: File): Boolean = withContext(Dispatchers.IO) {
        try {
            val connection = URL(urlStr).openConnection() as HttpURLConnection
            connection.connectTimeout = 10_000
            connection.readTimeout = 10_000
            connection.instanceFollowRedirects = true

            if (connection.responseCode != 200) {
                connection.disconnect()
                return@withContext false
            }

            connection.inputStream.use { input ->
                dest.outputStream().use { output ->
                    input.copyTo(output)
                }
            }
            connection.disconnect()
            true
        } catch (e: Exception) {
            Log.d(TAG, "Failed to download cover art: ${e.message}")
            dest.delete()
            false
        }
    }

    companion object {
        private const val TAG = "CoverArtManager"
        private const val BASE_URL = "https://raw.githubusercontent.com/libretro-thumbnails"

        // GoodTools-style short region tags can map to several LibRetro long forms.
        // Order matters — earlier entries are tried first.
        private val regionExpansions = mapOf(
            "(U)" to listOf("(USA)", "(USA, Europe)", "(World)"),
            "(E)" to listOf("(Europe)", "(Europe, Australia)", "(World)"),
            "(J)" to listOf("(Japan)", "(Japan, USA)", "(World)"),
            "(UE)" to listOf("(USA, Europe)", "(USA)", "(World)"),
            "(JU)" to listOf("(Japan, USA)", "(USA)", "(Japan)"),
            "(W)" to listOf("(World)"),
            "(F)" to listOf("(France)"),
            "(G)" to listOf("(Germany)"),
            "(S)" to listOf("(Spain)"),
            "(I)" to listOf("(Italy)"),
            "(Unl)" to listOf("(USA) (Unl)", "(Unl)"),
        )

        // Region tags to append to a cleaned (paren-stripped) title as a fallback for ROMs
        // whose original filename had no region tag, an unrecognized region tag, or a tag
        // that doesn't match what LibRetro stores for that specific game (e.g. SMB1 is
        // "(World)" but most US dumps tag it "(U)").
        private val COMMON_LIBRETRO_REGIONS = listOf(
            "(USA)",
            "(World)",
            "(USA, Europe)",
            "(Europe)",
            "(Japan)",
            "(Japan, USA)",
        )

        fun expandRegions(title: String): List<String> {
            val stripped = title
                .replace(Regex("\\s*\\[[^]]*]"), "") // strip flags like [!], [b], [p1]
                .trim()
            for ((short, expansions) in regionExpansions) {
                if (short in stripped) {
                    return expansions.map { stripped.replace(short, it) }
                }
            }
            return listOf(stripped)
        }

        fun cleanTitle(title: String): String {
            return title
                // Remove region tags: (USA), (Europe), (Japan), etc.
                .replace(Regex("\\s*\\([^)]*\\)"), "")
                // Remove flags: [!], [b], etc.
                .replace(Regex("\\s*\\[[^]]*]"), "")
                // Remove leading "The " for matching (LibRetro uses "Game, The" format)
                .trim()
                .let { cleaned ->
                    if (cleaned.startsWith("The ", ignoreCase = true)) {
                        cleaned.removePrefix("The ").removePrefix("the ") + ", The"
                    } else cleaned
                }
        }
    }
}
