package de.kif.backend.repository

import de.kif.backend.database.DbRoom
import de.kif.backend.database.dbQuery
import de.kif.backend.util.PushService
import de.kif.common.*
import de.kif.common.model.Room
import de.westermann.kobserve.event.EventHandler
import kotlinx.coroutines.runBlocking
import org.jetbrains.exposed.sql.*
import java.util.Date

object RoomRepository : Repository<Room> {

    override val onCreate = EventHandler<Long>()
    override val onUpdate = EventHandler<Long>()
    override val onDelete = EventHandler<Long>()

    private fun rowToModel(row: ResultRow): Room {
        val id = row[DbRoom.id]
        val name = row[DbRoom.name]
        val places = row[DbRoom.places]
        val projector = row[DbRoom.projector]
        val internet = row[DbRoom.internet]
        val whiteboard = row[DbRoom.whiteboard]
        val blackboard = row[DbRoom.blackboard]
        val accessible = row[DbRoom.accessible]

        val createdAt = row[DbRoom.createdAt]
        val updatedAt = row[DbRoom.updatedAt]

        return Room(
            id,
            name,
            places,
            projector,
            internet,
            whiteboard,
            blackboard,
            accessible,
            createdAt,
            updatedAt
        )
    }

    override suspend fun get(id: Long): Room? {
        return dbQuery {
            rowToModel(DbRoom.select { DbRoom.id eq id }.firstOrNull() ?: return@dbQuery null)
        }
    }

    override suspend fun create(model: Room): Long {
        val now = Date().time

        return dbQuery {
            val id = DbRoom.insert {
                it[name] = model.name
                it[places] = model.places
                it[projector] = model.projector
                it[internet] = model.internet
                it[whiteboard] = model.whiteboard
                it[blackboard] = model.blackboard
                it[accessible] = model.accessible
                it[createdAt] = now
                it[updatedAt] = now
            }[DbRoom.id] ?: throw IllegalStateException("Cannot create model!")

            onCreate.emit(id)

            id
        }
    }

    override suspend fun update(model: Room) {
        if (model.id == null) throw IllegalStateException("Cannot update model which was not created!")

        val now = Date().time

        dbQuery {
            DbRoom.update({ DbRoom.id eq model.id }) {
                it[name] = model.name
                it[places] = model.places
                it[projector] = model.projector
                it[internet] = model.internet
                it[whiteboard] = model.whiteboard
                it[blackboard] = model.blackboard
                it[accessible] = model.accessible
                it[updatedAt] = now
            }

            onUpdate.emit(model.id)
        }
    }

    override suspend fun delete(id: Long) {
        onDelete.emit(id)

        dbQuery {
            DbRoom.deleteWhere { DbRoom.id eq id }
        }
    }

    override suspend fun all(): List<Room> {
        return dbQuery {
            val result = DbRoom.selectAll()

            result.map(this::rowToModel)
        }
    }

    fun registerPushService() {
        onCreate {
            runBlocking {
                PushService.notify(MessageType.CREATE, RepositoryType.ROOM, it)
            }
        }
        onUpdate {
            runBlocking {
                PushService.notify(MessageType.UPDATE, RepositoryType.ROOM, it)
            }
        }
        onDelete {
            runBlocking {
                PushService.notify(MessageType.DELETE, RepositoryType.ROOM, it)
            }
        }
    }
}
