package com.aiwazian.messenger.utils

import android.util.Log
import com.aiwazian.messenger.enums.WebSocketAction
import com.aiwazian.messenger.services.TokenManager
import io.socket.client.IO
import io.socket.client.Socket
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.jsonObject
import org.json.JSONObject
import java.net.URI

object WebSocketManager {

    private var socket: Socket? = null

    private val _isConnectedState = MutableStateFlow(false)
    val isConnectedState = _isConnectedState.asStateFlow()

    val json = Json {
        ignoreUnknownKeys = true
        prettyPrint = true
    }

    var onConnect: (() -> Unit)? = null
    var onClose: ((Int) -> Unit)? = null
    var onFailure: (() -> Unit)? = null

    private val messageHandlers = mutableMapOf<WebSocketAction, MutableList<(JsonObject) -> Unit>>()

    private val eventActions = mapOf(
        "message:new" to WebSocketAction.NEW_MESSAGE,
        "message:delete" to WebSocketAction.DELETE_MESSAGE,
        "chat:new" to WebSocketAction.NEW_CHAT,
        "chat:history_clear" to WebSocketAction.HISTORY_CLEAR,
        "kanban:update" to WebSocketAction.KANBAN_UPDATE
    )

    private fun registerMessageHandler(
        action: WebSocketAction,
        handler: (JsonObject) -> Unit
    ) {
        val handlersList = messageHandlers.getOrPut(action) { mutableListOf() }
        handlersList.add(handler)
    }

    private fun unregisterMessageHandler(
        action: WebSocketAction,
        handler: (JsonObject) -> Unit
    ) {
        val handlersList = messageHandlers.getOrPut(action) { mutableListOf() }
        handlersList.remove(handler)
    }

    internal inline fun <reified T> registerMessageHandler(
        action: WebSocketAction,
        crossinline handler: (T) -> Unit
    ) {
        registerMessageHandler(action) { webSocketData ->
            try {
                handler(json.decodeFromJsonElement<T>(webSocketData))
            } catch (e: Exception) {
                Log.e("WebSocketManager", "Ошибка десериализации для действия $action", e)
            }
        }
    }

    internal inline fun <reified T> unregisterMessageHandler(
        action: WebSocketAction,
        crossinline handler: (T) -> Unit
    ) {
        unregisterMessageHandler(action) { webSocketData ->
            try {
                handler(json.decodeFromJsonElement<T>(webSocketData))
            } catch (e: Exception) {
                Log.e("WebSocketManager", "Ошибка десериализации для действия $action", e)
            }
        }
    }

    fun connect() {
        if (TokenManager.getToken().isBlank()) {
            _isConnectedState.update { false }
            return
        }

        if (socket?.connected() == true) {
            return
        }

        socket?.close()
        socket = createSocket().also { it.connect() }
    }

    private fun createSocket(): Socket {
        val options = IO.Options.builder()
            .setAuth(mapOf("token" to TokenManager.getToken()))
            .setTransports(arrayOf("websocket"))
            .setReconnection(false)
            .build()

        val socketUrl = Constants.WEB_SOCKET_URL
            .replaceFirst("wss://", "https://")
            .replaceFirst("ws://", "http://")

        return IO.socket(URI.create(socketUrl), options).apply {
            on(Socket.EVENT_CONNECT) {
                _isConnectedState.update { true }
                onConnect?.invoke()
            }

            on(Socket.EVENT_DISCONNECT) {
                _isConnectedState.update { false }
                onClose?.invoke(1000)
            }

            on(Socket.EVENT_CONNECT_ERROR) { args ->
                _isConnectedState.update { false }
                Log.e("WebSocketManager", "Ошибка подключения Socket.IO: ${args.firstOrNull()}")
                onFailure?.invoke()
            }

            on("auth:error") {
                _isConnectedState.update { false }
                onClose?.invoke(1008)
            }

            eventActions.forEach { (event, action) ->
                on(event) { args ->
                    dispatch(action, args.firstOrNull())
                }
            }
        }
    }

    private fun dispatch(
        action: WebSocketAction,
        payload: Any?
    ) {
        try {
            val jsonObject = when (payload) {
                is JSONObject -> json.parseToJsonElement(payload.toString()).jsonObject
                is String -> json.parseToJsonElement(payload).jsonObject
                else -> return
            }

            messageHandlers[action]?.forEach { handler ->
                handler(jsonObject)
            }
        } catch (e: Exception) {
            Log.e("WebSocketManager", "Ошибка обработки события $action", e)
        }
    }

    fun close() {
        _isConnectedState.update { false }
        socket?.close()
        socket = null
    }
}
