package de.krisenvorrat.server.websocket

import de.krisenvorrat.shared.model.MessageDto
import io.ktor.websocket.*
import kotlinx.coroutines.channels.ClosedSendChannelException
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArraySet

internal class WebSocketManager {

    private val sessions = ConcurrentHashMap<String, CopyOnWriteArraySet<WebSocketSession>>()

    fun addSession(userId: String, session: WebSocketSession) {
        sessions.getOrPut(userId) { CopyOnWriteArraySet() }.add(session)
    }

    fun removeSession(userId: String, session: WebSocketSession) {
        sessions[userId]?.remove(session)
    }

    fun isOnline(userId: String): Boolean = sessions[userId]?.isNotEmpty() == true

    suspend fun notifyInventoryUpdated(userId: String, itemId: String) {
        val payload = buildJsonObject {
            put("type", "inventoryUpdated")
            put("itemId", itemId)
            put("timestamp", System.currentTimeMillis())
        }
        broadcast(userId, Json.encodeToString(payload))
    }

    suspend fun notifyFullSyncRequired(userId: String) {
        val payload = buildJsonObject {
            put("type", "fullSyncRequired")
            put("timestamp", System.currentTimeMillis())
        }
        broadcast(userId, Json.encodeToString(payload))
    }

    suspend fun notifyNewMessage(receiverId: String, message: MessageDto) {
        val payload = buildJsonObject {
            put("type", "new_message")
            put("id", message.id)
            put("senderId", message.senderId)
            put("senderUsername", message.senderUsername)
            put("receiverId", message.receiverId)
            put("body", message.body)
            put("sentAt", message.sentAt)
        }
        broadcast(receiverId, Json.encodeToString(payload))
    }

    private suspend fun broadcast(userId: String, message: String) {
        val userSessions = sessions[userId] ?: return
        val toRemove = mutableListOf<WebSocketSession>()
        for (session in userSessions) {
            try {
                session.send(Frame.Text(message))
            } catch (_: ClosedSendChannelException) {
                toRemove.add(session)
            } catch (_: Exception) {
                toRemove.add(session)
            }
        }
        toRemove.forEach { userSessions.remove(it) }
    }
}
