package de.tsschulz.tt_tagebuch.shared.api

import de.tsschulz.tt_tagebuch.shared.api.http.AuthedHttpClient
import de.tsschulz.tt_tagebuch.shared.api.models.AddExternalTournamentParticipantBody
import de.tsschulz.tt_tagebuch.shared.api.models.AddMiniChampionshipBody
import de.tsschulz.tt_tagebuch.shared.api.models.AddStandardTournamentBody
import de.tsschulz.tt_tagebuch.shared.api.models.AddTournamentClassBody
import de.tsschulz.tt_tagebuch.shared.api.models.AssignParticipantToGroupBody
import de.tsschulz.tt_tagebuch.shared.api.models.CreateTournamentPairingBody
import de.tsschulz.tt_tagebuch.shared.api.models.GaveUpFlagBody
import de.tsschulz.tt_tagebuch.shared.api.models.InternalTournamentDetailDto
import de.tsschulz.tt_tagebuch.shared.api.models.InternalTournamentStatsDto
import de.tsschulz.tt_tagebuch.shared.api.models.InternalTournamentSummaryDto
import de.tsschulz.tt_tagebuch.shared.api.models.MergeTournamentPoolBody
import de.tsschulz.tt_tagebuch.shared.api.models.RemoveExternalTournamentParticipantBody
import de.tsschulz.tt_tagebuch.shared.api.models.ResetTournamentPoolBody
import de.tsschulz.tt_tagebuch.shared.api.models.SeededFlagBody
import de.tsschulz.tt_tagebuch.shared.api.models.SetTournamentModusBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentAddInternalParticipantBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentAddMatchResultBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentAdvanceStageBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentClassDto
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentCleanupOrphanedBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentClubTournamentBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentCreateGroupMatchesBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentCreateGroupsBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentCreateGroupsPerClassBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentDeleteKnockoutBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentDeleteMatchResultBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentExternalParticipantRowDto
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentFinishMatchBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentGetExternalParticipantsBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentGetParticipantsBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentManualAssignGroupsBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentMatchActiveBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentMatchDto
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentMatchTableBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentParticipantRowDto
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentRemoveInternalParticipantBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentReopenMatchBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentStartKnockoutBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentUpsertStagesBody
import de.tsschulz.tt_tagebuch.shared.api.models.UpdateParticipantClassBody
import de.tsschulz.tt_tagebuch.shared.api.models.UpdateTournamentClassBody
import de.tsschulz.tt_tagebuch.shared.api.models.UpdateTournamentMetaBody
import de.tsschulz.tt_tagebuch.shared.api.models.UpdateTournamentPairingBody
import io.ktor.client.call.body
import io.ktor.client.request.delete
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import io.ktor.client.request.post
import io.ktor.client.request.put
import io.ktor.client.request.setBody
import kotlinx.serialization.json.JsonElement

