package dev.meloda.fast.domain

import android.util.Log
import dev.meloda.fast.model.LongPollEvent
import dev.meloda.fast.model.LongPollParsedEvent
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlin.coroutines.CoroutineContext

class LongPollUpdatesParser(
    convoUseCase: ConvoUseCase,
    messagesUseCase: MessagesUseCase
) {
    private val job = SupervisorJob()

    private val exceptionHandler =
        CoroutineExceptionHandler { _, throwable ->
            Log.e("LongPollUpdatesParser", "error: $throwable")
            throwable.printStackTrace()
        }

    private val coroutineContext: CoroutineContext
        get() = Dispatchers.Default + job + exceptionHandler

    private val coroutineScope = CoroutineScope(coroutineContext)
    private val eventDispatcher = LongPollEventDispatcher()
    private val eventParser = LongPollEventParser(
        coroutineScope = coroutineScope,
        convoUseCase = convoUseCase,
        messagesUseCase = messagesUseCase,
        dispatch = eventDispatcher::dispatch,
        dispatchAll = eventDispatcher::dispatchAll
    )

    fun parseNextUpdate(event: List<Any>) {
        eventParser.parseNextUpdate(event)
    }

    fun onMessageSetFlags(block: (LongPollParsedEvent) -> Unit) {
        eventDispatcher.registerListener(LongPollEvent.MESSAGE_SET_FLAGS, assembleEventCallback(block))
    }

    fun onMessageMarkedAsImportant(block: (LongPollParsedEvent.MessageMarkedAsImportant) -> Unit) {
        eventDispatcher.registerListener(LongPollEvent.MARKED_AS_IMPORTANT, assembleEventCallback(block))
    }

    fun onMessageMarkedAsSpam(block: (LongPollParsedEvent.MessageMarkedAsSpam) -> Unit) {
        eventDispatcher.registerListener(LongPollEvent.MARKED_AS_SPAM, assembleEventCallback(block))
    }

    fun onMessageDeleted(block: (LongPollParsedEvent.MessageDeleted) -> Unit) {
        eventDispatcher.registerListener(LongPollEvent.MESSAGE_DELETED, assembleEventCallback(block))
    }

    fun onMessageClearFlags(block: (LongPollParsedEvent) -> Unit) {
        eventDispatcher.registerListener(LongPollEvent.MESSAGE_CLEAR_FLAGS, assembleEventCallback(block))
    }

    fun onMessageMarkedAsNotSpam(block: (LongPollParsedEvent.MessageMarkedAsNotSpam) -> Unit) {
        eventDispatcher.registerListener(LongPollEvent.MARKED_AS_NOT_SPAM, assembleEventCallback(block))
    }

    fun onMessageRestored(block: (LongPollParsedEvent.MessageRestored) -> Unit) {
        eventDispatcher.registerListener(LongPollEvent.MESSAGE_RESTORED, assembleEventCallback(block))
    }

    fun onNewMessage(block: (LongPollParsedEvent.NewMessage) -> Unit) {
        eventDispatcher.registerListener(LongPollEvent.MESSAGE_NEW, assembleEventCallback(block))
    }

    fun onMessageEdited(block: (LongPollParsedEvent.MessageEdited) -> Unit) {
        eventDispatcher.registerListener(LongPollEvent.MESSAGE_EDITED, assembleEventCallback(block))
    }

    fun onMessageIncomingRead(block: (LongPollParsedEvent.IncomingMessageRead) -> Unit) {
        eventDispatcher.registerListener(LongPollEvent.INCOMING_MESSAGE_READ, assembleEventCallback(block))
    }

    fun onMessageOutgoingRead(block: (LongPollParsedEvent.OutgoingMessageRead) -> Unit) {
        eventDispatcher.registerListener(LongPollEvent.OUTGOING_MESSAGE_READ, assembleEventCallback(block))
    }

    fun onChatCleared(block: (LongPollParsedEvent.ChatCleared) -> Unit) {
        eventDispatcher.registerListener(LongPollEvent.CHAT_CLEARED, assembleEventCallback(block))
    }

    fun onChatMajorChanged(block: (LongPollParsedEvent.ChatMajorChanged) -> Unit) {
        eventDispatcher.registerListener(LongPollEvent.CHAT_MAJOR_CHANGED, assembleEventCallback(block))
    }

    fun onChatMinorChanged(block: (LongPollParsedEvent.ChatMinorChanged) -> Unit) {
        eventDispatcher.registerListener(LongPollEvent.CHAT_MINOR_CHANGED, assembleEventCallback(block))
    }

    fun onChatArchived(block: (LongPollParsedEvent.ChatArchived) -> Unit) {
        eventDispatcher.registerListener(LongPollEvent.CHAT_ARCHIVED, assembleEventCallback(block))
    }

    fun onInteractions(block: (LongPollParsedEvent.Interaction) -> Unit) {
        eventDispatcher.registerListeners(
            eventTypes = listOf(
                LongPollEvent.TYPING,
                LongPollEvent.AUDIO_MESSAGE_RECORDING,
                LongPollEvent.PHOTO_UPLOADING,
                LongPollEvent.VIDEO_UPLOADING,
                LongPollEvent.FILE_UPLOADING
            ),
            listener = assembleEventCallback(block)
        )
    }
}

internal inline fun <R : LongPollParsedEvent> assembleEventCallback(
    crossinline block: (R) -> Unit,
): VkEventCallback<R> {
    return VkEventCallback { event -> block.invoke(event) }
}

fun interface VkEventCallback<in T : LongPollParsedEvent> {
    fun onEvent(event: T)
}
