package net.buzzert.kordophonedroid.ui.messagelist

import android.content.Context
import android.net.Uri
import android.webkit.MimeTypeMap
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import net.buzzert.kordophone.backend.model.Conversation
import net.buzzert.kordophone.backend.model.GUID
import net.buzzert.kordophone.backend.model.Message
import net.buzzert.kordophone.backend.model.OutgoingMessage
import net.buzzert.kordophone.backend.model.UploadingAttachmentMetadata
import net.buzzert.kordophone.backend.server.ChatRepository
import net.buzzert.kordophonedroid.ui.attachments.AttachmentImageLoader
import javax.inject.Inject

const val MVM_LOG: String = "MessageListViewModel"

@HiltViewModel
class MessageListViewModel @Inject constructor(
    private val repository: ChatRepository,
    private val imageLoader: AttachmentImageLoader
) : ViewModel()
{
    var conversationGUID: GUID? = null
        set(value) {
            field = value
            value?.let {
                conversation = repository.conversationForGuid(it)
            }
        }

    private var conversation: Conversation? = null
    private val pendingMessages: MutableStateFlow<List<OutgoingMessage>> = MutableStateFlow(listOf())

    init {
        // TODO: Need to handle settings changes here!!

        viewModelScope.launch {
            // Remove pending message after message is delivered.
            // By now, the repository should've committed this to the store.
            repository.messageDeliveredChannel.collectLatest { event ->
                pendingMessages.value =
                    pendingMessages.value.filter { it.guid != event.requestGuid }
            }
        }
    }

    val messages: Flow<List<Message>>
        get() = repository.messagesChanged(conversation!!)
            .combine(pendingMessages) { a, b -> a.union(b.map { it.asMessage() }) }
            .map { messages ->
                messages
                    .sortedBy { it.date }
                    .reversed()
            }

    val title: String get() = conversation!!.formattedDisplayName()

    val isGroupChat: Boolean get() = conversation!!.isGroupChat

    fun enqueueOutgoingMessage(
        text: String,
        attachmentUris: Set<Uri>,
        context: Context,
    ) {
        val outgoingMessage = OutgoingMessage(
            body = text,
            conversation = conversation!!,
            attachmentUris = attachmentUris,
            attachmentDataSource = { uri ->
                val resolver = context.contentResolver
                val inputStream = resolver.openInputStream(uri)
                val mimeType = resolver.getType(uri)

                val extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType) ?: "jpg"
                val filename = uri.lastPathSegment + ".$extension"

                if (inputStream != null && mimeType != null) {
                    UploadingAttachmentMetadata(
                        inputStream = inputStream,
                        mimeType = mimeType,
                        filename = filename
                    )
                } else {
                    null
                }
            }
        )

        val outgoingGUID = repository.enqueueOutgoingMessage(outgoingMessage)
        pendingMessages.value = pendingMessages.value + listOf(outgoingMessage)
    }

    fun isPendingMessage(message: Message): Boolean {
        return pendingMessages.value.any { it.guid == message.guid }
    }

    fun markAsRead() = viewModelScope.launch {
        repository.markConversationAsRead(conversation!!)
    }

    fun synchronize() = viewModelScope.launch {
        repository.synchronizeConversation(conversation!!, limit = 100)
    }
}

