package de.tsschulz.tt_tagebuch.app.ui

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Stop
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.Divider
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.Alignment
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import de.tsschulz.tt_tagebuch.app.AppDependencies
import de.tsschulz.tt_tagebuch.shared.api.TournamentsApi
import de.tsschulz.tt_tagebuch.shared.api.models.AddTournamentClassBody
import de.tsschulz.tt_tagebuch.shared.api.models.CreateTournamentPairingBody
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.TournamentClassDto
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentDeleteKnockoutBody
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.TournamentMatchTableBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentReopenMatchBody
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.focus.FocusState
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentStartKnockoutBody
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentMatchDto
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.UpdateParticipantClassBody
import de.tsschulz.tt_tagebuch.shared.api.models.UpdateTournamentClassBody
import de.tsschulz.tt_tagebuch.shared.api.models.AddExternalTournamentParticipantBody
import de.tsschulz.tt_tagebuch.shared.api.models.RemoveExternalTournamentParticipantBody
import kotlinx.coroutines.CoroutineScope
import de.tsschulz.tt_tagebuch.shared.api.models.InternalTournamentDetailDto
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentClubTournamentBody
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.intOrNull
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import androidx.compose.ui.graphics.Color
import androidx.compose.foundation.background
import de.tsschulz.tt_tagebuch.shared.api.models.TournamentMatchActiveBody

@Composable
internal fun TournamentEditorClassesTab(
    clubId: Int,
    tournamentId: Int,
    classes: List<TournamentClassDto>,
    tr: (String, String) -> String,
    api: TournamentsApi,
    scope: CoroutineScope,
    onReload: () -> Unit,
    onError: (String?) -> Unit,
) {
    var showAdd by remember { mutableStateOf(false) }
    var newName by remember { mutableStateOf("") }
    var newDoubles by remember { mutableStateOf(false) }
    var showDistributedDialog by remember { mutableStateOf(false) }
    var distributedMatches by remember { mutableStateOf<List<TournamentMatchDto>>(emptyList()) }
    var distributedMessage by remember { mutableStateOf<String?>(null) }
    if (showAdd) {
        StableAlertDialog(
            onDismissRequest = { showAdd = false },
            title = { Text(tr("tournaments.addClass", "Klasse anlegen")) },
            text = {
                Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                    OutlinedTextField(value = newName, onValueChange = { newName = it }, label = { Text(tr("tournaments.className", "Klassenname")) })
                    Row {
                        Text(tr("mobile.doublesTournament", "Doppel"))
                        androidx.compose.material.Switch(checked = newDoubles, onCheckedChange = { newDoubles = it })
                    }
                }
            },
            confirmButton = {
                TextButton(
                    onClick = {
                        if (newName.isBlank()) return@TextButton
                        scope.launch {
                            runCatching {
                                withContext(Dispatchers.IO) {
                                    api.addTournamentClass(
                                        clubId,
                                        tournamentId,
                                        AddTournamentClassBody(name = newName.trim(), isDoubles = newDoubles),
                                    )
                                }
                            }.onFailure { onError(it.message) }
                            showAdd = false
                            newName = ""
                            newDoubles = false
                            onReload()
                        }
                    },
                ) { Text(tr("mobile.add", "Hinzufügen")) }
            },
            dismissButton = {
                TextButton(onClick = { showAdd = false }) { Text(tr("common.cancel", "Abbrechen")) }
            },
        )
    }
    Column(Modifier.fillMaxWidth()) {
        Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) {
            Button(onClick = { showAdd = true }, modifier = Modifier.weight(1f)) {
                Text(tr("tournaments.addClass", "Klasse anlegen"))
            }
            OutlinedButton(onClick = {
                scope.launch {
                    runCatching {
                        val resp = withContext(Dispatchers.IO) {
                            api.distributeTables(de.tsschulz.tt_tagebuch.shared.api.models.TournamentClubTournamentBody(clubId, tournamentId))
                        }

                        // If server returned no updates, show info message
                        if (resp.updated.isEmpty()) {
                            // show simple AlertDialog with server message or default
                            showDistributedDialog = true
                            distributedMatches = emptyList()
                            distributedMessage = resp.message ?: tr("tournaments.tablesDistributed", "Tische wurden verteilt.")
                        } else {
                            // If server already included player objects, use them directly
                            val firstHasPlayers = resp.updated.firstOrNull()?.player1 != null || resp.updated.firstOrNull()?.player2 != null
                            if (firstHasPlayers) {
                                distributedMatches = resp.updated
                                showDistributedDialog = true
                            } else {
                                // fallback: fetch full matches to extract player info
                                val allMatches = withContext(Dispatchers.IO) { api.listMatches(clubId, tournamentId) }
                                val ids = resp.updated.map { it.id }.toSet()
                                distributedMatches = allMatches.filter { it.id in ids }
                                showDistributedDialog = true
                            }
                        }
                    }.onFailure { onError(it.message) }
                    onReload()
                }
            }, modifier = Modifier.weight(1f)) {
                Text(tr("tournaments.distributeTables", "Freie Tische verteilen"))
            }

            if (showDistributedDialog) {
                StableAlertDialog(
                    onDismissRequest = { showDistributedDialog = false },
                    title = { Text(tr("tournaments.distributeTablesResult", "Tischverteilung")) },
                    text = {
                        Column(Modifier.fillMaxWidth()) {
                                if (distributedMatches.isEmpty()) {
                                // No assignments returned
                                Text(distributedMessage ?: tr("tournaments.tablesDistributed", "Tische wurden verteilt."))
                            } else {
                                // Header
                                Row(Modifier.fillMaxWidth().padding(bottom = 8.dp)) {
                                    Text(tr("tournaments.playerA", "Spieler A"), modifier = Modifier.weight(1f), fontWeight = FontWeight.SemiBold)
                                    Text(tr("tournaments.playerB", "Spieler B"), modifier = Modifier.weight(1f), fontWeight = FontWeight.SemiBold)
                                    Text(tr("tournaments.tableShort", "Tisch"), modifier = Modifier.width(48.dp), fontWeight = FontWeight.SemiBold)
                                }
                                Divider()
                                LazyColumn {
                                    items(distributedMatches) { m ->
                                        val name1 = extractPlayerName(m.player1)
                                        val name2 = extractPlayerName(m.player2)
                                        Row(Modifier.fillMaxWidth().padding(vertical = 6.dp)) {
                                            Text(name1, modifier = Modifier.weight(1f))
                                            Text("–", modifier = Modifier.width(16.dp), textAlign = androidx.compose.ui.text.style.TextAlign.Center)
                                            Text(name2, modifier = Modifier.weight(1f))
                                            Text((m.tableNumber ?: "-").toString(), modifier = Modifier.width(48.dp))
                                        }
                                        Divider()
                                    }
                                }
                            }
                        }
                    },
                    confirmButton = {
                        TextButton(onClick = { showDistributedDialog = false }) { Text(tr("common.ok", "OK")) }
                    }
                )
            }
        }
        Column(verticalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.padding(top = 12.dp)) {
            classes.forEach { c ->
                ClassRowEditor(
                    clubId = clubId,
                    tournamentId = tournamentId,
                    c = c,
                    tr = tr,
                    api = api,
                    scope = scope,
                    onReload = onReload,
                    onError = onError,
                )
            }
        }
    }
}

