package com.seven.ikar.kotlin.feature.mainmenu

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.seven.ikar.kotlin.app.AppContainer
import com.seven.ikar.kotlin.core.model.ChatSummaryDto
import com.seven.ikar.kotlin.core.push.NotificationSettingsSnapshot
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch

data class MainMenuUiState(
    val unreadChatsCount: Int = 0
)

class MainMenuViewModel(private val container: AppContainer) : ViewModel() {
    private val _uiState = MutableStateFlow(MainMenuUiState())
    val uiState: StateFlow<MainMenuUiState> = _uiState.asStateFlow()

    private var debounceJob: Job? = null
    private var refreshJob: Job? = null
    private var latestChats: List<ChatSummaryDto> = emptyList()

    init {
        attachNotificationSettings()
        attachRealtime()
        loadCachedUnreadChatsCount()
        refresh()
    }

    fun refresh() {
        val currentSession = container.sessionStore.read()
        if (currentSession == null) {
            clear()
            return
        }

        refreshJob?.cancel()
        refreshJob = viewModelScope.launch {
            runCatching {
                container.executeAuthorized { session ->
                    val chats = container.apiClient.getChats(session.accessToken)
                    session.user.id to chats
                }
            }.onSuccess { (ownerUserId, chats) ->
                if (container.sessionStore.read()?.user?.id != ownerUserId) {
                    return@onSuccess
                }
                latestChats = chats
                updateUnreadChatsCount(chats)
                cacheChatList(ownerUserId, chats)
            }.onFailure {
                loadCachedUnreadChatsCount()
            }
        }
    }

    fun clear() {
        debounceJob?.cancel()
        refreshJob?.cancel()
        latestChats = emptyList()
        _uiState.value = MainMenuUiState()
    }

    private fun loadCachedUnreadChatsCount() {
        val session = container.sessionStore.read() ?: return
        val ownerUserId = session.user.id
        viewModelScope.launch(Dispatchers.IO) {
            val chats = runCatching {
                container.chatListCache.loadChats(ownerUserId)
            }.getOrDefault(emptyList())
            if (container.sessionStore.read()?.user?.id == ownerUserId) {
                latestChats = chats
                updateUnreadChatsCount(chats)
            }
        }
    }

    private fun cacheChatList(ownerUserId: String, chats: List<ChatSummaryDto>) {
        viewModelScope.launch(Dispatchers.IO) {
            runCatching { container.chatListCache.replaceChats(ownerUserId, chats) }
        }
    }

    private fun attachRealtime() {
        viewModelScope.launch {
            container.realtimeService.chatCreated.collect { scheduleRefresh() }
        }
        viewModelScope.launch {
            container.realtimeService.chatsChanged.collect { scheduleRefresh() }
        }
        viewModelScope.launch {
            container.realtimeService.messagesCreated.collect { scheduleRefresh() }
        }
        viewModelScope.launch {
            container.realtimeService.messagesUpdated.collect { scheduleRefresh() }
        }
    }

    private fun attachNotificationSettings() {
        viewModelScope.launch {
            container.pushSettings.snapshot.collect {
                updateUnreadChatsCount(latestChats)
            }
        }
    }

    private fun scheduleRefresh() {
        debounceJob?.cancel()
        debounceJob = viewModelScope.launch {
            delay(400)
            refresh()
        }
    }

    private fun updateUnreadChatsCount(chats: List<ChatSummaryDto>) {
        _uiState.value = MainMenuUiState(unreadChatsCount = countUnreadChats(chats))
    }

    private fun countUnreadChats(chats: List<ChatSummaryDto>): Int =
        countUnreadChats(chats, container.pushSettings.snapshot.value)

    private fun countUnreadChats(
        chats: List<ChatSummaryDto>,
        settings: NotificationSettingsSnapshot
    ): Int {
        if (!settings.showBadge) {
            return 0
        }

        val eligibleChats = if (settings.includeMutedChatsInBadge) {
            chats
        } else {
            chats.filterNot { it.isMuted }
        }

        return if (settings.countMessagesInBadge) {
            eligibleChats.sumOf { it.unreadCount.coerceAtLeast(0) }
        } else {
            eligibleChats.count { it.unreadCount > 0 }
        }
    }
}
