package dev.meloda.fast.messageshistory

import android.util.Log
import dev.meloda.fast.common.VkConstants
import dev.meloda.fast.common.extensions.listenValue
import dev.meloda.fast.common.extensions.setValue
import dev.meloda.fast.common.paging.canPaginate as canPaginatePage
import dev.meloda.fast.common.paging.isPaginationExhausted as isPaginationExhaustedPage
import dev.meloda.fast.common.paging.loadingFlags
import dev.meloda.fast.common.paging.mergePage
import dev.meloda.fast.common.provider.ResourceProvider
import dev.meloda.fast.data.State
import dev.meloda.fast.data.VkUtils
import dev.meloda.fast.data.processState
import dev.meloda.fast.datastore.AppSettings
import dev.meloda.fast.domain.ConvoUseCase
import dev.meloda.fast.domain.MessagesUseCase
import dev.meloda.fast.domain.util.extractAvatar
import dev.meloda.fast.domain.util.extractTitle
import dev.meloda.fast.messageshistory.model.MessagesHistoryScreenState
import dev.meloda.fast.model.BaseError
import dev.meloda.fast.model.api.domain.VkMessage
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow

internal class MessagesHistoryLoaders(
    private val convoUseCase: ConvoUseCase,
    private val messagesUseCase: MessagesUseCase,
    private val resourceProvider: ResourceProvider,
    private val screenState: MutableStateFlow<MessagesHistoryScreenState>,
    private val messages: MutableStateFlow<List<VkMessage>>,
    private val imagesToPreload: MutableStateFlow<List<String>>,
    private val canPaginate: MutableStateFlow<Boolean>,
    private val baseError: MutableStateFlow<BaseError?>,
    private val scope: CoroutineScope,
    private val syncUiMessages: () -> Unit,
    private val onPinnedMessage: (VkMessage?) -> Unit,
) {
    fun loadConvo() {
        Log.d("MessagesHistoryViewModelImpl", "loadConvo()")

        convoUseCase.getById(
            peerIds = listOf(screenState.value.convoId),
            extended = true,
            fields = VkConstants.ALL_FIELDS
        ).listenValue(scope) { state ->
            state.processState(
                error = ::handleError,
                success = { response ->
                    val convo = response.firstOrNull() ?: return@listenValue
                    val title = convo.extractTitle(
                        useContactName = AppSettings.General.useContactNames,
                        resources = resourceProvider.resources
                    )
                    val avatar = convo.extractAvatar()

                    screenState.setValue { old ->
                        old.copy(
                            convo = convo,
                            title = title,
                            avatar = avatar
                        )
                    }

                    onPinnedMessage(convo.pinnedMessage)
                }
            )
        }
    }

    fun loadMessagesHistory(offset: Int) {
        Log.d("MessagesHistoryViewModel", "loadMessagesHistory: $offset")

        messagesUseCase.getMessagesHistory(
            convoId = screenState.value.convoId,
            count = MessagesHistoryViewModelImpl.MESSAGES_LOAD_COUNT,
            offset = offset,
        ).listenValue(scope) { state ->
            state.processState(
                error = ::handleError,
                success = { response ->
                    val messages = response.messages
                    val fullMessages = mergePage(this.messages.value, messages, offset)
                        .sorted()

                    imagesToPreload.setValue {
                        messages.mapNotNull { it.extractAvatar().extractUrl() }
                    }

                    messagesUseCase.storeMessages(messages)
                    convoUseCase.storeConvos(response.convos)

                    val itemsCountSufficient = canPaginatePage(
                        MessagesHistoryViewModelImpl.MESSAGES_LOAD_COUNT,
                        messages.size
                    )
                    val paginationExhausted = isPaginationExhaustedPage(
                        pageSize = MessagesHistoryViewModelImpl.MESSAGES_LOAD_COUNT,
                        loadedCount = messages.size,
                        hasExistingItems = this.messages.value.isNotEmpty()
                    )
                    screenState.setValue { old ->
                        old.copy(isPaginationExhausted = paginationExhausted)
                    }

                    this.messages.emit(fullMessages)
                    syncUiMessages()
                    canPaginate.setValue { itemsCountSufficient }
                }
            )

            val flags = loadingFlags(offset, state.isLoading())
            screenState.setValue { old ->
                old.copy(
                    isLoading = flags.isLoading,
                    isPaginating = flags.isPaginating
                )
            }
        }
    }

    private fun handleError(error: State.Error) {
        VkUtils.parseError(error)?.let { newBaseError ->
            baseError.setValue { newBaseError }
        }
    }

    private fun List<VkMessage>.sorted(): List<VkMessage> {
        return sortedWith { m1, m2 ->
            val dateDiff = m2.date - m1.date
            if (dateDiff != 0) {
                dateDiff
            } else {
                val idDiff = m2.id - m1.id
                idDiff.toInt()
            }
        }
    }
}