@Composable
private fun ClassRowEditor(
    clubId: Int,
    tournamentId: Int,
    c: TournamentClassDto,
    tr: (String, String) -> String,
    api: TournamentsApi,
    scope: CoroutineScope,
    onReload: () -> Unit,
    onError: (String?) -> Unit,
) {
    var name by remember(c.id) { mutableStateOf(c.name) }
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 6.dp),
    ) {
        OutlinedTextField(value = name, onValueChange = { name = it }, modifier = Modifier.fillMaxWidth(), label = { Text(c.name) })
        Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.padding(top = 4.dp)) {
            TextButton(
                onClick = {
                    scope.launch {
                        runCatching {
                            withContext(Dispatchers.IO) {
                                api.updateTournamentClass(
                                    clubId,
                                    tournamentId,
                                    c.id,
                                    UpdateTournamentClassBody(name = name.ifBlank { null }),
                                )
                            }
                        }.onFailure { onError(it.message) }
                        onReload()
                    }
                },
            ) { Text(tr("mobile.save", "Speichern")) }
            TextButton(
                onClick = {
                    scope.launch {
                        runCatching {
                            withContext(Dispatchers.IO) {
                                api.deleteTournamentClass(clubId, tournamentId, c.id)
                            }
                        }.onFailure { onError(it.message) }
                        onReload()
                    }
                },
            ) { Text(tr("mobile.delete", "Löschen")) }
        }
        Divider()
    }
}

private fun extractPlayerName(playerElem: JsonElement?): String {
    try {
        if (playerElem == null || playerElem is JsonNull) return "-"
        val obj = playerElem.jsonObject
        // Try member
        val member = obj["member"]?.jsonObject
        if (member != null) {
            val first = member["firstName"]?.jsonPrimitive?.contentOrNull ?: ""
            val last = member["lastName"]?.jsonPrimitive?.contentOrNull ?: ""
            val name = "$first $last".trim()
            if (name.isNotBlank()) return name
        }
        // Try direct fields
        val first = obj["firstName"]?.jsonPrimitive?.contentOrNull
        val last = obj["lastName"]?.jsonPrimitive?.contentOrNull
        if (first != null || last != null) return listOfNotNull(first, last).joinToString(" ")
        // Fallback to id
        val id = obj["id"]?.jsonPrimitive?.intOrNull
        return if (id != null) "#${id}" else "-"
    } catch (e: Exception) {
        return "-"
    }
}

