package com.example.godeye.managers

import com.example.godeye.utils.Logger
import kotlinx.coroutines.*
import okhttp3.*
import org.json.JSONObject
import java.util.concurrent.TimeUnit

/**
 * Менеджер WebSocket соединений для сигналинга WebRTC
 */
class WebSocketManager {

    companion object {
        private const val TAG = "WebSocketManager"
        private const val PING_INTERVAL = 30L // секунд
        private const val RECONNECT_INTERVAL = 5L // секунд
        private const val MAX_RECONNECT_ATTEMPTS = 5
    }

    private var webSocket: WebSocket? = null
    private var client: OkHttpClient? = null
    private var isConnected = false
    private var reconnectAttempts = 0
    private var shouldReconnect = true
    private var currentUrl: String? = null

    // Колбэки
    var onMessageReceived: ((String) -> Unit)? = null
    var onConnectionStateChanged: ((Boolean) -> Unit)? = null
    var onError: ((String) -> Unit)? = null

    // Корутины
    private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
    private var reconnectJob: Job? = null

    init {
        initializeClient()
    }

    /**
     * Инициализация HTTP клиента
     */
    private fun initializeClient() {
        client = OkHttpClient.Builder()
            .pingInterval(PING_INTERVAL, TimeUnit.SECONDS)
            .retryOnConnectionFailure(true)
            .build()
    }

    /**
     * Подключение к WebSocket серверу
     */
    fun connect(url: String) {
        try {
            Logger.step("WEBSOCKET_CONNECT", "Connecting to WebSocket: $url")

            currentUrl = url
            shouldReconnect = true
            reconnectAttempts = 0

            disconnect() // Отключаемся от предыдущего соединения

            val request = Request.Builder()
                .url(url)
                .build()

            webSocket = client?.newWebSocket(request, object : WebSocketListener() {
                override fun onOpen(webSocket: WebSocket, response: Response) {
                    Logger.step("WEBSOCKET_OPENED", "WebSocket connection opened")
                    isConnected = true
                    reconnectAttempts = 0

                    scope.launch(Dispatchers.Main) {
                        onConnectionStateChanged?.invoke(true)
                    }

                    // Отправляем приветственное сообщение
                    sendConnectionMessage()
                }

                override fun onMessage(webSocket: WebSocket, text: String) {
                    Logger.step("WEBSOCKET_MESSAGE", "Message received: ${text.take(100)}...")

                    scope.launch(Dispatchers.Main) {
                        onMessageReceived?.invoke(text)
                    }
                }

                override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
                    Logger.step("WEBSOCKET_CLOSING", "WebSocket closing: $code - $reason")
                    isConnected = false

                    scope.launch(Dispatchers.Main) {
                        onConnectionStateChanged?.invoke(false)
                    }
                }

                override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
                    Logger.step("WEBSOCKET_CLOSED", "WebSocket closed: $code - $reason")
                    isConnected = false

                    scope.launch(Dispatchers.Main) {
                        onConnectionStateChanged?.invoke(false)
                    }

                    // Попытка переподключения
                    if (shouldReconnect) {
                        scheduleReconnect()
                    }
                }

                override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
                    Logger.error("WEBSOCKET_ERROR", "WebSocket error: ${t.message}", t)
                    isConnected = false

                    scope.launch(Dispatchers.Main) {
                        onConnectionStateChanged?.invoke(false)
                        onError?.invoke("WebSocket ошибка: ${t.message}")
                    }

                    // Попытка переподключения
                    if (shouldReconnect) {
                        scheduleReconnect()
                    }
                }
            })

        } catch (e: Exception) {
            Logger.error("WEBSOCKET_CONNECT_ERROR", "Failed to connect to WebSocket", e)
            scope.launch(Dispatchers.Main) {
                onError?.invoke("Ошибка подключения: ${e.message}")
            }
        }
    }

    /**
     * Отправка сообщения
     */
    fun sendMessage(message: String): Boolean {
        return try {
            if (isConnected && webSocket != null) {
                val success = webSocket!!.send(message)
                if (success) {
                    Logger.step("WEBSOCKET_SEND", "Message sent: ${message.take(100)}...")
                } else {
                    Logger.error("WEBSOCKET_SEND_ERROR", "Failed to send message", null)
                }
                success
            } else {
                Logger.error("WEBSOCKET_NOT_CONNECTED", "WebSocket not connected", null)
                false
            }
        } catch (e: Exception) {
            Logger.error("WEBSOCKET_SEND_EXCEPTION", "Exception while sending message", e)
            false
        }
    }

    /**
     * Отправка JSON сообщения
     */
    fun sendJsonMessage(json: JSONObject): Boolean {
        return sendMessage(json.toString())
    }

    /**
     * Отправка приветственного сообщения
     */
    private fun sendConnectionMessage() {
        try {
            val connectionMessage = JSONObject().apply {
                put("type", "device_connected")
                put("deviceType", "android")
                put("timestamp", System.currentTimeMillis())
                put("capabilities", JSONObject().apply {
                    put("video", true)
                    put("audio", true)
                    put("webrtc", true)
                })
            }

            sendJsonMessage(connectionMessage)

        } catch (e: Exception) {
            Logger.error("WEBSOCKET_CONNECTION_MESSAGE_ERROR", "Failed to send connection message", e)
        }
    }

    /**
     * Планирование переподключения
     */
    private fun scheduleReconnect() {
        if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
            Logger.error("WEBSOCKET_MAX_RECONNECT", "Maximum reconnection attempts reached", null)
            scope.launch(Dispatchers.Main) {
                onError?.invoke("Превышено максимальное количество попыток переподключения")
            }
            return
        }

        reconnectJob?.cancel()
        reconnectJob = scope.launch {
            delay(RECONNECT_INTERVAL * 1000)

            if (shouldReconnect && !isConnected) {
                reconnectAttempts++
                Logger.step("WEBSOCKET_RECONNECT", "Reconnection attempt $reconnectAttempts/$MAX_RECONNECT_ATTEMPTS")

                currentUrl?.let { url ->
                    connect(url)
                }
            }
        }
    }

    /**
     * Отключение от WebSocket
     */
    fun disconnect() {
        try {
            Logger.step("WEBSOCKET_DISCONNECT", "Disconnecting WebSocket")

            shouldReconnect = false
            reconnectJob?.cancel()

            webSocket?.close(1000, "Client disconnect")
            webSocket = null
            isConnected = false

            scope.launch(Dispatchers.Main) {
                onConnectionStateChanged?.invoke(false)
            }

        } catch (e: Exception) {
            Logger.error("WEBSOCKET_DISCONNECT_ERROR", "Failed to disconnect WebSocket", e)
        }
    }

    /**
     * Проверка состояния соединения
     */
    fun isConnected(): Boolean {
        return isConnected
    }

    /**
     * Получение URL текущего соединения
     */
    fun getCurrentUrl(): String? {
        return currentUrl
    }

    /**
     * Освобождение ресурсов
     */
    fun dispose() {
        try {
            Logger.step("WEBSOCKET_DISPOSE", "Disposing WebSocket manager")

            disconnect()
            scope.cancel()

            Logger.step("WEBSOCKET_DISPOSED", "WebSocket manager disposed")

        } catch (e: Exception) {
            Logger.error("WEBSOCKET_DISPOSE_ERROR", "Failed to dispose WebSocket manager", e)
        }
    }
}
