package com.bytelyst.platform

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.concurrent.TimeUnit

// Top-level enums used by BroadcastUI
enum class MessagePriority { LOW, NORMAL, HIGH, URGENT }
enum class MessageStyle { BANNER, MODAL, TOAST, FULLSCREEN }
enum class MessageStatus { UNREAD, READ, DISMISSED }

// Top-level type alias for convenience
typealias InAppMessage = BLBroadcastClient.InAppMessage

/**
 * Broadcast Client — In-app message client for Android.
 * Part of ByteLystPlatformSDK.
 */
class BLBroadcastClient(
    private val config: BLPlatformConfig,
    private val tokenProvider: () -> String? = { null },
) {
    private val httpClient = OkHttpClient.Builder()
        .connectTimeout(30, TimeUnit.SECONDS)
        .readTimeout(30, TimeUnit.SECONDS)
        .build()

    private val json = Json { ignoreUnknownKeys = true }
    private var pollingJob: kotlinx.coroutines.Job? = null

    enum class Priority {
        LOW, NORMAL, HIGH, URGENT
    }

    enum class Style {
        BANNER, MODAL, TOAST, FULLSCREEN
    }

    enum class Status {
        UNREAD, READ, DISMISSED
    }

    @Serializable
    data class InAppMessage(
        val id: String,
        val userId: String,
        val productId: String,
        val broadcastId: String,
        val title: String,
        val body: String,
        val bodyMarkdown: String? = null,
        val ctaText: String? = null,
        val ctaUrl: String? = null,
        val priority: String,
        val style: String,
        val dismissible: Boolean,
        val expiresAt: String? = null,
        val imageUrl: String? = null,
        val status: String,
        val createdAt: String,
        val updatedAt: String,
    )

    @Serializable
    private data class MessagesResponse(
        val messages: List<InAppMessage>
    )

    @Serializable
    private data class ClickResponse(
        val success: Boolean,
        val redirectUrl: String? = null,
    )

    /**
     * List active in-app messages for the current user.
     */
    suspend fun getMessages() = listMessages()

    suspend fun listMessages(): Result<List<InAppMessage>> = withContext(Dispatchers.IO) {
        try {
            val request = buildRequest(path = "/broadcasts")
            val response = httpClient.newCall(request).execute()

            if (!response.isSuccessful) {
                return@withContext Result.failure(Exception("HTTP ${response.code}"))
            }

            val body = response.body?.string() ?: return@withContext Result.failure(Exception("Empty response"))
            val result = json.decodeFromString(MessagesResponse.serializer(), body)
            Result.success(result.messages)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    /**
     * Mark a message as read.
     */
    suspend fun markRead(messageId: String): Result<Unit> = withContext(Dispatchers.IO) {
        try {
            val request = buildRequest(
                path = "/broadcasts/$messageId/read",
                method = "POST"
            )
            val response = httpClient.newCall(request).execute()

            if (!response.isSuccessful) {
                return@withContext Result.failure(Exception("HTTP ${response.code}"))
            }

            Result.success(Unit)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    /**
     * Mark a message as dismissed.
     */
    suspend fun markDismissed(messageId: String): Result<Unit> = withContext(Dispatchers.IO) {
        try {
            val request = buildRequest(
                path = "/broadcasts/$messageId/dismiss",
                method = "POST"
            )
            val response = httpClient.newCall(request).execute()

            if (!response.isSuccessful) {
                return@withContext Result.failure(Exception("HTTP ${response.code}"))
            }

            Result.success(Unit)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    /**
     * Track a CTA click and get the redirect URL.
     */
    suspend fun trackClick(messageId: String): Result<String?> = withContext(Dispatchers.IO) {
        try {
            val request = buildRequest(
                path = "/broadcasts/$messageId/click",
                method = "POST"
            )
            val response = httpClient.newCall(request).execute()

            if (!response.isSuccessful) {
                return@withContext Result.failure(Exception("HTTP ${response.code}"))
            }

            val body = response.body?.string() ?: return@withContext Result.success(null)
            val result = json.decodeFromString(ClickResponse.serializer(), body)
            Result.success(result.redirectUrl)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    /**
     * Start polling for new messages.
     */
    fun startPolling(
        intervalMs: Long = 60000L,
        onUpdate: (List<InAppMessage>) -> Unit,
    ) {
        stopPolling()
        pollingJob = kotlinx.coroutines.CoroutineScope(Dispatchers.IO).launch {
            while (isActive) {
                listMessages()
                    .onSuccess { messages -> onUpdate(messages) }
                    .onFailure { /* Silently ignore polling errors */ }
                delay(intervalMs)
            }
        }
    }

    /**
     * Stop polling for messages.
     */
    fun stopPolling() {
        pollingJob?.cancel()
        pollingJob = null
    }

    private fun buildRequest(
        path: String,
        method: String = "GET",
        body: String? = null,
    ): Request {
        val url = "${config.baseUrl}$path"
        val token = tokenProvider() ?: ""

        val builder = Request.Builder()
            .url(url)
            .header("Authorization", "Bearer $token")
            .header("x-product-id", config.productId)
            .header("x-platform", "android")
            .header("x-app-version", config.appVersion)
            .header("x-os-version", config.osVersion)

        if (body != null) {
            builder.method(method, body.toRequestBody("application/json".toMediaType()))
        } else if (method != "GET") {
            builder.method(method, "".toRequestBody(null))
        }

        return builder.build()
    }
}
