package dev.meloda.fast.chatmaterials

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dev.meloda.fast.chatmaterials.model.ChatMaterialsScreenState
import dev.meloda.fast.chatmaterials.model.MaterialType
import dev.meloda.fast.chatmaterials.navigation.ChatMaterials
import dev.meloda.fast.chatmaterials.util.asPresentation
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.data.State
import dev.meloda.fast.data.VkUtils
import dev.meloda.fast.data.processState
import dev.meloda.fast.domain.MessagesUseCase
import dev.meloda.fast.model.BaseError
import dev.meloda.fast.model.api.domain.VkAttachmentHistoryMessage
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

interface ChatMaterialsViewModel {
    val screenState: StateFlow<ChatMaterialsScreenState>
    val baseError: StateFlow<BaseError?>
    val imagesToPreload: StateFlow<List<String>>
    val currentOffset: StateFlow<Int>
    val canPaginate: StateFlow<Boolean>

    fun onPaginationConditionsMet()

    fun onRefresh()

    fun onErrorConsumed()

    fun onTypeChanged(newType: String)
}

class ChatMaterialsViewModelImpl(
    private val materialType: MaterialType,
    private val messagesUseCase: MessagesUseCase,
    savedStateHandle: SavedStateHandle
) : ViewModel(), ChatMaterialsViewModel {

    override val screenState = MutableStateFlow(ChatMaterialsScreenState.EMPTY)

    override val baseError = MutableStateFlow<BaseError?>(null)
    override val imagesToPreload = MutableStateFlow<List<String>>(emptyList())
    override val currentOffset = MutableStateFlow(0)
    override val canPaginate = MutableStateFlow(false)

    init {
        val arguments = ChatMaterials.from(savedStateHandle)

        screenState.setValue { old ->
            old.copy(
                peerId = arguments.peerId,
                cmId = arguments.cmId
            )
        }

        loadChatMaterials()
    }

    override fun onPaginationConditionsMet() {
        currentOffset.setValue { old -> old + LOAD_COUNT }
        loadChatMaterials()
    }

    override fun onRefresh() {
        loadChatMaterials(offset = 0)
    }

    override fun onErrorConsumed() {
        baseError.setValue { null }
    }

    override fun onTypeChanged(newType: String) {
        screenState.setValue { old -> old.copy(attachmentType = newType) }
        loadChatMaterials(0)
    }

    private fun loadChatMaterials(offset: Int = currentOffset.value) {
        messagesUseCase.getHistoryAttachments(
            peerId = screenState.value.peerId,
            count = LOAD_COUNT,
            offset = offset,
            attachmentTypes = listOf(materialType.toString()),
            cmId = screenState.value.cmId
        ).listenValue(viewModelScope) { state ->
            state.processState(
                error = ::handleError,
                success = { response ->
                    val itemsCountSufficient = canPaginatePage(LOAD_COUNT, response.size)
                    canPaginate.setValue { itemsCountSufficient }

                    val paginationExhausted = isPaginationExhaustedPage(
                        pageSize = LOAD_COUNT,
                        loadedCount = response.size,
                        hasExistingItems = screenState.value.materials.isNotEmpty()
                    )

                    val loadedMaterials = response.mapNotNull(VkAttachmentHistoryMessage::asPresentation)

                    val newState = screenState.value.copy(
                        isPaginationExhausted = paginationExhausted,
                        cmId = if (loadedMaterials.size + offset > 200) {
                            currentOffset.setValue { 0 }
                            loadedMaterials.lastOrNull()?.cmId ?: -1
                        } else {
                            screenState.value.cmId
                        }
                    )

                    screenState.setValue {
                        newState.copy(
                            materials = mergePage(newState.materials, loadedMaterials, offset)
                        )
                    }
                }
            )

            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 }
        }
    }

    companion object {
        const val LOAD_COUNT = 100
    }
}