@Composable
internal fun TournamentEditorParticipantsTab(
    clubId: Int,
    tournamentId: Int,
    classes: List<TournamentClassDto>,
    participants: List<TournamentParticipantRowDto>,
    externalParticipants: List<TournamentExternalParticipantRowDto>,
    tournamentDate: String?,
    dependencies: AppDependencies,
    tr: (String, String) -> String,
    onReload: () -> Unit,
    onError: (String?) -> Unit,
) {
    val api = dependencies.tournamentsApi
    val scope = dependencies.applicationScope
    val membersState by dependencies.membersManager.state.collectAsState()
    
    var addMenu by remember { mutableStateOf(false) }
    var extDialog by remember { mutableStateOf(false) }
    var extFirst by remember { mutableStateOf("") }
    var extLast by remember { mutableStateOf("") }
    var importingFromTraining by remember { mutableStateOf(false) }
    var trainingImportMessage by remember { mutableStateOf<String?>(null) }

    LaunchedEffect(clubId) {
        dependencies.membersManager.loadMembers(clubId)
    }

    var showInternal by remember { mutableStateOf(true) }
    var showExternal by remember { mutableStateOf(false) }

    Column(
        modifier = Modifier
            .fillMaxWidth(),
        verticalArrangement = Arrangement.spacedBy(8.dp),
    ) {
        Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
            Text(tr("tournaments.internalParticipants", "Vereinsmitglieder"), style = MaterialTheme.typography.subtitle1, fontWeight = FontWeight.SemiBold)
            TextButton(onClick = { showInternal = !showInternal }) { Text(if (showInternal) tr("mobile.hide", "Verbergen") else tr("mobile.show", "Anzeigen")) }
        }
        if (showInternal) {
            Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                OutlinedButton(onClick = { addMenu = true }) { Text(tr("tournaments.addParticipant", "Teilnehmer hinzufügen")) }
                Button(
                    enabled = !importingFromTraining,
                    onClick = {
                        scope.launch {
                            importingFromTraining = true
                            trainingImportMessage = null
                            runCatching {
                                withContext(Dispatchers.IO) {
                                    val dateKey = normalizeTournamentDate(tournamentDate)
                                    if (dateKey.isBlank()) {
                                        return@withContext tr("tournaments.noTournamentDate", "Kein Turnierdatum vorhanden.")
                                    }
                                    val trainingDay = dependencies.diaryManager
                                        .listDates(clubId)
                                        .firstOrNull { normalizeTournamentDate(it.date) == dateKey }
                                        ?: return@withContext tr("tournaments.noTrainingDayForDate", "Kein Trainingstag für dieses Datum gefunden.")
                                    val allTrainingParticipants = dependencies.diaryManager.listTrainingParticipants(trainingDay.id)
                                    val presentTrainingParticipants = allTrainingParticipants.filter { it.attendanceStatus == "present" }
                                    if (presentTrainingParticipants.isEmpty()) {
                                        return@withContext if (allTrainingParticipants.isEmpty()) {
                                            tr("tournaments.noTrainingParticipantsForDate", "Keine Teilnehmer im Trainingstag für dieses Datum gefunden.")
                                        } else {
                                            tr("tournaments.noPresentTrainingParticipantsForDate", "Keine anwesenden Teilnehmer im Trainingstag für dieses Datum gefunden.")
                                        }
                                    }
                                    val existing = participants.mapNotNull { it.clubMemberId }.toSet()
                                    val toAdd = presentTrainingParticipants
                                        .map { it.memberId }
                                        .filter { it !in existing }
                                        .distinct()
                                    if (toAdd.isEmpty()) {
                                        return@withContext tr("tournaments.trainingParticipantsAlreadyAdded", "Alle anwesenden Trainingsteilnehmer sind bereits im Turnier.")
                                    }
                                    toAdd.forEach { memberId ->
                                        api.addInternalParticipant(
                                            TournamentAddInternalParticipantBody(
                                                clubId = clubId,
                                                classId = null,
                                                participant = memberId,
                                                tournamentId = tournamentId,
                                            ),
                                        )
                                    }
                                    tr("tournaments.trainingParticipantsLoaded", "Anwesende Trainingsteilnehmer übernommen: {count}")
                                        .replace("{count}", toAdd.size.toString())
                                }
                            }.fold(
                                onSuccess = {
                                    trainingImportMessage = it
                                    onReload()
                                },
                                onFailure = { onError(it.message) },
                            )
                            importingFromTraining = false
                        }
                    },
                ) {
                    Text(
                        if (importingFromTraining) {
                            tr("mobile.loading", "Laden...")
                        } else {
                            tr("tournaments.loadParticipantsFromTraining", "Aus Trainingstag laden")
                        },
                    )
                }
                DropdownMenu(expanded = addMenu, onDismissRequest = { addMenu = false }) {
                    val existing = participants.mapNotNull { it.clubMemberId }.toSet()
                    membersState.members
                        .filter { it.id !in existing }
                        .take(200)
                        .forEach { m ->
                            DropdownMenuItem(
                                onClick = {
                                    addMenu = false
                                    scope.launch {
                                        runCatching {
                                            withContext(Dispatchers.IO) {
                                                api.addInternalParticipant(
                                                    TournamentAddInternalParticipantBody(
                                                        clubId = clubId,
                                                        classId = null,
                                                        participant = m.id,
                                                        tournamentId = tournamentId,
                                                    ),
                                                )
                                            }
                                        }.onFailure { onError(it.message) }
                                        onReload()
                                    }
                                },
                            ) {
                                Text("${m.firstName} ${m.lastName}".trim())
                            }
                        }
                }
            }
            trainingImportMessage?.let { Text(it, style = MaterialTheme.typography.caption) }
            participants.forEach { p ->
                val label = p.member?.let { "${it.firstName.orEmpty()} ${it.lastName.orEmpty()}".trim() }.orEmpty().ifBlank { "#${p.id}" }
                Row(
                    modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp),
                    horizontalArrangement = Arrangement.SpaceBetween,
                ) {
                    Column(Modifier.weight(1f)) {
                        Text(label, fontWeight = FontWeight.Medium)
                        ClassPickerChipRow(
                            classes = classes,
                            selectedClassId = p.classId,
                            tr = tr,
                            onPick = { classId ->
                                scope.launch {
                                    runCatching {
                                        withContext(Dispatchers.IO) {
                                            api.updateParticipantClass(
                                                clubId,
                                                tournamentId,
                                                p.id,
                                                UpdateParticipantClassBody(classId = classId, isExternal = false),
                                            )
                                        }
                                    }.onFailure { onError(it.message) }
                                    onReload()
                                }
                            },
                        )
                    }
                    TextButton(
                        onClick = {
                            scope.launch {
                                runCatching {
                                    withContext(Dispatchers.IO) {
                                        api.removeInternalParticipant(
                                            TournamentRemoveInternalParticipantBody(clubId, tournamentId, p.id),
                                        )
                                    }
                                }.onFailure { onError(it.message) }
                                onReload()
                            }
                        },
                    ) { Text(tr("mobile.remove", "Entfernen")) }
                }
                Divider()
            }
        }

        Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth().padding(top = 8.dp)) {
            Text(tr("tournaments.externalParticipants", "Externe Teilnehmer"), style = MaterialTheme.typography.subtitle1, fontWeight = FontWeight.SemiBold)
            TextButton(onClick = { showExternal = !showExternal }) { Text(if (showExternal) tr("mobile.hide", "Verbergen") else tr("mobile.show", "Anzeigen")) }
        }
        if (showExternal) {
            OutlinedButton(onClick = { extDialog = true }) { Text(tr("tournaments.addExternalParticipant", "Extern hinzufügen")) }
            if (extDialog) {
                StableAlertDialog(
                    onDismissRequest = { extDialog = false },
                    title = { Text(tr("tournaments.addExternalParticipant", "Extern hinzufügen")) },
                    text = {
                        Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                            OutlinedTextField(extFirst, { extFirst = it }, label = { Text(tr("members.firstName", "Vorname")) })
                            OutlinedTextField(extLast, { extLast = it }, label = { Text(tr("members.lastName", "Nachname")) })
                        }
                    },
                    confirmButton = {
                        TextButton(
                            onClick = {
                                if (extFirst.isBlank() || extLast.isBlank()) return@TextButton
                                scope.launch {
                                    runCatching {
                                        withContext(Dispatchers.IO) {
                                            api.addExternalParticipant(
                                                AddExternalTournamentParticipantBody(
                                                    clubId = clubId,
                                                    tournamentId = tournamentId,
                                                    classId = null,
                                                    firstName = extFirst.trim(),
                                                    lastName = extLast.trim(),
                                                ),
                                            )
                                        }
                                    }.onFailure { onError(it.message) }
                                    extDialog = false
                                    extFirst = ""
                                    extLast = ""
                                    onReload()
                                }
                            },
                        ) { Text(tr("mobile.add", "Hinzufügen")) }
                    },
                    dismissButton = { TextButton(onClick = { extDialog = false }) { Text(tr("common.cancel", "Abbrechen")) } },
                )
            }
            externalParticipants.forEach { e ->
                Row(
                    modifier = Modifier.fillMaxWidth().padding(vertical = 2.dp),
                    horizontalArrangement = Arrangement.SpaceBetween,
                ) {
                    Text("${e.firstName.orEmpty()} ${e.lastName.orEmpty()} (${tr("tournaments.externalShort", "ext.")})".trim())
                    TextButton(
                        onClick = {
                            scope.launch {
                                runCatching {
                                    withContext(Dispatchers.IO) {
                                        api.removeExternalParticipant(
                                            RemoveExternalTournamentParticipantBody(clubId, tournamentId, e.id),
                                        )
                                    }
                                }.onFailure { onError(it.message) }
                                onReload()
                            }
                        },
                    ) { Text(tr("mobile.remove", "Entfernen")) }
                }
                Divider()
            }
        }
    }
}

private fun normalizeTournamentDate(date: String?): String {
    return date.orEmpty().trim().substringBefore("T")
}

@Composable
private fun ClassPickerChipRow(
    classes: List<TournamentClassDto>,
    selectedClassId: Int?,
    tr: (String, String) -> String,
    onPick: (Int?) -> Unit,
) {
    var open by remember { mutableStateOf(false) }
    val label = classes.find { it.id == selectedClassId }?.name ?: tr("tournaments.noClassSelected", "Keine Klasse")
    TextButton(onClick = { open = true }) { Text("${tr("tournaments.classLabel", "Klasse")}: $label") }
    DropdownMenu(expanded = open, onDismissRequest = { open = false }) {
        DropdownMenuItem(onClick = { open = false; onPick(null) }) { Text("—") }
        classes.forEach { c ->
            DropdownMenuItem(onClick = { open = false; onPick(c.id) }) { Text(c.name) }
        }
    }
}

