package de.kif.backend.repository

import de.kif.backend.database.DbTrack
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.Track
import de.kif.common.model.parseColor
import de.westermann.kobserve.event.EventHandler
import kotlinx.coroutines.runBlocking
import org.jetbrains.exposed.sql.*
import java.util.Date

object TrackRepository : Repository<Track> {

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

    private fun rowToModel(row: ResultRow): Track {
        val id = row[DbTrack.id]
        val name = row[DbTrack.name]
        val color = row[DbTrack.color].parseColor()

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

        return Track(id, name, color, createdAt, updatedAt)
    }

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

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

        return dbQuery {
            val id = DbTrack.insert {
                it[name] = model.name
                it[color] = model.color.toString()

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

            onCreate.emit(id)

            id
        }
    }

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

        val now = Date().time

        dbQuery {
            DbTrack.update({ DbTrack.id eq model.id }) {
                it[name] = model.name
                it[color] = model.color.toString()

                it[updatedAt] = now
            }

            onUpdate.emit(model.id)
        }
    }

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

        dbQuery {
            DbTrack.deleteWhere { DbTrack.id eq id }
        }
    }

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

            result.map(this::rowToModel)
        }
    }

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