package com.bytelyst.platform

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.UUID
import java.util.concurrent.TimeUnit

/**
 * Azure Blob Storage client via platform-service SAS tokens.
 *
 * Flow: Get SAS token from POST /api/blob/sas → Upload directly to Azure Blob.
 * Mirrors the Swift BLBlobClient API.
 */
class BLBlobClient(
    private val config: BLPlatformConfig,
    private val tokenProvider: () -> String? = { null },
) {
    @Serializable
    data class SasResponse(
        val sasUrl: String,
        val blobUrl: String,
    )

    private val json = Json { ignoreUnknownKeys = true }

    private val platformClient = BLPlatformClient(config, tokenProvider)

    private val uploadClient = OkHttpClient.Builder()
        .connectTimeout(60, TimeUnit.SECONDS)
        .writeTimeout(120, TimeUnit.SECONDS)
        .readTimeout(30, TimeUnit.SECONDS)
        .build()

    /**
     * Upload data to Azure Blob Storage.
     *
     * @param data The raw bytes to upload.
     * @param container Azure Blob container name (e.g., "audio", "attachments").
     * @param fileName The blob name / file path.
     * @param contentType MIME type (e.g., "audio/wav", "image/png").
     * @return The public blob URL on success, or null on failure.
     */
    suspend fun upload(
        data: ByteArray,
        container: String,
        fileName: String,
        contentType: String,
    ): String? = withContext(Dispatchers.IO) {
        try {
            // Step 1: Get SAS token from platform-service
            val sasBody = json.encodeToString(
                MapSerializer(String.serializer(), String.serializer()),
                mapOf(
                    "container" to container,
                    "blobName" to fileName,
                    "permissions" to "w",
                    "contentType" to contentType,
                ),
            )
            val sasResponse = platformClient.request("POST", "/api/blob/sas", sasBody)
            val sas = json.decodeFromString<SasResponse>(sasResponse)

            // Step 2: Upload directly to Azure Blob via SAS URL
            val request = Request.Builder()
                .url(sas.sasUrl)
                .put(data.toRequestBody(contentType.toMediaType()))
                .header("x-ms-blob-type", "BlockBlob")
                .header("x-ms-blob-content-type", contentType)
                .build()

            uploadClient.newCall(request).execute().use { response ->
                if (response.isSuccessful) sas.blobUrl else null
            }
        } catch (_: Exception) {
            null
        }
    }

    /**
     * Upload audio data (convenience method).
     */
    suspend fun uploadAudio(data: ByteArray, fileName: String): String? {
        return upload(data, "audio", fileName, "audio/wav")
    }
}