@Composable
internal fun TournamentEditorMatchesTab(
    clubId: Int,
    tournamentId: Int,
    matches: List<TournamentMatchDto>,
    winningSets: Int,
    detail: InternalTournamentDetailDto,
    groupsJson: JsonElement?,
    tr: (String, String) -> String,
    api: TournamentsApi,
    scope: CoroutineScope,
    onReload: () -> Unit,
    onError: (String?) -> Unit,
) {
    
    Column(
        modifier = Modifier
            .fillMaxWidth(),
        verticalArrangement = Arrangement.spacedBy(12.dp),
    ) {
        var confirmDelete by remember { mutableStateOf<Pair<Int, Int>?>(null) }
        var editingSet by remember { mutableStateOf<Pair<Int, Int>?>(null) }
        var editInput by remember { mutableStateOf("") }
        fun isEmptyThirdPlacePlaceholder(match: TournamentMatchDto): Boolean {
            return match.round.contains("Spiel um Platz 3") &&
                match.player1Id == null &&
                match.player2Id == null &&
                match.player1 == null &&
                match.player2 == null &&
                match.result == null
        }
        val hasKO = matches.any { it.round.lowercase() != "group" && it.round.isNotBlank() && !isEmptyThirdPlacePlaceholder(it) }
        var showDistributedDialog by remember { mutableStateOf(false) }
        var distributedMatches by remember { mutableStateOf<List<TournamentMatchDto>>(emptyList()) }
        var distributedMessage by remember { mutableStateOf<String?>(null) }

        Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
            TextButton(
                onClick = {
                    scope.launch {
                        runCatching {
                            withContext(Dispatchers.IO) {
                                api.deleteKnockoutMatches(TournamentDeleteKnockoutBody(clubId, tournamentId, classId = null))
                            }
                        }.onFailure { onError(it.message) }
                        onReload()
                    }
                },
            ) { Text(tr("tournaments.resetKnockoutShort", "KO löschen")) }
            if (!hasKO) {
                TextButton(
                    onClick = {
                        scope.launch {
                            runCatching {
                                withContext(Dispatchers.IO) {
                                    api.startKnockout(TournamentStartKnockoutBody(clubId, tournamentId))
                                }
                            }.onFailure { onError(it.message) }
                            onReload()
                        }
                    },
                ) { Text(tr("tournaments.startKnockout", "K.-o. starten")) }
            }
            TextButton(onClick = {
                scope.launch {
                    runCatching {
                        val resp = withContext(Dispatchers.IO) {
                            api.distributeTables(TournamentClubTournamentBody(clubId, tournamentId))
                        }
                        if (resp.updated.isEmpty()) {
                            showDistributedDialog = true
                            distributedMatches = emptyList()
                            distributedMessage = resp.message ?: tr("tournaments.tablesDistributed", "Tische wurden verteilt.")
                        } else {
                            val firstHasPlayers = resp.updated.firstOrNull()?.player1 != null || resp.updated.firstOrNull()?.player2 != null
                            if (firstHasPlayers) {
                                distributedMatches = resp.updated
                                showDistributedDialog = true
                            } else {
                                val allMatches = withContext(Dispatchers.IO) { api.listMatches(clubId, tournamentId) }
                                val ids = resp.updated.map { it.id }.toSet()
                                distributedMatches = allMatches.filter { it.id in ids }
                                showDistributedDialog = true
                            }
                        }
                    }.onFailure { onError(it.message) }
                    onReload()
                }
            }) { Text(tr("tournaments.distributeTables", "Freie Tische verteilen")) }
        }
        // Table-like view: show header + flattened rows so Android resembles web UI
        val displayList = matches.filterNot { isEmptyThirdPlacePlaceholder(it) }.sortedWith(compareBy<TournamentMatchDto> {
            it.isFinished == true
        }.thenBy {
            // primary: if groupRound available use it, else try parse round as int, else large
            it.groupRound ?: it.round.toIntOrNull() ?: Int.MAX_VALUE
        }.thenBy { it.groupId ?: Int.MAX_VALUE }.thenBy { it.groupRound ?: 0 })

        // build group id -> sequential number map from groupsJson (order returned by backend)
        val groupNumberById = remember(groupsJson) {
            val map = mutableMapOf<Int, Int>()
            try {
                if (groupsJson is JsonArray) {
                    var idx = 1
                    for (g in groupsJson) {
                        // Backend returns groups as { groupId, classId, groupNumber, participants }
                        val id = g.jsonObject["groupId"]?.jsonPrimitive?.intOrNull
                            ?: g.jsonObject["id"]?.jsonPrimitive?.intOrNull
                        if (id != null) {
                            map[id] = idx
                            idx++
                        }
                    }
                }
            } catch (e: Exception) {
                // ignore
            }
            map
        }

        // header
        Row(modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
            Text("Runde", fontWeight = FontWeight.SemiBold, modifier = Modifier.width(56.dp))
            Text(tr("tournaments.group", "Gruppe"), fontWeight = FontWeight.SemiBold, modifier = Modifier.width(96.dp))
            Text(tr("tournaments.match", "Begegnung"), fontWeight = FontWeight.SemiBold, modifier = Modifier.weight(1f))
            Text(tr("tournaments.result", "Ergebnis"), fontWeight = FontWeight.SemiBold, modifier = Modifier.weight(0.7f))
            Text("Sätze", fontWeight = FontWeight.SemiBold, modifier = Modifier.width(72.dp))
            Text("Aktion", fontWeight = FontWeight.SemiBold, modifier = Modifier.width(96.dp))
        }

        Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
            displayList.forEach { m ->
                var resultInput by remember(m.id, m.tournamentResults) { mutableStateOf("") }
                var resultError by remember(m.id, m.tournamentResults) { mutableStateOf<String?>(null) }
                var wasResultFocused by remember(m.id) { mutableStateOf(false) }
                var isSavingResult by remember(m.id) { mutableStateOf(false) }
                fun saveResultInput() {
                    if (isSavingResult) return
                    if (resultInput.isBlank()) {
                        resultError = null
                        return
                    }
                    val normalized = normalizeResult(resultInput)
                    if (normalized == null) {
                        resultError = tr("tournaments.invalidResultInput", "Ungültiges Ergebnis")
                        onError(tr("tournaments.invalidResultInputHint", "Bitte ein gültiges Ergebnis eingeben, z.B. 11:7, 7 oder -7."))
                        return
                    }
                    val nextSet = (m.tournamentResults?.maxOfOrNull { it.set } ?: 0) + 1
                    isSavingResult = true
                    scope.launch {
                        runCatching {
                            withContext(Dispatchers.IO) {
                                api.addMatchResult(
                                    TournamentAddMatchResultBody(
                                        clubId = clubId,
                                        tournamentId = tournamentId,
                                        matchId = m.id,
                                        set = nextSet,
                                        result = normalized,
                                    ),
                                )
                            }
                        }.fold(
                            onSuccess = {
                                resultInput = ""
                                resultError = null
                                isSavingResult = false
                                onReload()
                            },
                            onFailure = {
                                resultError = it.message ?: tr("messages.error", "Fehler")
                                isSavingResult = false
                                onError(resultError)
                            },
                        )
                    }
                }
                val roundLabel = m.groupRound?.toString() ?: m.round.takeIf { it.isNotBlank() } ?: "-"
                    val groupNumber = m.groupId?.let { groupNumberById[it] }
                    val groupLabel = groupNumber?.let { "${tr("tournaments.group", "Gruppe")} $it" } ?: "-"
                val rowBg = when {
                    m.isFinished == true -> MaterialTheme.colors.onSurface.copy(alpha = 0.06f)
                    m.isActive == true -> Color(0xFFFFF3E0)
                    else -> Color.Transparent
                }

                Row(modifier = Modifier
                    .fillMaxWidth()
                    .background(rowBg)
                    .padding(vertical = 6.dp), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                    Text(roundLabel, modifier = Modifier.width(56.dp))
                    Text(groupLabel, modifier = Modifier.width(96.dp))
                    Column(modifier = Modifier.weight(1f)) {
                        Text("${displayNameFromPlayerJson(m.player1)}  –  ${displayNameFromPlayerJson(m.player2)}", style = MaterialTheme.typography.body2)
                    }
                    Column(modifier = Modifier.weight(0.7f)) {
                        if (m.isFinished == true) {
                            Text((m.result ?: "—") + " ✓")
                        } else if (m.result == "BYE") {
                            Text("BYE")
                        } else {
                            OutlinedTextField(
                                value = resultInput,
                                onValueChange = {
                                    resultInput = it
                                    resultError = null
                                },
                                placeholder = { Text(tr("tournaments.setShort", "Satz")) },
                                singleLine = true,
                                isError = resultError != null,
                                keyboardOptions = KeyboardOptions(
                                    keyboardType = KeyboardType.Text,
                                    imeAction = ImeAction.Done,
                                ),
                                keyboardActions = KeyboardActions(onDone = { saveResultInput() }),
                                modifier = Modifier
                                    .width(86.dp)
                                    .onFocusChanged { focusState ->
                                        if (wasResultFocused && !focusState.isFocused) {
                                            saveResultInput()
                                        }
                                        wasResultFocused = focusState.isFocused
                                    },
                            )
                            resultError?.let {
                                Text(it, color = MaterialTheme.colors.error, style = MaterialTheme.typography.caption)
                            }
                        }
                    }
                    Column(modifier = Modifier.width(72.dp)) {
                        val results = m.tournamentResults
                        if (!results.isNullOrEmpty()) {
                            Text(formatMatchSets(m))
                                results.sortedBy { it.set }.forEach { r ->
                                val a = r.pointsPlayer1?.let { kotlin.math.abs(it).toString() } ?: "-"
                                val b = r.pointsPlayer2?.let { kotlin.math.abs(it).toString() } ?: "-"
                                Row(verticalAlignment = Alignment.CenterVertically) {
                                    Text("${r.set}: $a:$b", style = MaterialTheme.typography.caption)
                                    if (m.isFinished != true) {
                                        Spacer(modifier = Modifier.width(6.dp))
                                        IconButton(onClick = { confirmDelete = Pair(m.id, r.set) }, modifier = Modifier.size(28.dp)) {
                                            Icon(
                                                Icons.Filled.Close,
                                                contentDescription = "Löschen",
                                                tint = MaterialTheme.colors.error,
                                                modifier = Modifier.size(18.dp),
                                            )
                                        }
                                        IconButton(onClick = {
                                            editingSet = Pair(m.id, r.set)
                                            editInput = "$a:$b"
                                        }, modifier = Modifier.size(28.dp)) {
                                            Icon(Icons.Filled.Edit, contentDescription = "Bearbeiten", modifier = Modifier.size(18.dp))
                                        }
                                    }
                                }
                            }
                        } else {
                            Text(m.tournamentResults?.size?.toString() ?: "0")
                        }
                    }
                    // Table number editor
                    Column(modifier = Modifier.width(96.dp), horizontalAlignment = Alignment.CenterHorizontally) {
                        var tableInput by remember(m.id) { mutableStateOf(m.tableNumber?.toString() ?: "") }
                        var savingTable by remember(m.id) { mutableStateOf(false) }
                        OutlinedTextField(
                            value = tableInput,
                            onValueChange = { tableInput = it.filter { ch -> ch.isDigit() || ch == '-' } },
                            singleLine = true,
                            label = { Text(tr("tournaments.tableShort", "Tisch")) },
                            modifier = Modifier.width(72.dp).onFocusChanged { fs ->
                                if (!fs.isFocused && !savingTable) {
                                    val nt = tableInput.toIntOrNull()
                                    savingTable = true
                                    scope.launch {
                                        runCatching {
                                            withContext(Dispatchers.IO) {
                                                api.setMatchTable(clubId, tournamentId, m.id, TournamentMatchTableBody(tableNumber = nt))
                                            }
                                        }.onFailure { onError(it.message) }
                                        savingTable = false
                                        onReload()
                                    }
                                }
                            },
                        )
                    }
                    // actions column
                    Column(modifier = Modifier.width(120.dp)) {
                        if (m.isFinished != true) {
                            Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
                                IconButton(onClick = {
                                    // finish
                                    scope.launch {
                                        runCatching {
                                            withContext(Dispatchers.IO) {
                                                api.finishMatch(TournamentFinishMatchBody(clubId, tournamentId, m.id))
                                            }
                                        }.onFailure { onError(it.message) }
                                        onReload()
                                    }
                                }) { Icon(Icons.Filled.Check, contentDescription = tr("tournaments.finishMatchShort", "Fertig")) }

                                if (m.isActive == true) {
                                    IconButton(onClick = {
                                        scope.launch {
                                            runCatching {
                                                withContext(Dispatchers.IO) {
                                                    api.setMatchActive(clubId, tournamentId, m.id, TournamentMatchActiveBody(isActive = false))
                                                }
                                            }.onFailure { onError(it.message) }
                                            onReload()
                                        }
                                    }) { Icon(Icons.Filled.Stop, contentDescription = tr("tournaments.stopMatch", "Stoppen")) }
                                } else {
                                    IconButton(onClick = {
                                        scope.launch {
                                            runCatching {
                                                withContext(Dispatchers.IO) {
                                                    api.setMatchActive(clubId, tournamentId, m.id, TournamentMatchActiveBody(isActive = true))
                                                }
                                            }.onFailure { onError(it.message) }
                                            onReload()
                                        }
                                    }) { Icon(Icons.Filled.PlayArrow, contentDescription = tr("tournaments.startMatch", "Starten")) }
                                }
                            }
                        } else {
                            TextButton(onClick = {
                                scope.launch {
                                    runCatching {
                                        withContext(Dispatchers.IO) {
                                            api.reopenMatch(TournamentReopenMatchBody(clubId, tournamentId, m.id))
                                        }
                                    }.onFailure { onError(it.message) }
                                    onReload()
                                }
                            }) { Text(tr("tournaments.correct", "Korrigieren")) }
                        }
                        // no duplicate stop button
                    }
                }
                Divider()
            }
        }
        // Confirmation dialog for deleting a set
        if (confirmDelete != null) {
            val (matchIdToDelete, setToDelete) = confirmDelete!!
            StableAlertDialog(
                onDismissRequest = { confirmDelete = null },
                title = { Text(tr("tournaments.confirmDeleteSetTitle", "Satz löschen")) },
                text = { Text(tr("tournaments.confirmDeleteSet", "Soll dieser Satz wirklich gelöscht werden?")) },
                confirmButton = {
                    TextButton(onClick = {
                        scope.launch {
                            runCatching {
                                withContext(Dispatchers.IO) {
                                    api.deleteMatchResult(de.tsschulz.tt_tagebuch.shared.api.models.TournamentDeleteMatchResultBody(
                                        clubId = clubId,
                                        tournamentId = tournamentId,
                                        matchId = matchIdToDelete,
                                        set = setToDelete,
                                    ))
                                }
                            }.onFailure { onError(it.message) }
                            confirmDelete = null
                            onReload()
                        }
                    }) { Text(tr("common.delete", "Löschen")) }
                },
                dismissButton = {
                    TextButton(onClick = { confirmDelete = null }) { Text(tr("common.cancel", "Abbrechen")) }
                }
            )
        }
        // Edit set dialog
        if (editingSet != null) {
            val (matchIdToEdit, setToEdit) = editingSet!!
            StableAlertDialog(
                onDismissRequest = { editingSet = null },
                title = { Text(tr("tournaments.editSetTitle", "Satz bearbeiten")) },
                text = {
                    Column {
                        OutlinedTextField(
                            value = editInput,
                            onValueChange = { editInput = it },
                            placeholder = { Text("11:7") }
                        )
                        Text(tr("tournaments.editSetHint", "Gib ein neues Ergebnis ein, z.B. 11:7"), style = MaterialTheme.typography.caption)
                    }
                },
                confirmButton = {
                    TextButton(onClick = {
                        val normalized = normalizeResult(editInput)
                        if (normalized == null) {
                            onError(tr("tournaments.invalidResultInput", "Ungültiges Ergebnis"))
                            return@TextButton
                        }
                        scope.launch {
                            runCatching {
                                withContext(Dispatchers.IO) {
                                    // Find current match results snapshot
                                    val matchObj = matches.firstOrNull { it.id == matchIdToEdit }
                                    val results = matchObj?.tournamentResults?.sortedBy { it.set } ?: emptyList()
                                    // Tail: sets after the edited one
                                    val tail = results.filter { it.set > setToEdit }.map { Pair(it.pointsPlayer1, it.pointsPlayer2) }
                                    // Delete tail from highest to lowest
                                    for (i in results.size downTo setToEdit + 1) {
                                        api.deleteMatchResult(de.tsschulz.tt_tagebuch.shared.api.models.TournamentDeleteMatchResultBody(
                                            clubId, tournamentId, matchIdToEdit, i
                                        ))
                                    }
                                    // Delete the set to edit
                                    api.deleteMatchResult(de.tsschulz.tt_tagebuch.shared.api.models.TournamentDeleteMatchResultBody(
                                        clubId, tournamentId, matchIdToEdit, setToEdit
                                    ))
                                    // Add the edited set
                                    api.addMatchResult(de.tsschulz.tt_tagebuch.shared.api.models.TournamentAddMatchResultBody(
                                        clubId, tournamentId, matchIdToEdit, setToEdit, normalized
                                    ))
                                    // Re-add tail sets in order
                                    var cur = setToEdit + 1
                                    for (t in tail) {
                                        val r = "${kotlin.math.abs(t.first ?: 0)}:${kotlin.math.abs(t.second ?: 0)}"
                                        api.addMatchResult(de.tsschulz.tt_tagebuch.shared.api.models.TournamentAddMatchResultBody(
                                            clubId, tournamentId, matchIdToEdit, cur, r
                                        ))
                                        cur++
                                    }
                                }
                            }.onFailure { onError(it.message) }
                            editingSet = null
                            editInput = ""
                            onReload()
                        }
                    }) { Text(tr("common.save", "Speichern")) }
                },
                dismissButton = {
                    TextButton(onClick = { editingSet = null }) { Text(tr("common.cancel", "Abbrechen")) }
                }
            )
        }
        if (showDistributedDialog) {
            StableAlertDialog(
                onDismissRequest = { showDistributedDialog = false },
                title = { Text(tr("tournaments.distributeTablesResult", "Tischverteilung")) },
                text = {
                    Column(Modifier.fillMaxWidth()) {
                        if (distributedMatches.isEmpty()) {
                            Text(distributedMessage ?: tr("tournaments.tablesDistributed", "Tische wurden verteilt."))
                        } else {
                            Row(Modifier.fillMaxWidth().padding(bottom = 8.dp)) {
                                Text(tr("tournaments.playerA", "Spieler A"), modifier = Modifier.weight(1f), fontWeight = FontWeight.SemiBold)
                                Text(tr("tournaments.playerB", "Spieler B"), modifier = Modifier.weight(1f), fontWeight = FontWeight.SemiBold)
                                Text(tr("tournaments.tableShort", "Tisch"), modifier = Modifier.width(48.dp), fontWeight = FontWeight.SemiBold)
                            }
                            Divider()
                            LazyColumn {
                                items(distributedMatches) { m ->
                                    val name1 = displayNameFromPlayerJson(m.player1)
                                    val name2 = displayNameFromPlayerJson(m.player2)
                                    Row(Modifier.fillMaxWidth().padding(vertical = 6.dp)) {
                                        Text(name1, modifier = Modifier.weight(1f))
                                        Text("–", modifier = Modifier.width(16.dp), textAlign = androidx.compose.ui.text.style.TextAlign.Center)
                                        Text(name2, modifier = Modifier.weight(1f))
                                        Text(m.tableNumber?.toString() ?: "-", modifier = Modifier.width(48.dp))
                                    }
                                    Divider()
                                }
                            }
                        }
                    }
                },
                confirmButton = {
                    TextButton(onClick = { showDistributedDialog = false }) { Text(tr("common.ok", "OK")) }
                },
            )
        }
    }
}

