import Foundation
import SwiftUI

@Observable
final class ChatViewModel {
    var messages: [Message] = []
    var isLoading = false
    var isSending = false
    var errorMessage: String?
    var searchQuery = ""
    var searchResults: [String] = []  // message IDs matching search
    var currentSearchIndex = 0

    private var notificationTask: Task<Void, Never>?

    func loadMessages(convId: String, chatClient: ChatClient) async {
        isLoading = true
        messages = await chatClient.getMessages(convId: convId, limit: 50)
        isLoading = false

        // Mark as read
        let unreadIds = messages.filter { !$0.isMine(currentUserId: await chatClient.userId ?? "") }.map(\.id)
        if !unreadIds.isEmpty {
            await chatClient.markRead(convId: convId, messageIds: unreadIds)
        }
    }

    func loadOlderMessages(convId: String, chatClient: ChatClient) async {
        let older = await chatClient.getMessages(convId: convId, limit: 50, offset: messages.count)
        messages.insert(contentsOf: older, at: 0)
    }

    func sendMessage(convId: String, text: String, members: [ConversationMember],
                     chatClient: ChatClient, replyTo: String? = nil) async {
        guard !text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return }

        isSending = true
        errorMessage = nil

        let (success, msg) = await chatClient.sendMessage(
            convId: convId, text: text, members: members, replyTo: replyTo
        )

        isSending = false

        if !success {
            errorMessage = msg
        } else {
            // Reload messages to get the sent message
            await loadMessages(convId: convId, chatClient: chatClient)
        }
    }

    func deleteMessage(messageId: String, convId: String, chatClient: ChatClient) async {
        let success = await chatClient.deleteMessage(messageId: messageId, convId: convId)
        if success {
            messages.removeAll { $0.id == messageId }
        }
    }

    func search(query: String) {
        searchQuery = query
        if query.isEmpty {
            searchResults = []
            currentSearchIndex = 0
            return
        }
        let lower = query.lowercased()
        searchResults = messages.filter { $0.text?.lowercased().contains(lower) == true }.map(\.id)
        currentSearchIndex = searchResults.isEmpty ? 0 : searchResults.count - 1
    }

    func nextSearchResult() {
        guard !searchResults.isEmpty else { return }
        currentSearchIndex = (currentSearchIndex + 1) % searchResults.count
    }

    func prevSearchResult() {
        guard !searchResults.isEmpty else { return }
        currentSearchIndex = (currentSearchIndex - 1 + searchResults.count) % searchResults.count
    }

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

    @MainActor
    private func handleNotification(_ notification: ChatNotification, convId: String, chatClient: ChatClient) {
        switch notification {
        case .newMessage(let data):
            if data["conversation_id"] as? String == convId {
                if let msg = Task.detached(priority: .userInitiated, operation: {
                    await chatClient.decryptNotification(data)
                }) as? Task<Message?, Never> {
                    Task {
                        if let message = await msg.value {
                            messages.append(message)
                            // Mark as read immediately since we're viewing this conv
                            await chatClient.markRead(convId: convId, messageIds: [message.id])
                        }
                    }
                }
            }
        case .messageDeleted(let data):
            if let msgId = data["message_id"] as? String {
                messages.removeAll { $0.id == msgId }
            }
        case .messagesRead(let data):
            if let readUserId = data["user_id"] as? String,
               let msgIds = data["message_ids"] as? [String] {
                for i in messages.indices {
                    if msgIds.contains(messages[i].id) {
                        messages[i].readBy.insert(readUserId)
                    }
                }
            }
        default:
            break
        }
    }

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