class TournamentsApi(
    private val client: AuthedHttpClient,
) {
    /** Query [type]: `mini` nur Minimeisterschaften; sonst alle (clientseitig nach intern/offen filtern). */
    suspend fun listTournaments(clubId: Int, type: String? = null): List<InternalTournamentSummaryDto> {
        return client.http.get("/api/tournament/$clubId") {
            type?.takeIf { it.isNotBlank() }?.let { parameter("type", it) }
        }.body()
    }

    suspend fun getTournament(clubId: Int, tournamentId: Int): InternalTournamentDetailDto {
        val result: InternalTournamentDetailDto = client.http.get("/api/tournament/$clubId/$tournamentId").body()
        try {
            println("[TournamentsApi] getTournament -> numberOfTables: ${'$'}{result.numberOfTables}")
        } catch (t: Throwable) {
            // ignore on platforms where println may not be available
        }
        return result
    }

    suspend fun getInternalTournamentStats(
        clubId: Int,
        months: Int,
        ageClassKeys: String?,
    ): InternalTournamentStatsDto {
        return client.http.get("/api/tournament/internal-stats/$clubId") {
            parameter("months", months)
            ageClassKeys?.let { parameter("ageClassKeys", it) }
        }.body()
    }

    suspend fun addStandardTournament(body: AddStandardTournamentBody): InternalTournamentDetailDto {
        return client.http.post("/api/tournament") {
            setBody(body)
        }.body()
    }

    suspend fun addMiniChampionship(body: AddMiniChampionshipBody): InternalTournamentDetailDto {
        return client.http.post("/api/tournament/mini") {
            setBody(body)
        }.body()
    }

    suspend fun updateTournament(clubId: Int, tournamentId: Int, body: UpdateTournamentMetaBody): InternalTournamentDetailDto {
        // Debug: print body so it appears in device logs (useful during development)
        try {
            println("[TournamentsApi] updateTournament body: $body")
        } catch (t: Throwable) {
            // ignore on platforms where println may not be available
        }
        return client.http.put("/api/tournament/$clubId/$tournamentId") {
            setBody(body)
        }.body()
    }

    suspend fun setModus(body: SetTournamentModusBody) {
        client.http.post("/api/tournament/modus") {
            setBody(body)
        }
    }

    suspend fun createEmptyGroups(body: TournamentCreateGroupsBody) {
        client.http.put("/api/tournament/groups") {
            setBody(body)
        }
    }

    suspend fun createGroupsPerClass(body: TournamentCreateGroupsPerClassBody) {
        client.http.post("/api/tournament/groups/create") {
            setBody(body)
        }
    }

    suspend fun fillGroups(body: TournamentClubTournamentBody): List<TournamentParticipantRowDto> {
        return client.http.post("/api/tournament/groups") {
            setBody(body)
        }.body()
    }

    suspend fun createGroupMatches(body: TournamentCreateGroupMatchesBody) {
        client.http.post("/api/tournament/matches/create") {
            setBody(body)
        }
    }

    suspend fun resetGroups(body: TournamentClubTournamentBody) {
        client.http.post("/api/tournament/groups/reset") {
            setBody(body)
        }
    }

    suspend fun resetMatches(body: TournamentClubTournamentBody) {
        client.http.post("/api/tournament/matches/reset") {
            setBody(body)
        }
    }

    suspend fun cleanupOrphanedMatches(body: TournamentCleanupOrphanedBody) {
        client.http.post("/api/tournament/matches/cleanup-orphaned") {
            setBody(body)
        }
    }

    suspend fun distributeTables(body: TournamentClubTournamentBody): de.tsschulz.tt_tagebuch.shared.api.models.DistributeTablesResponseDto {
        return client.http.post("/api/tournament/matches/distribute") {
            setBody(body)
        }.body()
    }

    suspend fun getGroups(clubId: Int, tournamentId: Int): JsonElement {
        return client.http.get("/api/tournament/groups") {
            parameter("clubId", clubId)
            parameter("tournamentId", tournamentId)
        }.body()
    }

    suspend fun manualAssignGroups(body: TournamentManualAssignGroupsBody): JsonElement {
        return client.http.post("/api/tournament/groups/manual") {
            setBody(body)
        }.body()
    }

    suspend fun assignParticipantToGroup(body: AssignParticipantToGroupBody) {
        client.http.put("/api/tournament/participant/group") {
            setBody(body)
        }
    }

    suspend fun mergeClassesIntoPool(body: MergeTournamentPoolBody) {
        client.http.post("/api/tournament/pools/merge") {
            setBody(body)
        }
    }

    suspend fun resetPool(body: ResetTournamentPoolBody) {
        client.http.post("/api/tournament/pools/reset") {
            setBody(body)
        }
    }

    suspend fun listTournamentClasses(clubId: Int, tournamentId: Int): List<TournamentClassDto> {
        return client.http.get("/api/tournament/classes/$clubId/$tournamentId").body()
    }

    suspend fun addTournamentClass(clubId: Int, tournamentId: Int, body: AddTournamentClassBody): TournamentClassDto {
        return client.http.post("/api/tournament/class/$clubId/$tournamentId") {
            setBody(body)
        }.body()
    }

    suspend fun updateTournamentClass(
        clubId: Int,
        tournamentId: Int,
        classId: Int,
        body: UpdateTournamentClassBody,
    ): TournamentClassDto {
        return client.http.put("/api/tournament/class/$clubId/$tournamentId/$classId") {
            setBody(body)
        }.body()
    }

    suspend fun deleteTournamentClass(clubId: Int, tournamentId: Int, classId: Int) {
        client.http.delete("/api/tournament/class/$clubId/$tournamentId/$classId")
    }

    suspend fun listInternalParticipants(body: TournamentGetParticipantsBody): List<TournamentParticipantRowDto> {
        return client.http.post("/api/tournament/participants") {
            setBody(body)
        }.body()
    }

    suspend fun addInternalParticipant(body: TournamentAddInternalParticipantBody): List<TournamentParticipantRowDto> {
        return client.http.post("/api/tournament/participant") {
            setBody(body)
        }.body()
    }

    suspend fun removeInternalParticipant(body: TournamentRemoveInternalParticipantBody) {
        client.http.delete("/api/tournament/participant") {
            setBody(body)
        }
    }

    suspend fun updateParticipantClass(
        clubId: Int,
        tournamentId: Int,
        participantId: Int,
        body: UpdateParticipantClassBody,
    ) {
        client.http.put("/api/tournament/participant/$clubId/$tournamentId/$participantId/class") {
            setBody(body)
        }
    }

    suspend fun updateParticipantSeeded(clubId: Int, tournamentId: Int, participantId: Int, body: SeededFlagBody) {
        client.http.put("/api/tournament/participant/$clubId/$tournamentId/$participantId/seeded") {
            setBody(body)
        }
    }

    suspend fun setParticipantGaveUp(clubId: Int, tournamentId: Int, participantId: Int, body: GaveUpFlagBody) {
        client.http.put("/api/tournament/participant/$clubId/$tournamentId/$participantId/gave-up") {
            setBody(body)
        }
    }

    suspend fun listExternalParticipants(body: TournamentGetExternalParticipantsBody): List<TournamentExternalParticipantRowDto> {
        return client.http.post("/api/tournament/external-participants") {
            setBody(body)
        }.body()
    }

    suspend fun addExternalParticipant(body: AddExternalTournamentParticipantBody) {
        client.http.post("/api/tournament/external-participant") {
            setBody(body)
        }
    }

    suspend fun removeExternalParticipant(body: RemoveExternalTournamentParticipantBody) {
        client.http.delete("/api/tournament/external-participant") {
            setBody(body)
        }
    }

    suspend fun updateExternalParticipantSeeded(
        clubId: Int,
        tournamentId: Int,
        participantId: Int,
        body: SeededFlagBody,
    ) {
        client.http.put("/api/tournament/external-participant/$clubId/$tournamentId/$participantId/seeded") {
            setBody(body)
        }
    }

    suspend fun setExternalParticipantGaveUp(
        clubId: Int,
        tournamentId: Int,
        participantId: Int,
        body: GaveUpFlagBody,
    ) {
        client.http.put("/api/tournament/external-participant/$clubId/$tournamentId/$participantId/gave-up") {
            setBody(body)
        }
    }

    suspend fun listMatches(clubId: Int, tournamentId: Int): List<TournamentMatchDto> {
        return client.http.get("/api/tournament/matches/$clubId/$tournamentId").body()
    }

    suspend fun addMatchResult(body: TournamentAddMatchResultBody) {
        client.http.post("/api/tournament/match/result") {
            setBody(body)
        }
    }

    suspend fun deleteMatchResult(body: TournamentDeleteMatchResultBody) {
        client.http.delete("/api/tournament/match/result") {
            setBody(body)
        }
    }

    suspend fun finishMatch(body: TournamentFinishMatchBody) {
        client.http.post("/api/tournament/match/finish") {
            setBody(body)
        }
    }

    suspend fun reopenMatch(body: TournamentReopenMatchBody) {
        client.http.post("/api/tournament/match/reopen") {
            setBody(body)
        }
    }

    suspend fun setMatchActive(clubId: Int, tournamentId: Int, matchId: Int, body: TournamentMatchActiveBody) {
        client.http.put("/api/tournament/match/$clubId/$tournamentId/$matchId/active") {
            setBody(body)
        }
    }

    suspend fun setMatchTable(clubId: Int, tournamentId: Int, matchId: Int, body: TournamentMatchTableBody) {
        client.http.put("/api/tournament/match/$clubId/$tournamentId/$matchId/table") {
            setBody(body)
        }
    }

    suspend fun startKnockout(body: TournamentStartKnockoutBody) {
        client.http.post("/api/tournament/knockout") {
            setBody(body)
        }
    }

    suspend fun deleteKnockoutMatches(body: TournamentDeleteKnockoutBody) {
        client.http.delete("/api/tournament/matches/knockout") {
            setBody(body)
        }
    }

    suspend fun getStages(clubId: Int, tournamentId: Int): JsonElement {
        return client.http.get("/api/tournament/stages") {
            parameter("clubId", clubId)
            parameter("tournamentId", tournamentId)
        }.body()
    }

    suspend fun upsertStages(body: TournamentUpsertStagesBody): JsonElement {
        return client.http.put("/api/tournament/stages") {
            setBody(body)
        }.body()
    }

    suspend fun advanceStage(body: TournamentAdvanceStageBody): JsonElement {
        return client.http.post("/api/tournament/stages/advance") {
            setBody(body)
        }.body()
    }

    suspend fun listPairings(clubId: Int, tournamentId: Int, classId: Int): JsonElement {
        return client.http.get("/api/tournament/pairings/$clubId/$tournamentId/$classId").body()
    }

    suspend fun createPairing(clubId: Int, tournamentId: Int, classId: Int, body: CreateTournamentPairingBody): JsonElement {
        return client.http.post("/api/tournament/pairing/$clubId/$tournamentId/$classId") {
            setBody(body)
        }.body()
    }

    suspend fun updatePairing(
        clubId: Int,
        tournamentId: Int,
        pairingId: Int,
        body: UpdateTournamentPairingBody,
    ): JsonElement {
        return client.http.put("/api/tournament/pairing/$clubId/$tournamentId/$pairingId") {
            setBody(body)
        }.body()
    }

    suspend fun deletePairing(clubId: Int, tournamentId: Int, pairingId: Int) {
        client.http.delete("/api/tournament/pairing/$clubId/$tournamentId/$pairingId")
    }
}