@Composable
private fun MatchResultRow(
    clubId: Int,
    tournamentId: Int,
    m: TournamentMatchDto,
    @Suppress("UNUSED_PARAMETER") winningSets: Int,
    maxTables: Int?,
    tr: (String, String) -> String,
    api: TournamentsApi,
    scope: CoroutineScope,
    onReload: () -> Unit,
    onError: (String?) -> Unit,
) {
    var resultInput by remember(m.id, m.tournamentResults) { mutableStateOf("") }
    val p1 = displayNameFromPlayerJson(m.player1)
    val p2 = displayNameFromPlayerJson(m.player2)
    Column(modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp)) {
        Text("$p1  vs  $p2", style = MaterialTheme.typography.body2)
        val setsText = m.tournamentResults?.joinToString(", ") { r ->
            val a = r.pointsPlayer1?.toString() ?: "-"
            val b = r.pointsPlayer2?.toString() ?: "-"
            "$a:$b"
        } ?: ""
        if (setsText.isNotBlank()) {
            Text(setsText, style = MaterialTheme.typography.body2)
        }
        val resultDisplay = if (!m.tournamentResults.isNullOrEmpty()) {
            formatMatchSets(m) + if (m.isFinished == true) " ✓" else ""
        } else {
            (m.result ?: "—") + if (m.isFinished == true) " ✓" else ""
        }
        Text(
            "${tr("tournaments.result", "Ergebnis")}: $resultDisplay",
            style = MaterialTheme.typography.caption,
        )
        if (m.isFinished != true) {
            Row(modifier = Modifier.padding(top = 4.dp).fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                OutlinedTextField(
                    value = resultInput,
                    onValueChange = { resultInput = it },
                    modifier = Modifier.weight(1f),
                            placeholder = { Text(tr("tournaments.setShort", "Satz")) },
                    singleLine = true,
                )
                var tableExpanded by remember { mutableStateOf(false) }
                val max = maxTables ?: 12
                Box {
                    OutlinedButton(onClick = { tableExpanded = true }, modifier = Modifier.width(84.dp)) {
                        Text(m.tableNumber?.toString() ?: "-")
                    }
                    DropdownMenu(expanded = tableExpanded, onDismissRequest = { tableExpanded = false }) {
                        for (i in 1..max) {
                            DropdownMenuItem(onClick = {
                                tableExpanded = false
                                scope.launch {
                                    runCatching {
                                        withContext(Dispatchers.IO) {
                                            api.setMatchTable(clubId, tournamentId, m.id, TournamentMatchTableBody(i))
                                        }
                                    }.onFailure { onError(it.message) }
                                    onReload()
                                }
                            }) { Text(i.toString()) }
                        }
                    }
                }
                Spacer(Modifier.weight(0.1f))
                TextButton(
                    onClick = {
                        val r = normalizeResult(resultInput) ?: return@TextButton
                        val nextSet = (m.tournamentResults?.size ?: 0) + 1
                        scope.launch {
                            runCatching {
                                withContext(Dispatchers.IO) {
                                    api.addMatchResult(
                                        TournamentAddMatchResultBody(
                                            clubId = clubId,
                                            tournamentId = tournamentId,
                                            matchId = m.id,
                                            set = nextSet,
                                            result = r,
                                        ),
                                    )
                                }
                            }.onFailure { onError(it.message) }
                            resultInput = ""
                            onReload()
                        }
                    },
                ) { Text(tr("tournaments.addSetShort", "Satz")) }
                TextButton(
                    onClick = {
                        scope.launch {
                            runCatching {
                                withContext(Dispatchers.IO) {
                                    api.finishMatch(
                                        TournamentFinishMatchBody(clubId, tournamentId, m.id),
                                    )
                                }
                            }.onFailure { onError(it.message) }
                            onReload()
                        }
                    },
                ) { Text(tr("tournaments.finishMatchShort", "Fertig")) }
            }
        } else {
            // finished -> allow reopening for correction
            Row(modifier = Modifier.padding(top = 6.dp)) {
                Text(tr("tournaments.table", "Tisch") + ": ${m.tableNumber ?: "-"}")
                Spacer(Modifier.weight(1f))
                TextButton(onClick = {
                    scope.launch {
                        runCatching {
                            withContext(Dispatchers.IO) {
                                api.reopenMatch(TournamentReopenMatchBody(clubId, tournamentId, m.id))
                            }
                        }.onFailure { onError(it.message) }
                        onReload()
                    }
                }) { Text(tr("tournaments.correct", "Korrigieren")) }
            }
        }
        Divider()
    }
}

