package com.seven.ikar.kotlin.core.local

import androidx.room.withTransaction
import com.seven.ikar.kotlin.core.model.MessageDto
import kotlinx.serialization.json.Json
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString

class ChatMessageCache(
    private val database: IkarLocalDatabase,
    private val dao: CachedChatMessageDao,
    private val json: Json
) {
    suspend fun loadLatestMessages(
        ownerUserId: String,
        chatId: String,
        limit: Int = LatestMessagesLimit
    ): List<MessageDto> =
        dao.loadLatestMessages(ownerUserId, chatId, limit)
            .mapNotNull { it.toMessageOrNull() }
            .sortedBy(MessageDto::sentAt)

    suspend fun loadOptimisticMessages(ownerUserId: String, chatId: String): List<MessageDto> =
        dao.loadOptimisticMessages(ownerUserId, chatId)
            .mapNotNull { it.toMessageOrNull() }
            .sortedBy(MessageDto::sentAt)

    suspend fun upsertMessages(
        ownerUserId: String,
        chatId: String,
        messages: List<MessageDto>,
        isOptimistic: Boolean = false
    ) {
        if (messages.isEmpty()) {
            return
        }

        dao.upsert(
            messages.map { message ->
                CachedChatMessageEntity(
                    ownerUserId = ownerUserId,
                    chatId = chatId,
                    messageId = message.id,
                    sentAt = message.sentAt,
                    payloadJson = json.encodeToString(message),
                    isOptimistic = isOptimistic
                )
            }
        )
    }

    suspend fun replaceOptimisticMessage(
        ownerUserId: String,
        chatId: String,
        localMessageId: String,
        serverMessage: MessageDto
    ) {
        database.withTransaction {
            dao.deleteMessage(ownerUserId, chatId, localMessageId)
            upsertMessages(ownerUserId, chatId, listOf(serverMessage), isOptimistic = false)
        }
    }

    suspend fun clearChat(ownerUserId: String, chatId: String) {
        dao.clearChat(ownerUserId, chatId)
    }

    private fun CachedChatMessageEntity.toMessageOrNull(): MessageDto? =
        runCatching { json.decodeFromString<MessageDto>(payloadJson) }.getOrNull()

    private companion object {
        const val LatestMessagesLimit = 100
    }
}
