import Foundation
import SwiftUI

@Observable
final class ConversationListVM {
    var conversations: [Conversation] = []
    var invitations: [Invitation] = []
    var onlineUsers: Set<String> = []
    var unreadCounts: [String: Int] = [:]
    var favorites: Set<String> = []
    var isLoading = false

    private var notificationTask: Task<Void, Never>?

    func load(chatClient: ChatClient, email: String) async {
        isLoading = true

        // Load favorites from disk
        favorites = KeyStorage.loadFavorites(email: email)

        // Fetch conversations
        let convs = await chatClient.listConversations()
        conversations = sortConversations(convs, currentUserId: await chatClient.userId ?? "")

        // Populate unread counts from server
        for conv in conversations where conv.unreadCount > 0 {
            unreadCounts[conv.id] = conv.unreadCount
        }

        // Fetch invitations
        invitations = await chatClient.listInvitations()

        isLoading = false

        // Start notification listener
        startNotificationListener(chatClient: chatClient, email: email)
    }

    func refresh(chatClient: ChatClient) async {
        let convs = await chatClient.listConversations()
        conversations = sortConversations(convs, currentUserId: await chatClient.userId ?? "")
        invitations = await chatClient.listInvitations()
    }

    func toggleFavorite(convId: String, email: String) {
        if favorites.contains(convId) {
            favorites.remove(convId)
        } else {
            favorites.insert(convId)
        }
        try? KeyStorage.saveFavorites(email: email, favorites: favorites)

        // Re-sort
        let userId = conversations.first?.createdBy ?? ""
        conversations = sortConversations(conversations, currentUserId: userId)
    }

    func markConversationRead(convId: String) {
        unreadCounts[convId] = 0
    }

    func incrementUnread(convId: String) {
        unreadCounts[convId, default: 0] += 1
    }

    private func sortConversations(_ convs: [Conversation], currentUserId: String) -> [Conversation] {
        var result = convs.map { conv -> Conversation in
            var c = conv
            c.isFavorite = favorites.contains(conv.id)
            c.unreadCount = unreadCounts[conv.id] ?? conv.unreadCount
            return c
        }

        result.sort { a, b in
            // Favorites first
            if a.isFavorite != b.isFavorite { return a.isFavorite }
            // Online DMs next
            let aOnline = a.dmPartnerId(currentUserId: currentUserId).map { onlineUsers.contains($0) } ?? false
            let bOnline = b.dmPartnerId(currentUserId: currentUserId).map { onlineUsers.contains($0) } ?? false
            if aOnline != bOnline { return aOnline }
            // Alphabetical
            return a.displayName(currentUserId: currentUserId).lowercased() < b.displayName(currentUserId: currentUserId).lowercased()
        }

        return result
    }

    private func startNotificationListener(chatClient: ChatClient, email: String) {
        notificationTask?.cancel()
        notificationTask = Task {
            for await notification in await chatClient.notifications {
                await handleNotification(notification, chatClient: chatClient, email: email)
            }
        }
    }

    @MainActor
    private func handleNotification(_ notification: ChatNotification, chatClient: ChatClient, email: String) {
        switch notification {
        case .newMessage(let data):
            if let convId = data["conversation_id"] as? String {
                incrementUnread(convId: convId)
            }
        case .onlineUsers(let userIds):
            onlineUsers = Set(userIds)
        case .userOnline(let userId):
            onlineUsers.insert(userId)
        case .userOffline(let userId):
            onlineUsers.remove(userId)
        case .conversationCreated, .memberAdded, .memberRemoved, .conversationRenamed:
            Task { await refresh(chatClient: chatClient) }
        case .groupInvitation:
            Task { invitations = await chatClient.listInvitations() }
        case .connectionStateChanged(let connected):
            if !connected {
                // Could trigger auto-reconnect here
            }
        default:
            break
        }
    }

    func stop() {
        notificationTask?.cancel()
        notificationTask = nil
    }
}