private fun normalizeResult(s: String): String? {
    val t = s.trim()
    if (t.contains(':')) {
        val parts = t.split(':')
        if (parts.size != 2) return null
        val a = parts[0].trim().toIntOrNull() ?: return null
        val b = parts[1].trim().toIntOrNull() ?: return null
        if (a < 0 || b < 0) return null
        if ((a < 11 && b < 11) || kotlin.math.abs(a - b) < 2) return null
        return "$a:$b"
    }

    val value = t.toIntOrNull() ?: return null
    val losing = kotlin.math.abs(value)
    val winning = if (losing < 10) 11 else losing + 2
    return if (t.startsWith("-")) {
        "$losing:$winning"
    } else {
        "$winning:$losing"
    }
}

private fun formatMatchSets(match: TournamentMatchDto): String {
    val results = match.tournamentResults.orEmpty()
    if (results.isEmpty()) return match.result ?: "—"
    var p1Wins = 0
    var p2Wins = 0
    for (r in results.sortedBy { it.set }) {
        val a = r.pointsPlayer1?.let { kotlin.math.abs(it) }
        val b = r.pointsPlayer2?.let { kotlin.math.abs(it) }
        if (a != null && b != null) {
            if (a > b) p1Wins++ else if (b > a) p2Wins++
        }
    }
    return "${p1Wins}:${p2Wins}"
}

