/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2026 SteamWar.de-Serverteam
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.sql

import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.greater
import org.jetbrains.exposed.v1.core.lessEq
import org.jetbrains.exposed.v1.dao.IntEntity
import org.jetbrains.exposed.v1.dao.IntEntityClass
import org.jetbrains.exposed.v1.javatime.timestamp
import org.jetbrains.exposed.v1.jdbc.insertAndGetId
import java.sql.Timestamp
import java.time.Instant

object EventTable : IntIdTable("Event", "EventId") {
    val name = varchar("EventName", 100).uniqueIndex()
    val deadline = timestamp("Deadline")
    val start = timestamp("Start").index()
    val end = timestamp("End").index()
    val maxPlayers = integer("MaximumTeamMembers")
    val schemType = varchar("SchemType", 16).nullable()
    val publicsOnly = bool("PublicSchemsOnly")
}

class Event(id: EntityID<Int>) : IntEntity(id) {
    companion object : IntEntityClass<Event>(EventTable) {
        private var current: Event? = null

        @JvmStatic
        fun get(): Event? =
            if (current?.now() == true) {
                current
            } else useDb {
                find { EventTable.start.lessEq(Instant.now()) and EventTable.end.greater(Instant.now()) }.firstOrNull()
                    ?.also { current == it }
            }

        @JvmStatic
        fun getAll() =
            useDb { all().toList() }

        @JvmStatic
        fun create(name: String, start: Timestamp, end: Timestamp) =
            useDb {
                EventTable.insertAndGetId {
                    it[this.name] = name
                    it[this.deadline] = start.toInstant()
                    it[this.start] = start.toInstant()
                    it[this.end] = end.toInstant()
                    it[this.maxPlayers] = 5
                    it[this.publicsOnly] = false
                }.let { get(it) }
            }

        @JvmStatic
        fun byId(id: Int) =
            useDb { findById(id) }

        @JvmStatic
        fun get(name: String) =
            useDb { find { EventTable.name eq name }.firstOrNull() }

        @JvmStatic
        fun getComing() =
            useDb { find { EventTable.start greater Instant.now() }.toList() }
    }

    val eventID by EventTable.id.transform({ EntityID(it, EventTable) }, { it.value })
    var eventName by EventTable.name
        private set
    var deadline: Timestamp by EventTable.deadline.transform({ it.toInstant() }, { Timestamp.from(it) })
        private set
    var start: Timestamp by EventTable.start.transform({ it.toInstant() }, { Timestamp.from(it) })
        private set
    var end: Timestamp by EventTable.end.transform({ it.toInstant() }, { Timestamp.from(it) })
        private set
    var maximumTeamMembers by EventTable.maxPlayers
        private set
    var schematicType by EventTable.schemType.transform({ it?.toDB() }, { it?.let { SchematicType.fromDB(it) } })
        private set
    var publicSchemsOnly by EventTable.publicsOnly
        private set

    fun publicSchemsOnly() =
        publicSchemsOnly

    fun now() =
        Instant.now().let { it.isAfter(start.toInstant()) && it.isBefore(end.toInstant()) }

    fun update(
        name: String,
        deadline: Timestamp,
        start: Timestamp,
        end: Timestamp,
        schematicType: SchematicType?,
        maxPlayers: Int,
        publicSchemsOnly: Boolean
    ) =
        useDb {
            this@Event.eventName = name
            this@Event.deadline = deadline
            this@Event.start = start
            this@Event.end = end
            this@Event.maximumTeamMembers = maxPlayers
            this@Event.schematicType = schematicType
            this@Event.publicSchemsOnly = publicSchemsOnly
        }

    override fun delete() =
        useDb { super.delete() }
}