/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2025 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.or
import org.jetbrains.exposed.v1.dao.IntEntity
import org.jetbrains.exposed.v1.dao.IntEntityClass
import org.jetbrains.exposed.v1.jdbc.deleteWhere
import org.jetbrains.exposed.v1.jdbc.select

object EventRelationTable : IntIdTable("EventRelation") {
    val fightId = reference("FightId", EventFightTable).index()
    val fightTeam = enumeration("FightTeam", EventRelation.FightTeam::class)
    val fromType = enumeration("FromType", EventRelation.FromType::class)
    val fromId = integer("FromId")
    val fromPlace = integer("FromPlace")
}

class EventRelation(id: EntityID<Int>) : IntEntity(id) {
    companion object : IntEntityClass<EventRelation>(EventRelationTable) {
        @JvmStatic
        fun get(event: Event) =
            useDb {
                EventRelationTable.innerJoin(EventFightTable)
                    .select(EventRelationTable.columns)
                    .where { EventFightTable.eventId eq event.id }
                    .map { wrapRow(it) }
            }

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

        @JvmStatic
        fun getFightRelations(fight: EventFight) =
            useDb { find { (EventRelationTable.fromId eq fight.id.value) and (EventRelationTable.fromType eq FromType.FIGHT) }.toList() }

        @JvmStatic
        fun getGroupRelations(group: EventGroup) =
            useDb { find { (EventRelationTable.fromId eq group.id.value) and (EventRelationTable.fromType eq FromType.GROUP) }.toList() }

        @JvmStatic
        fun deleteRelations(fight: EventFight) =
            useDb {
                EventRelationTable.deleteWhere {
                    (EventRelationTable.fightId eq fight.id) or
                        ((EventRelationTable.fromId eq fight.id.value) and (EventRelationTable.fromType eq FromType.FIGHT))
                }
            }

        @JvmStatic
        fun deleteRelations(group: EventGroup) =
            useDb {
                EventRelationTable.deleteWhere {
                    (EventRelationTable.fromId eq group.id.value) and (EventRelationTable.fromType eq FromType.GROUP)
                }
            }

        @JvmStatic
        fun create(fight: EventFight, fightTeam: FightTeam, fromType: FromType, fromId: Int, fromPlace: Int) =
            useDb {
                new {
                    this.fightEntityId = fight.id
                    this.fightTeam = fightTeam
                    this.fromType = fromType
                    this.fromId = fromId
                    this.fromPlace = fromPlace
                }
            }
    }

    var fightEntityId by EventRelationTable.fightId
    var fightId: Int
        get() = fightEntityId.value
        set(value) = useDb { fightEntityId = EntityID(value, EventFightTable) }
    val fight by lazy { useDb { EventFight[fightEntityId] } }
    var fightTeam by EventRelationTable.fightTeam
        private set
    var fromType by EventRelationTable.fromType
        private set
    var fromId by EventRelationTable.fromId
        private set

    var fromFightId: Int?
        get() = if (fromType == FromType.FIGHT) fromId else null
        set(value) = useDb {
            fromType = FromType.FIGHT
            fromId = value!!
        }
    val fromFight: EventFight?
        get() = fromFightId?.let { useDb { EventFight[it] } }
    var fromGroupId: Int?
        get() = if (fromType == FromType.GROUP) fromId else null
        set(value) = useDb {
            fromType = FromType.GROUP
            fromId = value!!
        }
    val fromGroup: EventGroup?
        get() = fromGroupId?.let { useDb { EventGroup[it] } }
    var fromPlace by EventRelationTable.fromPlace
        private set

    fun getId() =
        id.value

    fun setUpdateTeam(team: FightTeam) =
        useDb {
            fightTeam = team
        }

    fun setFromFight(fight: EventFight, place: Int) =
        useDb {
            fromType = FromType.FIGHT
            fromId = fight.id.value
            fromPlace = place
        }

    fun setFromGroup(group: EventGroup, place: Int) =
        useDb {
            fromType = FromType.GROUP
            fromId = group.id.value
            fromPlace = place
        }

    fun getAdvancingTeam(): Team? =
        useDb {
            when (fromType) {
                FromType.FIGHT -> if (fromPlace == 0) fromFight?.winner else fromFight?.losser
                FromType.GROUP -> fromGroup?.calculatePoints()?.toList()?.sortedBy { (_, v) -> v }?.reversed()?.elementAt(fromPlace)?.first
            }
        }

    fun apply(): Boolean {
        val team = getAdvancingTeam()
            ?: return false

        useDb {
            when (fightTeam) {
                FightTeam.BLUE -> fight.teamBlue = team.teamId
                FightTeam.RED -> fight.teamRed = team.teamId
            }
        }

        return true
    }

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

    enum class FightTeam {
        BLUE, RED
    }

    enum class FromType {
        FIGHT, GROUP
    }
}
