package com.aryan.reader.desktop

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import java.net.HttpURLConnection
import java.net.URL
import java.net.URLEncoder

internal data class DesktopAccountProfile(
    val isProUser: Boolean = false,
    val credits: Int = 0
)

internal class DesktopAccountProfileRepository(
    private val config: DesktopCloudConfig
) {
    suspend fun fetchProfile(uid: String, idToken: String): DesktopAccountProfile = withContext(Dispatchers.IO) {
        if (uid.isBlank() || idToken.isBlank()) return@withContext DesktopAccountProfile()
        val url = "https://firestore.googleapis.com/v1/projects/${urlEncode(config.firebaseProjectId)}/databases/(default)/documents/users/${urlEncode(uid)}"
        val connection = (URL(url).openConnection() as HttpURLConnection).apply {
            requestMethod = "GET"
            setRequestProperty("Authorization", "Bearer $idToken")
            setRequestProperty("Accept", "application/json")
            connectTimeout = 12_000
            readTimeout = 20_000
        }
        try {
            if (connection.responseCode == HttpURLConnection.HTTP_NOT_FOUND) return@withContext DesktopAccountProfile()
            val stream = if (connection.responseCode in 200..299) connection.inputStream else connection.errorStream
            val text = stream?.bufferedReader(Charsets.UTF_8)?.use { it.readText() }.orEmpty()
            if (connection.responseCode !in 200..299) {
                throw IllegalStateException("Could not check account status: HTTP ${connection.responseCode}")
            }
            val fields = DesktopAccountJson.parseToJsonElement(text).jsonObject["fields"].jsonObjectOrNull()
            DesktopAccountProfile(
                isProUser = fields?.booleanField("isPro") == true,
                credits = fields?.numberField("credits")?.toInt() ?: 0
            )
        } finally {
            connection.disconnect()
        }
    }
}

private val DesktopAccountJson = Json { ignoreUnknownKeys = true }

private fun JsonObject?.booleanField(key: String): Boolean? {
    return this?.get(key)
        ?.jsonObjectOrNull()
        ?.get("booleanValue")
        ?.jsonPrimitive
        ?.contentOrNull
        ?.toBooleanStrictOrNull()
}

private fun JsonObject?.numberField(key: String): Double? {
    val field = this?.get(key)?.jsonObjectOrNull() ?: return null
    return field["integerValue"]?.jsonPrimitive?.contentOrNull?.toDoubleOrNull()
        ?: field["doubleValue"]?.jsonPrimitive?.contentOrNull?.toDoubleOrNull()
}

private fun JsonElement?.jsonObjectOrNull(): JsonObject? = this as? JsonObject

private fun urlEncode(value: String): String = URLEncoder.encode(value, Charsets.UTF_8.name())