private fun displayNameFromPlayerJson(el: JsonElement?): String {
    if (el == null || el is JsonNull) return "—"
    val o = el as? JsonObject ?: return "?"
    val member = o["member"] as? JsonObject
    if (member != null) {
        val fn = member["firstName"]?.jsonPrimitive?.contentOrNull.orEmpty()
        val ln = member["lastName"]?.jsonPrimitive?.contentOrNull.orEmpty()
        return "$fn $ln".trim().ifEmpty { "?" }
    }
    val fn = o["firstName"]?.jsonPrimitive?.contentOrNull.orEmpty()
    val ln = o["lastName"]?.jsonPrimitive?.contentOrNull.orEmpty()
    return "$fn $ln".trim().ifEmpty { "?" }
}

@Composable
internal fun TournamentEditorPairingsTab(
    clubId: Int,
    tournamentId: Int,
    classes: List<TournamentClassDto>,
    participants: List<TournamentParticipantRowDto>,
    externalParticipants: List<TournamentExternalParticipantRowDto>,
    tr: (String, String) -> String,
    api: TournamentsApi,
    scope: CoroutineScope,
    onReload: () -> Unit,
    onError: (String?) -> Unit,
) {
    val doublesClasses = classes.filter { it.isDoubles == true }
    var classId by remember(doublesClasses) {
        mutableIntStateOf(doublesClasses.firstOrNull()?.id ?: 0)
    }
    var pairingJson by remember { mutableStateOf<JsonElement?>(null) }
    var p1Menu by remember { mutableStateOf(false) }
    var p2Menu by remember { mutableStateOf(false) }
    var sel1 by remember { mutableStateOf<Pair<String, Int>?>(null) }
    var sel2 by remember { mutableStateOf<Pair<String, Int>?>(null) }

    LaunchedEffect(clubId, tournamentId, classId) {
        if (classId == 0) {
            pairingJson = null
            return@LaunchedEffect
        }
        pairingJson = runCatching {
            withContext(Dispatchers.IO) { api.listPairings(clubId, tournamentId, classId) }
        }.getOrElse { null }
    }

    Column(Modifier.fillMaxWidth()) {
        if (doublesClasses.isEmpty()) {
            Text(tr("tournaments.noDoublesClasses", "Keine Doppel-Klassen."))
            return@Column
        }
        Text(tr("tournaments.selectClass", "Klasse"), fontWeight = FontWeight.SemiBold)
        doublesClasses.forEach { c ->
            TextButton(onClick = { classId = c.id }) {
                Text(if (classId == c.id) "✓ ${c.name}" else c.name)
            }
        }
        Text(tr("tournaments.newPairing", "Neue Paarung"), fontWeight = FontWeight.SemiBold, modifier = Modifier.padding(top = 12.dp))
        Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
            OutlinedButton(onClick = { p1Menu = true }) {
                Text(sel1?.let { "${it.first} #${it.second}" } ?: tr("tournaments.player1", "Spieler 1"))
            }
            DropdownMenu(expanded = p1Menu, onDismissRequest = { p1Menu = false }) {
                participants.forEach { p ->
                    val label = p.member?.let { "${it.firstName.orEmpty()} ${it.lastName.orEmpty()}".trim() }.orEmpty().ifBlank { "ID ${p.id}" }
                    DropdownMenuItem(
                        onClick = {
                            p1Menu = false
                            sel1 = "member" to p.id
                        },
                    ) { Text("$label (${tr("tournaments.internalShort", "int.")})") }
                }
                externalParticipants.forEach { ex ->
                    DropdownMenuItem(
                        onClick = {
                            p1Menu = false
                            sel1 = "external" to ex.id
                        },
                    ) { Text("${ex.firstName.orEmpty()} ${ex.lastName.orEmpty()} (${tr("tournaments.externalShort", "ext.")})") }
                }
            }
            OutlinedButton(onClick = { p2Menu = true }) {
                Text(sel2?.let { "${it.first} #${it.second}" } ?: tr("tournaments.player2", "Spieler 2"))
            }
            DropdownMenu(expanded = p2Menu, onDismissRequest = { p2Menu = false }) {
                participants.forEach { p ->
                    val label = p.member?.let { "${it.firstName.orEmpty()} ${it.lastName.orEmpty()}".trim() }.orEmpty().ifBlank { "ID ${p.id}" }
                    DropdownMenuItem(
                        onClick = {
                            p2Menu = false
                            sel2 = "member" to p.id
                        },
                    ) { Text("$label (${tr("tournaments.internalShort", "int.")})") }
                }
                externalParticipants.forEach { ex ->
                    DropdownMenuItem(
                        onClick = {
                            p2Menu = false
                            sel2 = "external" to ex.id
                        },
                    ) { Text("${ex.firstName.orEmpty()} ${ex.lastName.orEmpty()} (${tr("tournaments.externalShort", "ext.")})") }
                }
            }
        }
        Button(
            onClick = {
                val a = sel1 ?: return@Button
                val b = sel2 ?: return@Button
                if (a.second == b.second && a.first == b.first) return@Button
                scope.launch {
                    runCatching {
                        withContext(Dispatchers.IO) {
                            api.createPairing(
                                clubId,
                                tournamentId,
                                classId,
                                CreateTournamentPairingBody(
                                    player1Type = a.first,
                                    player1Id = a.second,
                                    player2Type = b.first,
                                    player2Id = b.second,
                                    seeded = false,
                                ),
                            )
                        }
                    }.onFailure { onError(it.message) }
                    sel1 = null
                    sel2 = null
                    onReload()
                }
            },
            modifier = Modifier.padding(top = 8.dp),
        ) { Text(tr("mobile.add", "Hinzufügen")) }

        Divider(modifier = Modifier.padding(vertical = 12.dp))
        Text(tr("tournaments.pairings", "Paarungen"), fontWeight = FontWeight.SemiBold)
        when (val pj = pairingJson) {
            is JsonArray -> {
                pj.forEach { elem ->
                    val id = (elem as? JsonObject)?.get("id")?.jsonPrimitive?.intOrNull ?: return@forEach
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.SpaceBetween,
                    ) {
                        Text(pairingSummary(elem), style = MaterialTheme.typography.body2)
                        TextButton(
                            onClick = {
                                scope.launch {
                                    runCatching {
                                        withContext(Dispatchers.IO) {
                                            api.deletePairing(clubId, tournamentId, id)
                                        }
                                    }.onFailure { onError(it.message) }
                                    onReload()
                                }
                            },
                        ) { Text(tr("mobile.delete", "Löschen")) }
                    }
                }
            }
            else -> Text("—", style = MaterialTheme.typography.caption)
        }
    }
}

