import Foundation

@MainActor
final class PrivateChatViewModel: ObservableObject {
    @Published private(set) var messages: [MessageItem] = []
    @Published private(set) var isInitialLoading: Bool = false
    @Published private(set) var isLoadingMore: Bool = false
    @Published var errorMessage: String?
    @Published private(set) var isSending: Bool = false

    private let chatService: ChatService
    private let chatId: String
    private let currentUserId: String?
    private let pageSize: Int
    private let maxMessageLength: Int = 4096
    private var hasMore: Bool = true
    private var didLoadInitially: Bool = false
    private var messageObserver: NSObjectProtocol?

    init(chatId: String, currentUserId: String?, chatService: ChatService = ChatService(), pageSize: Int = 30) {
        self.chatId = chatId
        self.currentUserId = currentUserId
        self.chatService = chatService
        self.pageSize = pageSize

        messageObserver = NotificationCenter.default.addObserver(
            forName: .socketDidReceivePrivateMessage,
            object: nil,
            queue: .main
        ) { [weak self] notification in
            guard
                let self,
                let message = notification.object as? MessageItem,
                message.chatId == self.chatId
            else { return }
            self.messages = Self.merge(existing: self.messages, newMessages: [message])
        }
    }

    deinit {
        if let observer = messageObserver {
            NotificationCenter.default.removeObserver(observer)
        }
    }

    func loadInitialHistory(force: Bool = false) {
        if !force && didLoadInitially { return }
        guard !isInitialLoading else { return }

        isInitialLoading = true
        errorMessage = nil
        hasMore = true

        chatService.fetchPrivateChatHistory(chatId: chatId, beforeMessageId: nil, limit: pageSize) { [weak self] result in
            guard let self else { return }

            switch result {
            case .success(let data):
                self.hasMore = data.hasMore
                self.messages = Self.merge(existing: [], newMessages: data.items)
                self.didLoadInitially = true
            case .failure(let error):
                self.errorMessage = self.message(for: error)
            }

            self.isInitialLoading = false
        }
    }

    func sendMessage(text: String, completion: @escaping (Bool) -> Void) {
        let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !trimmed.isEmpty else {
            completion(false)
            return
        }
        guard trimmed.count <= maxMessageLength else {
            errorMessage = NSLocalizedString("Сообщение слишком длинное.", comment: "")
            completion(false)
            return
        }
        guard !isSending else {
            completion(false)
            return
        }
        guard let currentUserId else {
            completion(false)
            return
        }

        isSending = true

        chatService.sendPrivateMessage(chatId: chatId, content: trimmed) { [weak self] result in
            guard let self else { return }

            switch result {
            case .success(let data):
                let newMessage = MessageItem(
                    messageId: data.messageId,
                    messageType: "text",
                    chatId: data.chatId,
                    senderId: currentUserId,
                    senderData: nil,
                    content: trimmed,
                    mediaLink: nil,
                    isViewed: true,
                    createdAt: data.createdAt,
                    updatedAt: data.createdAt,
                    forwardMetadata: nil
                )

                self.messages = Self.merge(existing: self.messages, newMessages: [newMessage])
                self.errorMessage = nil
                completion(true)
            case .failure(let error):
                self.errorMessage = self.message(for: error)
                completion(false)
            }

            self.isSending = false
        }
    }

    func refresh() {
        didLoadInitially = false
        loadInitialHistory(force: true)
    }

    func loadMoreIfNeeded(for message: MessageItem) {
        guard didLoadInitially, !isInitialLoading, hasMore, !isLoadingMore else { return }
        guard let first = messages.first, first.id == message.id else { return }

        isLoadingMore = true

        chatService.fetchPrivateChatHistory(chatId: chatId, beforeMessageId: message.id, limit: pageSize) { [weak self] result in
            guard let self else { return }

            switch result {
            case .success(let data):
                self.hasMore = data.hasMore
                if !data.items.isEmpty {
                    self.messages = Self.merge(existing: self.messages, newMessages: data.items)
                }
            case .failure(let error):
                self.errorMessage = self.message(for: error)
            }

            self.isLoadingMore = false
        }
    }

    private func message(for error: Error) -> String {
        if let chatError = error as? ChatServiceError {
            return chatError.errorDescription ?? NSLocalizedString("Не удалось загрузить историю чата.", comment: "")
        }

        if let networkError = error as? NetworkError {
            switch networkError {
            case .unauthorized:
                return NSLocalizedString("Сессия истекла. Войдите снова.", comment: "")
            case .invalidURL, .noResponse:
                return NSLocalizedString("Ошибка соединения с сервером.", comment: "")
            case .network(let underlying):
                return String(format: NSLocalizedString("Ошибка сети: %@", comment: ""), underlying.localizedDescription)
            case .server(let statusCode, _):
                return String(format: NSLocalizedString("Ошибка сервера (%@).", comment: ""), "\(statusCode)")
            }
        }

        return NSLocalizedString("Неизвестная ошибка. Попробуйте позже.", comment: "")
    }

    private static func merge(existing: [MessageItem], newMessages: [MessageItem]) -> [MessageItem] {
        var combined: [MessageItem] = []
        combined.reserveCapacity(existing.count + newMessages.count)

        var seen: Set<String> = []

        for message in newMessages {
            if seen.insert(message.id).inserted {
                combined.append(message)
            }
        }

        for message in existing {
            if seen.insert(message.id).inserted {
                combined.append(message)
            }
        }

        combined.sort(by: compare)
        return combined
    }

    private static func compare(lhs: MessageItem, rhs: MessageItem) -> Bool {
        if let lhsDate = lhs.createdAt, let rhsDate = rhs.createdAt, lhsDate != rhsDate {
            return lhsDate < rhsDate
        }

        if let lhsId = Int(lhs.messageId), let rhsId = Int(rhs.messageId), lhsId != rhsId {
            return lhsId < rhsId
        }

        return lhs.messageId < rhs.messageId
    }
}
