package de.krisenvorrat.server.repository

import de.krisenvorrat.server.db.Messages
import de.krisenvorrat.server.db.Users
import de.krisenvorrat.shared.model.MessageDto
import org.jetbrains.exposed.sql.JoinType
import org.jetbrains.exposed.sql.SortOrder
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.or
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update

internal class MessageRepository {

    fun save(
        id: String,
        senderId: String,
        senderUsername: String,
        receiverId: String,
        body: String,
        sentAt: Long
    ): MessageDto {
        transaction {
            Messages.insert {
                it[Messages.id] = id
                it[Messages.senderId] = senderId
                it[Messages.receiverId] = receiverId
                it[Messages.body] = body
                it[Messages.sentAt] = sentAt
                it[Messages.deliveredAt] = null
            }
        }
        return MessageDto(
            id = id,
            senderId = senderId,
            senderUsername = senderUsername,
            receiverId = receiverId,
            body = body,
            sentAt = sentAt,
            deliveredAt = null
        )
    }

    fun getUndelivered(receiverId: String): List<MessageDto> = transaction {
        Messages.join(Users, JoinType.LEFT, Messages.senderId, Users.id)
            .selectAll()
            .where { (Messages.receiverId eq receiverId) and Messages.deliveredAt.isNull() }
            .map { row ->
                MessageDto(
                    id = row[Messages.id],
                    senderId = row[Messages.senderId],
                    senderUsername = row.getOrNull(Users.username) ?: "",
                    receiverId = row[Messages.receiverId],
                    body = row[Messages.body],
                    sentAt = row[Messages.sentAt],
                    deliveredAt = row[Messages.deliveredAt]
                )
            }
    }

    fun markDelivered(messageId: String) {
        transaction {
            Messages.update({ Messages.id eq messageId }) {
                it[deliveredAt] = System.currentTimeMillis()
            }
        }
    }

    fun markAllDeliveredForReceiver(receiverId: String) {
        transaction {
            Messages.update({
                (Messages.receiverId eq receiverId) and Messages.deliveredAt.isNull()
            }) {
                it[deliveredAt] = System.currentTimeMillis()
            }
        }
    }

    fun getConversation(userId1: String, userId2: String): List<MessageDto> = transaction {
        Messages.join(Users, JoinType.LEFT, Messages.senderId, Users.id)
            .selectAll()
            .where {
                ((Messages.senderId eq userId1) and (Messages.receiverId eq userId2)) or
                    ((Messages.senderId eq userId2) and (Messages.receiverId eq userId1))
            }
            .orderBy(Messages.sentAt to SortOrder.ASC)
            .map { row ->
                MessageDto(
                    id = row[Messages.id],
                    senderId = row[Messages.senderId],
                    senderUsername = row.getOrNull(Users.username) ?: "",
                    receiverId = row[Messages.receiverId],
                    body = row[Messages.body],
                    sentAt = row[Messages.sentAt],
                    deliveredAt = row[Messages.deliveredAt]
                )
            }
    }
}
