package com.demoscene.tv.data
import android.content.Context
import android.util.Log
import com.google.gson.Gson
import com.google.gson.JsonElement
import com.google.gson.annotations.SerializedName
import com.google.gson.stream.JsonReader
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.File
import java.io.InputStream
import java.util.concurrent.TimeUnit
import java.util.zip.GZIPInputStream
import org.tukaani.xz.XZInputStream

data class PouetProd(
    @SerializedName("id") val id: String?,
    @SerializedName("name") val name: String?,
    @SerializedName("groups") val groups: JsonElement?,
    @SerializedName("releaseDate") val releaseDate: String?,
    @SerializedName("platforms") val platforms: JsonElement?,
    @SerializedName("type") val type: String?,
    @SerializedName("voteup") val voteup: Int?,
    @SerializedName("votepig") val votepig: Int?,
    @SerializedName("votedown") val votedown: Int?,
    @SerializedName("screenshot") val screenshot: String?,
    @SerializedName("downloadLinks") val downloadLinks: JsonElement?
)
data class PouetDump(
    @SerializedName("prods") val prods: List<PouetProd>?
)
data class PouetDumpInfo(
    @SerializedName("filename") val filename: String?,
    @SerializedName("url") val url: String?
)
data class PouetDumpDate(
    @SerializedName("prods") val prods: PouetDumpInfo?
)
data class PouetDumpMetadata(
    @SerializedName("dumps") val dumps: Map<String, PouetDumpDate>?
)
object PouetCache {
    private const val POUET_METADATA_URL = "https://data.pouet.net/json.php"
    private const val CACHE_FILE = "pouet_filtered.json"
    private fun getCacheFile(context: Context): File {
        return File(context.filesDir, CACHE_FILE)
    }
    private fun hasYoutubeLink(prodElement: JsonElement): Boolean {
        try {
            if (!prodElement.isJsonObject) return false
            val prodObj = prodElement.asJsonObject
            val linksElement = prodObj.get("downloadLinks")
            if (linksElement == null || !linksElement.isJsonArray) return false
            val linksArray = linksElement.asJsonArray
            for (linkElement in linksArray) {
                if (!linkElement.isJsonObject) continue
                val linkObj = linkElement.asJsonObject
                val typeElement = linkObj.get("type")
                if (typeElement != null && typeElement.isJsonPrimitive) {
                    val typeStr = typeElement.asString
                    if (typeStr == "youtube") {
                        return true
                    }
                }
            }
        } catch (e: Exception) {
        }
        return false
    }
    
    // Decompress based on URL extension - handles both .xz and .gz
    private fun decompressStream(inputStream: InputStream, url: String): InputStream {
        return when {
            url.endsWith(".gz") -> GZIPInputStream(inputStream)
            url.endsWith(".xz") -> XZInputStream(inputStream)
            else -> GZIPInputStream(inputStream) // Default to gzip for new format
        }
    }
    
    suspend fun downloadAndCache(context: Context, onProgress: (String) -> Unit = {}): Boolean {
        return withContext(Dispatchers.IO) {
            try {
                onProgress("Getting dump info...")
                Log.d("DemosceneTV", "Fetching metadata")
                val client = OkHttpClient.Builder()
                    .connectTimeout(60, TimeUnit.SECONDS)
                    .readTimeout(300, TimeUnit.SECONDS)
                    .build()
                val metaRequest = Request.Builder().url(POUET_METADATA_URL).build()
                val metaResponse = client.newCall(metaRequest).execute()
                val metaJson = metaResponse.body?.string() ?: return@withContext false
                val metadata = Gson().fromJson(metaJson, PouetDumpMetadata::class.java)
                val latestDate = metadata.dumps?.keys?.maxOrNull()
                val dumpUrl = metadata.dumps?.get(latestDate)?.prods?.url ?: return@withContext false
                Log.d("DemosceneTV", "Downloading: " + dumpUrl)
                onProgress("Downloading dump...")
                val dumpRequest = Request.Builder().url(dumpUrl).build()
                val dumpResponse = client.newCall(dumpRequest).execute()
                val rawStream = dumpResponse.body?.byteStream() ?: return@withContext false
                
                // Use correct decompressor based on file extension
                val decompressedStream = decompressStream(rawStream, dumpUrl)
                
                onProgress("Processing (this takes a minute)...")
                Log.d("DemosceneTV", "Streaming and filtering...")
                val filteredProds = mutableListOf<PouetProd>()
                val jsonReader = JsonReader(decompressedStream.bufferedReader())
                jsonReader.beginObject()
                while (jsonReader.hasNext()) {
                    if (jsonReader.nextName() == "prods") {
                        jsonReader.beginArray()
                        var count = 0
                        while (jsonReader.hasNext()) {
                            try {
                                val prodElement = Gson().fromJson<JsonElement>(jsonReader, JsonElement::class.java)
                                if (hasYoutubeLink(prodElement)) {
                                    val prod = Gson().fromJson<PouetProd>(prodElement, PouetProd::class.java)
                                    filteredProds.add(prod)
                                }
                                count++
                                if (count % 5000 == 0) {
                                    Log.d("DemosceneTV", "Processed " + count.toString() + ", kept " + filteredProds.size.toString())
                                }
                            } catch (e: Exception) {
                            }
                        }
                        jsonReader.endArray()
                    } else {
                        jsonReader.skipValue()
                    }
                }
                jsonReader.endObject()
                jsonReader.close()
                Log.d("DemosceneTV", "Filtered to " + filteredProds.size.toString() + " demos")
                onProgress("Saving " + filteredProds.size.toString() + " demos...")
                val filtered = PouetDump(filteredProds)
                val json = Gson().toJson(filtered)
                getCacheFile(context).writeText(json)
                Log.d("DemosceneTV", "Saved: " + json.length.toString() + " bytes")
                onProgress("Complete!")
                true
            } catch (e: Exception) {
                Log.e("DemosceneTV", "Error: " + (e.message ?: "Unknown"), e)
                onProgress("Error: " + (e.message ?: "Unknown"))
                false
            }
        }
    }
    suspend fun isCached(context: Context): Boolean {
        return withContext(Dispatchers.IO) {
            val file = getCacheFile(context)
            file.exists() && file.length() > 10000
        }
    }
    suspend fun loadFromCache(context: Context): PouetDump? {
        return withContext(Dispatchers.IO) {
            try {
                val json = getCacheFile(context).readText()
                Log.d("DemosceneTV", "Loading " + json.length.toString() + " bytes")
                Gson().fromJson(json, PouetDump::class.java)
            } catch (e: Exception) {
                Log.e("DemosceneTV", "Load error: " + (e.message ?: "Unknown"), e)
                null
            }
        }
    }
}
