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.domain.model.Chat import chats.domain.model.Message import chats.domain.model.MediaType import chats.domain.repository.ChatRepository import core.network.ServerConfig import core.security.TokenManager import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MultipartBody import okhttp3.RequestBody.Companion.asRequestBody import javax.inject.Inject class ChatRepositoryImpl @Inject constructor( private val api: ChatApi, private val tokenManager: TokenManager, private val serverConfig: ServerConfig ) : ChatRepository { override suspend fun getChats(): List { val currentUserId = tokenManager.getUserId() ?: "" val baseUrl = serverConfig.getBaseUrl().removeSuffix("/api/") return api.getChats().map { it.toDomain(currentUserId, baseUrl) } } override suspend fun getMessages(chatId: String): List { val baseUrl = serverConfig.getBaseUrl().removeSuffix("/api/") return api.getMessages(chatId).map { it.toDomain(baseUrl) } } override suspend fun sendMessage(chatId: String, content: String): Message { val request = SendMessageRequest(content = content, type = "text") val baseUrl = serverConfig.getBaseUrl().removeSuffix("/api/") return api.sendMessage(chatId, request).toDomain(baseUrl) } 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) { api.markMessagesAsRead(chatId, lastMessageId) } override suspend fun uploadMedia(file: java.io.File): String { val requestFile = file.asRequestBody("image/*".toMediaTypeOrNull()) val body = MultipartBody.Part.createFormData("file", file.name, requestFile) return api.uploadFile(body).url } } // Mappers fun ChatDto.toDomain(currentUserId: String, baseUrl: String): Chat { val chatName = name ?: if (type == "personal") { members.firstOrNull { it.userId != currentUserId }?.user?.displayName ?: "Unknown Chat" } else "Group Chat" val chatAvatar = (avatar ?: if (type == "personal") { members.firstOrNull { it.userId != currentUserId }?.user?.avatarUrl } else null)?.ensureAbsoluteUrl(baseUrl) return Chat( id = id, type = type, name = chatName, avatar = chatAvatar, unreadCount = unreadCount, lastMessage = messages.firstOrNull()?.toDomain(baseUrl) ) } fun MessageDto.toDomain(baseUrl: String): Message { val domainMediaType = when (type) { "image", "photo" -> MediaType.IMAGE "video" -> MediaType.VIDEO "audio", "voice" -> MediaType.AUDIO "file" -> MediaType.FILE else -> when (media.firstOrNull()?.type) { "image", "photo" -> MediaType.IMAGE "video" -> MediaType.VIDEO "audio", "voice" -> MediaType.AUDIO "file" -> MediaType.FILE else -> MediaType.TEXT } } return Message( id = id, chatId = chatId ?: "", senderId = senderId ?: "", senderName = sender?.displayName ?: "Unknown", senderAvatar = sender?.avatarUrl?.ensureAbsoluteUrl(baseUrl), content = content, sequenceId = sequenceId ?: 0, createdAt = createdAt ?: "", media = media.map { chats.domain.model.Media( id = it.id, type = it.type, url = it.url.ensureAbsoluteUrl(baseUrl), filename = it.filename, size = it.size, duration = it.duration ) }, mediaType = domainMediaType, reactions = reactions?.associate { it.emoji to it.count } ?: emptyMap(), replyTo = replyTo?.toDomain(baseUrl) ) } fun String.ensureAbsoluteUrl(baseUrl: String): String { return if (this.startsWith("http")) { this } else { val base = baseUrl.removeSuffix("/") val path = if (this.startsWith("/")) this else "/$this" "$base$path" } }