package chats.data.repository import chats.data.remote.api.ChatApi import chats.data.remote.api.SendMessageRequest import chats.data.remote.dto.ChatDto import chats.data.remote.dto.MessageDto import chats.data.remote.dto.MediaItemDto import chats.data.remote.dto.ReactionDto import chats.domain.model.Chat import chats.domain.model.Message import chats.domain.model.MediaType import chats.domain.repository.ChatRepository import core.network.ServerConfig import chats.data.remote.signalr.ReadMessagesRequest import core.security.TokenManager import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody import okhttp3.RequestBody.Companion.asRequestBody import javax.inject.Inject import kotlinx.coroutines.flow.map class ChatRepositoryImpl @Inject constructor( private val api: ChatApi, private val tokenManager: TokenManager, private val serverConfig: ServerConfig, private val hubClient: chats.data.remote.signalr.ChatHubClient ) : ChatRepository { private val gson = com.google.gson.Gson() override suspend fun getChats(): List { val currentUserId = tokenManager.getUserId() ?: "" val baseUrl = serverConfig.getBaseUrl().removeSuffix("/api/") return api.getChats().map { it.toDomain(currentUserId, baseUrl) } } override fun getMessagesFlow(chatId: String): kotlinx.coroutines.flow.Flow> { // Кэш отключён - всегда возвращаем пустой поток return kotlinx.coroutines.flow.flowOf(emptyList()) } override suspend fun getMessages(chatId: String, cursor: String?, pivot: Long?, limit: Int?): List { val baseUrl = serverConfig.getBaseUrl().removeSuffix("/api/") val currentUserId = tokenManager.getUserId() ?: "" return try { android.util.Log.d("ChatRepo", "FETCH: chatId=$chatId, cursor=$cursor, limit=$limit") val messages = api.getMessages(chatId, cursor = cursor, limit = limit) if (messages.isNotEmpty()) { android.util.Log.d("ChatRepo", "Received ${messages.size} messages. TopSeq: ${messages.first().sequenceId}, BottomSeq: ${messages.last().sequenceId}") } // Мапим в доменные модели. По умолчанию считаем прочитанными, // так как unreadCount нам тут не критичен для истории. messages.map { msg -> msg.toDomain(currentUserId, baseUrl).copy(isRead = true) } } catch (e: Exception) { android.util.Log.e("ChatRepo", "Fetch messages failed", e) emptyList() } } override suspend fun sendMessage( chatId: String, content: String?, type: String, attachments: List?, replyToId: String?, forwardedFromId: String? ): Message { val request = SendMessageRequest( content = content, type = type, attachments = attachments, replyToId = replyToId, forwardedFromId = forwardedFromId ) android.util.Log.d("ChatRepoImpl", "sendMessage request: $request") val baseUrl = serverConfig.getBaseUrl().removeSuffix("/api/") val userId = tokenManager.getUserId() ?: "" return try { val messageId = api.sendMessage(chatId, request) android.util.Log.d("ChatRepoImpl", "sendMessage response: $messageId") // Поскольку сервер вернул только ID, создаем заглушку Message. // Настоящее сообщение придет через SignalR. Message( id = messageId, chatId = chatId, senderId = userId, senderName = "", // Будет обновлено через SignalR content = content, sequenceId = 0, createdAt = java.time.ZonedDateTime.now().toString(), media = attachments?.map { chats.domain.model.Media( id = java.util.UUID.randomUUID().toString(), type = it.type, url = it.url, filename = it.fileName, size = it.fileSize ) } ?: emptyList(), mediaType = when(type) { "image" -> chats.domain.model.MediaType.IMAGE "video" -> chats.domain.model.MediaType.VIDEO "audio", "voice" -> chats.domain.model.MediaType.AUDIO else -> chats.domain.model.MediaType.TEXT } ) } catch (e: Exception) { android.util.Log.e("ChatRepoImpl", "sendMessage error", e) throw e } } override suspend fun addReaction(messageId: String, emoji: String) { api.addReaction(messageId, emoji) } override suspend fun sendTypingStatus(chatId: String) { api.sendTypingStatus(chatId) } override suspend fun markMessagesAsRead(chatId: String, lastMessageId: String, lastReadSequenceId: Int) { try { android.util.Log.d("ChatRepoImpl", "markMessagesAsRead CALLED FOR $chatId") hubClient.readMessages(ReadMessagesRequest(chatId, lastMessageId, lastReadSequenceId)) } catch (e: Exception) { android.util.Log.e("ChatRepo", "Error marking messages as read", e) } } override suspend fun saveMessage(message: Message) { // Кэш отключён } override suspend fun deleteLocalMessage(messageId: String) { // Локальное удаление не поддерживается без кэша } override suspend fun uploadMedia(file: java.io.File): String { val mimeType = when (file.extension.lowercase()) { "jpg", "jpeg" -> "image/jpeg" "png" -> "image/png" "webp" -> "image/webp" "mp4" -> "video/mp4" "mp3", "m4a", "wav" -> "audio/mpeg" else -> "application/octet-stream" } val requestFile = file.asRequestBody(mimeType.toMediaTypeOrNull()) val body = MultipartBody.Part.createFormData("file", file.name, requestFile) return api.uploadFile(body).url } override suspend fun getTrendingGifs(page: Int): List { return api.getTrendingGifs(page).data.data } override suspend fun searchGifs(query: String, page: Int): List { return api.searchGifs(query, page).data.data } override suspend fun getGifCategories(): List { return api.getGifCategories().data.categories } override suspend fun createPersonalChat(userId: String): Chat { val currentUserId = tokenManager.getUserId() ?: "" val baseUrl = serverConfig.getBaseUrl().removeSuffix("/api/") val request = chats.data.remote.api.CreatePersonalChatRequest(userId) return api.createPersonalChat(request).toDomain(currentUserId, baseUrl) } override suspend fun deleteMessage(messageId: String, forEveryone: Boolean) { api.deleteMessage(messageId, forEveryone) } override suspend fun editMessage(messageId: String, content: String): Message { val request = SendMessageRequest(content = content) val currentUserId = tokenManager.getUserId() ?: "" val returnedId = api.editMessage(messageId, request) return Message( id = returnedId, chatId = "", senderId = currentUserId, senderName = "", content = content, sequenceId = 0, createdAt = java.time.ZonedDateTime.now().toString() ) } }