/*
 *   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.routes

import de.steamwar.ResponseError
import de.steamwar.sql.EventFight
import de.steamwar.sql.Team
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import kotlinx.serialization.Serializable
import java.sql.Timestamp
import java.time.Instant

@Serializable
data class ResponseEventFight(
    val id: Int,
    val spielmodus: String,
    val map: String,
    val blueTeam: ResponseTeam,
    val redTeam: ResponseTeam,
    val start: Long,
    val ergebnis: Int,
    val spectatePort: Int?,
    val group: ResponseGroups?,
    val hasFinished: Boolean
) {
    constructor(eventFight: EventFight) : this(
        eventFight.fightID,
        eventFight.spielmodus,
        eventFight.map,
        ResponseTeam(Team.byId(eventFight.teamBlue)),
        ResponseTeam(Team.byId(eventFight.teamRed)),
        eventFight.startTime.time,
        eventFight.ergebnis,
        eventFight.spectatePort,
        eventFight.group.orElse(null)?.let { ResponseGroups(it, short = true) },
        eventFight.hasFinished()
    )
}

@Serializable
data class ResponseTeam(val id: Int, val name: String, val kuerzel: String, val color: String) {
    constructor(team: Team) : this(team.teamId, team.teamName, team.teamKuerzel, team.teamColor)
}

@Serializable
data class UpdateEventFight(
    val blueTeam: Int? = null,
    val redTeam: Int? = null,
    val start: Long? = null,
    val spielmodus: String? = null,
    val map: String? = null,
    val group: Int? = null,
    val spectatePort: Int? = null,
    val ergebnis: Int? = null,
)

@Serializable
data class CreateEventFight(
    val spielmodus: String,
    val map: String,
    val blueTeam: Int,
    val redTeam: Int,
    val start: Long,
    val spectatePort: Int? = null,
    val group: Int? = null
)

fun Route.configureEventFightRoutes() {
    route("/fights") {
        get {
            val event = call.receiveEvent()
                ?: return@get
            call.respond(EventFight.getEvent(event.eventID).map { ResponseEventFight(it) })
        }
        post {
            val event = call.receiveEvent()
                ?: return@post

            val fight = call.receiveNullable<CreateEventFight>()
            if (fight == null) {
                call.respond(HttpStatusCode.BadRequest, ResponseError("Invalid body"))
                return@post
            }

            val eventFight = EventFight.create(
                event.eventID,
                Timestamp.from(Instant.ofEpochMilli(fight.start)),
                fight.spielmodus,
                fight.map,
                fight.blueTeam,
                fight.redTeam,
                fight.spectatePort
            )
            if (fight.group != null) {
                eventFight.setGroup(fight.group)
            }
            call.respond(HttpStatusCode.Created, ResponseEventFight(eventFight))
        }
        route("/{fight}") {
            put {
                val fight = call.receiveFight()
                    ?: return@put
                val updateFight = call.receiveNullable<UpdateEventFight>()
                if (updateFight == null) {
                    call.respond(HttpStatusCode.BadRequest, ResponseError("Invalid body"))
                    return@put
                }

                val teamBlue = updateFight.blueTeam
                    ?: fight.teamBlue
                val teamRed = updateFight.redTeam
                    ?: fight.teamRed
                val start = updateFight.start?.let { Timestamp.from(Instant.ofEpochMilli(it)) }
                    ?: fight.startTime
                val spielmodus = updateFight.spielmodus
                    ?: fight.spielmodus
                val map = updateFight.map
                    ?: fight.map
                val spectatePort = updateFight.spectatePort
                    ?: fight.spectatePort

                if (updateFight.group != null) {
                    if (updateFight.group == -1) {
                        fight.setGroup(null)
                    } else {
                        fight.setGroup(updateFight.group)
                    }
                }

                if (updateFight.ergebnis != null) {
                    fight.ergebnis = updateFight.ergebnis
                }

                fight.update(start, spielmodus, map, teamBlue, teamRed, spectatePort)
                call.respond(HttpStatusCode.OK, ResponseEventFight(fight))
            }
            delete {
                val fight = call.receiveFight()
                    ?: return@delete
                fight.delete()
                call.respond(HttpStatusCode.OK)
            }

        }
    }
}

suspend fun ApplicationCall.receiveFight(fieldName: String = "fight"): EventFight? {
    val fightId = parameters[fieldName]?.toIntOrNull()
    if (fightId == null) {
        respond(HttpStatusCode.BadRequest, ResponseError("Invalid fight ID"))
        return null
    }

    val fight = EventFight.byId(fightId)
    if (fight == null) {
        respond(HttpStatusCode.NotFound, ResponseError("Fight not found"))
        return null
    }
    return fight
}