package de.kif.backend.repository

import de.kif.backend.database.DbUser
import de.kif.backend.database.DbUserPermission
import de.kif.backend.database.dbQuery
import de.kif.backend.util.PushService
import de.kif.common.MessageType
import de.kif.common.Repository
import de.kif.common.RepositoryType
import de.kif.common.model.User
import de.westermann.kobserve.event.EventHandler
import kotlinx.coroutines.runBlocking
import org.jetbrains.exposed.sql.*
import java.util.Date

object UserRepository : Repository<User> {

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

    private fun rowToModel(row: ResultRow): User {
        val id = row[DbUser.id]
        val username = row[DbUser.username]
        val password = row[DbUser.password]

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

        val permissions = DbUserPermission.slice(DbUserPermission.permission).select {
            DbUserPermission.userId eq id
        }.map { it[DbUserPermission.permission] }.toSet()

        return User(id, username, password, permissions, createdAt, updatedAt)
    }

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

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

        return dbQuery {
            val id = DbUser.insert {
                it[username] = model.username
                it[password] = model.password

                it[createdAt] = now
                it[updatedAt] = now
            }[DbUser.id] ?: throw IllegalStateException("Cannot create model!")

            for (permission in model.permissions) {
                DbUserPermission.insert {
                    it[userId] = id
                    it[this.permission] = permission
                }
            }

            onCreate.emit(id)

            id
        }
    }

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

        val now = Date().time

        dbQuery {
            DbUser.update({ DbUser.id eq model.id }) {
                it[username] = model.username
                it[password] = model.password

                it[updatedAt] = now
            }

            DbUserPermission.deleteWhere { DbUserPermission.userId eq model.id }

            for (permission in model.permissions) {
                DbUserPermission.insert {
                    it[userId] = model.id
                    it[this.permission] = permission
                }
            }

            onUpdate.emit(model.id)
        }
    }

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

        dbQuery {
            DbUserPermission.deleteWhere { DbUserPermission.userId eq id }
            DbUser.deleteWhere { DbUser.id eq id }
        }
    }

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

            result.map(this::rowToModel)
        }
    }

    suspend fun find(username: String): User? {
        return dbQuery {
            rowToModel(DbUser.select { DbUser.username eq username }.firstOrNull() ?: return@dbQuery null)
        }
    }

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