private fun pairingSummary(elem: JsonElement): String {
    val o = elem as? JsonObject ?: return elem.toString()
    val id = o["id"]?.jsonPrimitive?.intOrNull
    val m1 = o["member1"] as? JsonObject
    val m2 = o["member2"] as? JsonObject
    val n1 = m1?.let { me ->
        val mm = me["member"] as? JsonObject
        if (mm != null) {
            "${mm["firstName"]?.jsonPrimitive?.contentOrNull.orEmpty()} ${mm["lastName"]?.jsonPrimitive?.contentOrNull.orEmpty()}".trim()
        } else {
            "${me["firstName"]?.jsonPrimitive?.contentOrNull.orEmpty()} ${me["lastName"]?.jsonPrimitive?.contentOrNull.orEmpty()}".trim()
        }
    }.orEmpty().ifBlank { "?" }
    val n2 = m2?.let { me ->
        val mm = me["member"] as? JsonObject
        if (mm != null) {
            "${mm["firstName"]?.jsonPrimitive?.contentOrNull.orEmpty()} ${mm["lastName"]?.jsonPrimitive?.contentOrNull.orEmpty()}".trim()
        } else {
            "${me["firstName"]?.jsonPrimitive?.contentOrNull.orEmpty()} ${me["lastName"]?.jsonPrimitive?.contentOrNull.orEmpty()}".trim()
        }
    }.orEmpty().ifBlank { "?" }
    return "#$id $n1 / $n2"
}
