package de.tsschulz.tt_tagebuch.app.ui

import android.content.Intent
import android.content.ClipData
import android.net.Uri
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Card
import androidx.compose.material.Checkbox
import androidx.compose.material.CircularProgressIndicator
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.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import de.tsschulz.tt_tagebuch.app.AppDependencies
import de.tsschulz.tt_tagebuch.app.stats.TrainingStatsDerived
import de.tsschulz.tt_tagebuch.shared.api.models.ScheduleMatchDto
import de.tsschulz.tt_tagebuch.shared.api.models.ScheduleMatchScope
import de.tsschulz.tt_tagebuch.shared.api.models.ScheduleViewMode
import de.tsschulz.tt_tagebuch.shared.api.models.FriendlyMatchSaveBody
import de.tsschulz.tt_tagebuch.shared.api.models.FriendlyParticipantDto
import de.tsschulz.tt_tagebuch.shared.api.models.FriendlyResultRowDto
import de.tsschulz.tt_tagebuch.shared.api.models.canReadSchedule
import de.tsschulz.tt_tagebuch.shared.api.models.canWriteSchedule
import de.tsschulz.tt_tagebuch.shared.i18n.MobileStrings
import kotlinx.coroutines.launch

private val SchedulePad = 20.dp
private val ScheduleTouchMin = 48.dp

@Composable
internal fun ScheduleScreen(dependencies: AppDependencies, friendlyOnly: Boolean = false) {
    val clubState by dependencies.clubManager.state.collectAsState()
    val scheduleState by dependencies.scheduleManager.state.collectAsState()
    val membersState by dependencies.membersManager.state.collectAsState()
    val clubId = clubState.currentClubId ?: return
    val permissions = clubState.currentPermissions
    val languageCode = LocalLanguageCode.current
    fun tr(key: String, fb: String) = MobileStrings.get(languageCode, key, fb)
    val scope = rememberCoroutineScope()
    val context = LocalContext.current
    val copyToClipboard: (String) -> Unit = { value ->
        val cm = context.getSystemService(android.content.Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
        cm.setPrimaryClip(ClipData.newPlainText("tt_tagebuch", value))
    }
    var teamMenu by remember { mutableStateOf(false) }
    var otherTeamMenu by remember { mutableStateOf(false) }
    var detailMatch by remember { mutableStateOf<ScheduleMatchDto?>(null) }
    var playerMatch by remember { mutableStateOf<ScheduleMatchDto?>(null) }
    var friendlyEditMatch by remember { mutableStateOf<ScheduleMatchDto?>(null) }
    var showFriendlyCreate by remember { mutableStateOf(false) }
    var friendlyResultMatch by remember { mutableStateOf<ScheduleMatchDto?>(null) }
    var friendlyHomeMembers by remember { mutableStateOf(emptyList<FriendlyMemberOption>()) }
    var friendlyGuestMembers by remember { mutableStateOf(emptyList<FriendlyMemberOption>()) }
    var playerError by remember { mutableStateOf<String?>(null) }
    var playerSaving by remember { mutableStateOf(false) }
    var playerDialogMembers by remember { mutableStateOf(emptyList<de.tsschulz.tt_tagebuch.shared.api.models.Member>()) }

    suspend fun loadFriendlyMembersFor(match: ScheduleMatchDto?) {
        fun de.tsschulz.tt_tagebuch.shared.api.models.Member.toOption() = FriendlyMemberOption(id, "$firstName $lastName".trim())
        if (match?.isSharedFriendly == true) {
            friendlyHomeMembers = dependencies.matchesApi.listSharedFriendlyMembers(clubId, match.id, "home").filter { it.active }.map { it.toOption() }.sortedBy { it.name.lowercase() }
            friendlyGuestMembers = dependencies.matchesApi.listSharedFriendlyMembers(clubId, match.id, "guest").filter { it.active }.map { it.toOption() }.sortedBy { it.name.lowercase() }
        } else {
            dependencies.membersManager.loadMembers(clubId)
            val options = dependencies.matchesApi.listFriendlyMembers(clubId).filter { it.active }.map { it.toOption() }.sortedBy { it.name.lowercase() }
            friendlyHomeMembers = options
            friendlyGuestMembers = emptyList()
        }
    }

    suspend fun loadPlayerDialogMembersFor(match: ScheduleMatchDto) {
        playerDialogMembers = if (match.isFriendly && match.isSharedFriendly) {
            val ownSide = if (match.homeClubId == clubId) "home" else "guest"
            dependencies.matchesApi.listSharedFriendlyMembers(clubId, match.id, ownSide)
                .filter { it.active }
                .sortedBy { "${it.firstName} ${it.lastName}".lowercase() }
        } else if (match.isFriendly) {
            dependencies.matchesApi.listFriendlyMembers(clubId).filter { it.active }.sortedBy { "${it.firstName} ${it.lastName}".lowercase() }
        } else {
            dependencies.membersManager.loadMembers(clubId)
            dependencies.membersManager.state.value.members.filter { it.active }.sortedBy { "${it.firstName} ${it.lastName}".lowercase() }
        }
    }

    var readyIds by remember { mutableStateOf(emptyList<Int>()) }
    var plannedIds by remember { mutableStateOf(emptyList<Int>()) }
    var playedIds by remember { mutableStateOf(emptyList<Int>()) }

    LaunchedEffect(playerMatch?.id) {
        val pm = playerMatch ?: return@LaunchedEffect
        readyIds = pm.playersReady
        plannedIds = pm.playersPlanned
        playedIds = pm.playersPlayed
    }

    LaunchedEffect(clubId) {
        dependencies.scheduleManager.clear()
        if (friendlyOnly) {
            dependencies.scheduleManager.loadFriendlyMatches(clubId)
        } else {
            dependencies.scheduleManager.loadClubTeams(clubId)
        }
    }

    if (permissions != null && !permissions.canReadSchedule()) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(SchedulePad)
                .imePadding()
                .navigationBarsPadding(),
        ) {
            Text(tr("schedule.noPermission", "Keine Berechtigung für den Spielplan."), style = MaterialTheme.typography.body1)
        }
        return
    }

    val matches = scheduleState.displayedMatches
    LazyColumn(
        modifier = Modifier
            .fillMaxSize()
            .imePadding()
            .navigationBarsPadding()
            .padding(horizontal = SchedulePad, vertical = 12.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp),
    ) {
        item {
            Text(if (friendlyOnly) "Freundschaftsspiele" else tr("navigation.schedule", "Terminplan"), style = MaterialTheme.typography.h5, fontWeight = FontWeight.SemiBold)
            Spacer(modifier = Modifier.height(8.dp))
            if (!friendlyOnly) Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.spacedBy(8.dp),
            ) {
                OutlinedButton(
                    onClick = { scope.launch { dependencies.scheduleManager.loadOverallSchedule(clubId) } },
                    modifier = Modifier.weight(1f).heightIn(min = ScheduleTouchMin),
                ) { Text(tr("schedule.overallSchedule", "Gesamtplan"), maxLines = 2) }
                OutlinedButton(
                    onClick = { scope.launch { dependencies.scheduleManager.loadAdultSchedule(clubId) } },
                    modifier = Modifier.weight(1f).heightIn(min = ScheduleTouchMin),
                ) { Text(tr("schedule.adultSchedule", "Erwachsene"), maxLines = 2) }
            }
            Spacer(modifier = Modifier.height(8.dp))
            if (!friendlyOnly) OutlinedButton(
                onClick = { scope.launch { dependencies.scheduleManager.loadFriendlyMatches(clubId) } },
                modifier = Modifier.fillMaxWidth().heightIn(min = ScheduleTouchMin),
            ) { Text("Freundschaftsspiele") }
            Spacer(modifier = Modifier.height(8.dp))
        }

        if (scheduleState.viewMode != ScheduleViewMode.Friendly) item {
            Box {
                OutlinedButton(
                    onClick = { teamMenu = true },
                    modifier = Modifier.fillMaxWidth().heightIn(min = ScheduleTouchMin),
                ) {
                    val label = scheduleState.selectedTeam?.let { t ->
                        val lg = t.league?.name?.takeIf { it.isNotBlank() }
                        if (lg != null) "${t.name} ($lg)" else t.name
                    } ?: tr("schedule.selectTeam", "Mannschaft wählen")
                    Text(label, maxLines = 2)
                }
                DropdownMenu(expanded = teamMenu, onDismissRequest = { teamMenu = false }) {
                    scheduleState.teams.forEach { team ->
                        DropdownMenuItem(
                            onClick = {
                                teamMenu = false
                                scope.launch { dependencies.scheduleManager.selectTeam(clubId, team.id) }
                            },
                        ) {
                            val lg = team.league?.name?.takeIf { it.isNotBlank() }
                            Text(if (lg != null) "${team.name} ($lg)" else team.name)
                        }
                    }
                }
            }
        }

        if (scheduleState.viewMode == ScheduleViewMode.Team && scheduleState.selectedTeam != null) {
            item {
                Spacer(modifier = Modifier.height(8.dp))
                Text(tr("schedule.matchScope", "Spiele anzeigen"), style = MaterialTheme.typography.caption)
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.spacedBy(6.dp),
                ) {
                    ScheduleScopeChip(
                        label = tr("schedule.ownTeamMatches", "Eigene"),
                        selected = scheduleState.matchScope == ScheduleMatchScope.Own,
                        onClick = { dependencies.scheduleManager.setMatchScope(ScheduleMatchScope.Own) },
                    )
                    ScheduleScopeChip(
                        label = tr("schedule.allLeagueMatches", "Alle"),
                        selected = scheduleState.matchScope == ScheduleMatchScope.All,
                        onClick = { dependencies.scheduleManager.setMatchScope(ScheduleMatchScope.All) },
                    )
                    ScheduleScopeChip(
                        label = tr("schedule.otherTeamMatches", "Andere"),
                        selected = scheduleState.matchScope == ScheduleMatchScope.Other,
                        onClick = { dependencies.scheduleManager.setMatchScope(ScheduleMatchScope.Other) },
                    )
                }
            }
            if (scheduleState.matchScope == ScheduleMatchScope.Other) {
                item {
                    Box(modifier = Modifier.fillMaxWidth().padding(top = 6.dp)) {
                        OutlinedButton(
                            onClick = { otherTeamMenu = true },
                            modifier = Modifier.fillMaxWidth().heightIn(min = ScheduleTouchMin),
                        ) {
                            Text(
                                scheduleState.otherTeamName.ifBlank { tr("schedule.selectOtherTeam", "Mannschaft wählen") },
                                maxLines = 2,
                            )
                        }
                        DropdownMenu(expanded = otherTeamMenu, onDismissRequest = { otherTeamMenu = false }) {
                            scheduleState.leagueTeamOptions.forEach { name ->
                                DropdownMenuItem(
                                    onClick = {
                                        otherTeamMenu = false
                                        dependencies.scheduleManager.setOtherTeamName(name)
                                    },
                                ) { Text(name) }
                            }
                        }
                    }
                }
            }
        }

        item {
            Spacer(modifier = Modifier.height(8.dp))
            Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                OutlinedButton(
                    onClick = { scope.launch { dependencies.scheduleManager.refresh(clubId) } },
                    modifier = Modifier.weight(1f).heightIn(min = ScheduleTouchMin),
                ) { Text(tr("mobile.refresh", "Aktualisieren")) }
                if (scheduleState.viewMode == ScheduleViewMode.Friendly && permissions?.canWriteSchedule() == true) {
                    OutlinedButton(
                        onClick = { showFriendlyCreate = true },
                        modifier = Modifier.weight(1f).heightIn(min = ScheduleTouchMin),
                    ) { Text("Neu") }
                }
            }

            if (scheduleState.isLoading) {
                Row(
                    Modifier
                        .fillMaxWidth()
                        .padding(12.dp),
                    horizontalArrangement = Arrangement.Center,
                ) { CircularProgressIndicator() }
            }
            scheduleState.error?.let {
                Text(it, color = MaterialTheme.colors.error, modifier = Modifier.padding(vertical = 8.dp))
            }
        }

        if (scheduleState.viewMode == ScheduleViewMode.Team && scheduleState.leagueTable.isNotEmpty()) {
            item {
                Text(
                    tr("schedule.leagueTable", "Tabelle"),
                    style = MaterialTheme.typography.subtitle1,
                    fontWeight = FontWeight.SemiBold,
                    modifier = Modifier.padding(top = 12.dp),
                )
                Card(modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp), elevation = 1.dp) {
                    Column(modifier = Modifier.padding(8.dp)) {
                        Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
                            Text("#", fontWeight = FontWeight.Bold, modifier = Modifier.widthIn(28.dp))
                            Text(tr("schedule.team", "Team"), fontWeight = FontWeight.Bold, modifier = Modifier.weight(1f))
                            Text(tr("schedule.points", "Pkt"), fontWeight = FontWeight.Bold, modifier = Modifier.widthIn(40.dp))
                        }
                        Divider()
                        scheduleState.leagueTable.forEachIndexed { idx, row ->
                            Row(
                                Modifier
                                    .fillMaxWidth()
                                    .padding(vertical = 4.dp),
                                horizontalArrangement = Arrangement.SpaceBetween,
                            ) {
                                Text("${idx + 1}", modifier = Modifier.widthIn(28.dp))
                                Text(row.teamName, modifier = Modifier.weight(1f), maxLines = 2)
                                Text(row.tablePoints, modifier = Modifier.widthIn(40.dp))
                            }
                        }
                    }
                }
            }
        }

        item {
            Text(
                tr("schedule.games", "Spiele"),
                style = MaterialTheme.typography.subtitle1,
                fontWeight = FontWeight.SemiBold,
                modifier = Modifier.padding(top = 12.dp),
            )
        }

        if (matches.isEmpty() && !scheduleState.isLoading) {
            item { Text(tr("schedule.noGames", "Keine Spiele"), modifier = Modifier.padding(top = 8.dp)) }
        } else {
            items(matches, key = { it.id }) { m ->
                ScheduleMatchCard(
                    match = m,
                    highlightClubName = clubState.clubs.find { it.id == clubId }?.name.orEmpty(),
                    showLeagueColumn = scheduleState.viewMode != ScheduleViewMode.Team,
                    onClick = { detailMatch = m },
                )
            }
        }
    }

    detailMatch?.let { m ->
        StableAlertDialog(
            onDismissRequest = { detailMatch = null },
            title = { Text("${m.homeTeam?.name ?: "?"} : ${m.guestTeam?.name ?: "?"}") },
            text = {
                val scroll = rememberScrollState()
                Column(Modifier.verticalScroll(scroll)) {
                    Text("${TrainingStatsDerived.formatDateGerman(m.date)} · ${TrainingStatsDerived.weekdayGerman(m.date ?: "")}")
                    Text(
                        tr("schedule.time", "Zeit") + ": " + (m.time?.take(5)?.takeIf { it.isNotBlank() } ?: "—"),
                    )
                    if (m.isCompleted) {
                        Text(
                            tr("schedule.result", "Ergebnis") + ": ${m.homeMatchPoints}:${m.guestMatchPoints}",
                            fontWeight = FontWeight.SemiBold,
                        )
                    }
                    val loc = m.location
                    loc?.name?.takeIf { it.isNotBlank() && it != "Unbekannt" }?.let { hallName ->
                        Text(tr("schedule.location", "Halle") + ": $hallName")
                        val addr = listOfNotNull(
                            loc.address.takeIf { a -> a.isNotBlank() },
                            listOfNotNull(loc.zip.takeIf { z -> z.isNotBlank() }, loc.city.takeIf { c -> c.isNotBlank() })
                                .joinToString(" ")
                                .takeIf { s -> s.isNotBlank() },
                        ).joinToString(", ")
                        if (addr.isNotBlank()) Text(addr, style = MaterialTheme.typography.caption)
                    }
                    m.leagueDetails?.name?.takeIf { it.isNotBlank() }?.let {
                        Text(tr("schedule.ageClass", "Liga") + ": $it", style = MaterialTheme.typography.caption)
                    }
                    m.code?.takeIf { it.isNotBlank() }?.let { code ->
                        TextButton(onClick = { copyToClipboard(code) }) {
                            Text(tr("schedule.code", "Code") + ": $code")
                        }
                    }
                    Row {
                        m.homePin?.takeIf { it.isNotBlank() }?.let { pin ->
                            TextButton(onClick = { copyToClipboard(pin) }) {
                                Text(tr("schedule.homePin", "Heim-PIN") + ": $pin")
                            }
                        }
                        m.guestPin?.takeIf { it.isNotBlank() }?.let { pin ->
                            TextButton(onClick = { copyToClipboard(pin) }) {
                                Text(tr("schedule.guestPin", "Gast-PIN") + ": $pin")
                            }
                        }
                    }
                    m.pdfUrl?.takeIf { it.startsWith("http") }?.let { url ->
                        TextButton(
                            onClick = {
                                runCatching {
                                    context.startActivity(
                                        Intent(Intent.ACTION_VIEW, Uri.parse(url)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
                                    )
                                }
                            },
                        ) { Text(tr("schedule.openMatchReport", "Bericht öffnen")) }
                    }
                    if (permissions?.canWriteSchedule() == true) {
                        if (m.isFriendly) {
                            TextButton(
                                onClick = {
                                    playerError = null
                                    playerMatch = m
                                    detailMatch = null
                                },
                            ) {
                                Text(tr("schedule.players", "Aufstellung / Spieler"))
                            }
                            TextButton(
                                onClick = {
                                    friendlyResultMatch = m
                                    detailMatch = null
                                },
                            ) { Text(if (isFriendlyMatchLocked(m)) "Ansehen" else "Ergebnis") }
                            TextButton(
                                onClick = {
                                    friendlyEditMatch = m
                                    detailMatch = null
                                },
                            ) { Text(if (isFriendlyMatchLocked(m)) "Details" else "Bearbeiten") }
                        } else {
                            TextButton(
                                onClick = {
                                    playerError = null
                                    playerMatch = m
                                    detailMatch = null
                                },
                            ) {
                                Text(tr("schedule.players", "Aufstellung / Spieler"))
                            }
                        }
                    }
                }
            },
            confirmButton = {
                TextButton(onClick = { detailMatch = null }) { Text(tr("common.close", "Schließen")) }
            },
        )
    }

    if (showFriendlyCreate || friendlyEditMatch != null) {
        LaunchedEffect(friendlyEditMatch?.id, showFriendlyCreate) {
            loadFriendlyMembersFor(friendlyEditMatch)
        }
        FriendlyMatchEditDialog(
            match = friendlyEditMatch,
            clubName = clubState.clubs.find { it.id == clubId }?.name.orEmpty(),
            homeMemberOptions = friendlyFilteredMembers(friendlyEditMatch, friendlyHomeMembers),
            guestMemberOptions = friendlyFilteredMembers(friendlyEditMatch, friendlyGuestMembers),
            readonly = isFriendlyMatchLocked(friendlyEditMatch),
            onLoadMembers = { scope.launch { loadFriendlyMembersFor(friendlyEditMatch) } },
            onDismiss = {
                showFriendlyCreate = false
                friendlyEditMatch = null
            },
            onSave = { body ->
                scope.launch {
                    if (friendlyEditMatch != null) {
                        if (isFriendlyMatchLocked(friendlyEditMatch)) return@launch
                        dependencies.scheduleManager.updateFriendlyMatch(clubId, friendlyEditMatch!!.id, body)
                    } else {
                        dependencies.scheduleManager.createFriendlyMatch(clubId, body)
                    }
                    showFriendlyCreate = false
                    friendlyEditMatch = null
                }
            },
            onDelete = if (friendlyEditMatch != null) {
                {
                    scope.launch {
                        if (isFriendlyMatchLocked(friendlyEditMatch)) return@launch
                        dependencies.scheduleManager.deleteFriendlyMatch(clubId, friendlyEditMatch!!.id)
                        friendlyEditMatch = null
                    }
                }
            } else null,
        )
    }

    friendlyResultMatch?.let { match ->
        LaunchedEffect(match.id) { loadFriendlyMembersFor(match) }
        FriendlyResultDialog(
            match = match,
            memberOptions = friendlyHomeMembers + friendlyGuestMembers,
            readonly = isFriendlyMatchLocked(match),
            onDismiss = { friendlyResultMatch = null },
            onSave = { body ->
                scope.launch {
                    if (isFriendlyMatchLocked(match)) return@launch
                    dependencies.scheduleManager.updateFriendlyMatch(clubId, match.id, body)
                    val updated = body.toMatchLike(match)
                    friendlyResultMatch = updated
                }
            },
            onComplete = { body ->
                scope.launch {
                    if (isFriendlyMatchLocked(match)) return@launch
                    dependencies.scheduleManager.updateFriendlyMatch(clubId, match.id, body.copy(isCompleted = true))
                    friendlyResultMatch = null
                }
            },
        )
    }

    playerMatch?.let { m ->
        LaunchedEffect(m.id, clubId) {
            loadPlayerDialogMembersFor(m)
        }
        val dialogHeight = (LocalConfiguration.current.screenHeightDp * 0.82f).dp
        Dialog(onDismissRequest = { if (!playerSaving) playerMatch = null }) {
            Card(
                modifier = Modifier
                    .fillMaxWidth()
                    .widthIn(max = 560.dp)
                    .height(dialogHeight),
                elevation = 8.dp,
            ) {
                Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
                    Text(
                        tr("schedule.playerSelectionTitle", "Spieler"),
                        style = MaterialTheme.typography.h6,
                        fontWeight = FontWeight.SemiBold,
                    )
                    Spacer(modifier = Modifier.height(8.dp))
                    playerError?.let {
                        Text(it, color = MaterialTheme.colors.error, modifier = Modifier.padding(bottom = 8.dp))
                    }
                    val memberList = playerDialogMembers
                    if (membersState.isLoading && memberList.isEmpty()) {
                        Box(
                            modifier = Modifier.fillMaxWidth().weight(1f),
                            contentAlignment = Alignment.Center,
                        ) {
                            CircularProgressIndicator()
                        }
                    } else {
                        LazyColumn(
                            modifier = Modifier.fillMaxWidth().weight(1f),
                        ) {
                            items(memberList, key = { it.id }) { mem ->
                                val id = mem.id
                                Row(
                                    verticalAlignment = Alignment.CenterVertically,
                                    modifier = Modifier.fillMaxWidth().heightIn(min = 48.dp),
                                ) {
                                    Column(Modifier.weight(1f)) {
                                        Text("${mem.firstName} ${mem.lastName}".trim(), maxLines = 1)
                                    }
                                    Text("R", style = MaterialTheme.typography.caption)
                                    Checkbox(
                                        checked = id in readyIds,
                                        onCheckedChange = { c ->
                                            readyIds = if (c) (readyIds + id).distinct() else readyIds.filter { it != id }
                                        },
                                    )
                                    Text("P", style = MaterialTheme.typography.caption)
                                    Checkbox(
                                        checked = id in plannedIds,
                                        onCheckedChange = { c ->
                                            plannedIds = if (c) (plannedIds + id).distinct() else plannedIds.filter { it != id }
                                        },
                                    )
                                    Text("S", style = MaterialTheme.typography.caption)
                                    Checkbox(
                                        checked = id in playedIds,
                                        onCheckedChange = { c ->
                                            playedIds = if (c) (playedIds + id).distinct() else playedIds.filter { it != id }
                                        },
                                    )
                                }
                                Divider()
                            }
                        }
                    }
                    Spacer(modifier = Modifier.height(12.dp))
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.End,
                        verticalAlignment = Alignment.CenterVertically,
                    ) {
                        TextButton(enabled = !playerSaving, onClick = { playerMatch = null }) {
                            Text(tr("common.cancel", "Abbrechen"))
                        }
                        TextButton(
                            enabled = !playerSaving && !isFriendlyMatchLocked(m),
                            onClick = {
                                scope.launch {
                                    playerSaving = true
                                    playerError = null
                                    runCatching {
                                        val visibleIds = playerDialogMembers.map { it.id }.toSet()
                                        fun mergeVisible(existing: List<Int>, selected: List<Int>): List<Int> =
                                            (existing.filter { it !in visibleIds } + selected.filter { it in visibleIds }).distinct()
                                        dependencies.scheduleManager.updateMatchPlayersForMatch(
                                            clubId = clubId,
                                            match = m,
                                            ready = mergeVisible(m.playersReady, readyIds),
                                            planned = mergeVisible(m.playersPlanned, plannedIds),
                                            played = mergeVisible(m.playersPlayed, playedIds),
                                        )
                                        playerMatch = null
                                    }.onFailure { playerError = it.message ?: tr("schedule.errorSavingPlayerSelection", "Speichern fehlgeschlagen") }
                                    playerSaving = false
                                }
                            },
                        ) { Text(if (playerSaving) "…" else tr("common.save", "Speichern")) }
                    }
                }
            }
        }
    }
}

private data class FriendlyMemberOption(val id: Int, val name: String)

@Composable
private fun FriendlyMatchEditDialog(
    match: ScheduleMatchDto?,
    clubName: String,
    homeMemberOptions: List<FriendlyMemberOption>,
    guestMemberOptions: List<FriendlyMemberOption>,
    readonly: Boolean,
    onLoadMembers: () -> Unit,
    onDismiss: () -> Unit,
    onSave: (FriendlyMatchSaveBody) -> Unit,
    onDelete: (() -> Unit)?,
) {
    LaunchedEffect(Unit) { onLoadMembers() }
    var date by remember(match?.id) { mutableStateOf(match?.date?.take(10) ?: todayIsoDate()) }
    var time by remember(match?.id) { mutableStateOf(match?.time?.take(5) ?: "") }
    var homeTeam by remember(match?.id) { mutableStateOf(match?.homeTeam?.name ?: clubName) }
    var guestTeam by remember(match?.id) { mutableStateOf(match?.guestTeam?.name ?: "") }
    var matchSystem by remember(match?.id) { mutableStateOf(match?.matchSystem ?: "Braunschweiger System") }
    var winningSetsText by remember(match?.id) { mutableStateOf((match?.winningSets ?: 3).toString()) }
    var homeParticipants by remember(match?.id) { mutableStateOf(match?.homeParticipants ?: emptyList()) }
    var guestParticipants by remember(match?.id) { mutableStateOf(match?.guestParticipants ?: emptyList()) }
    var resultRows by remember(match?.id) { mutableStateOf(match?.resultDetails ?: emptyList()) }
    var error by remember { mutableStateOf<String?>(null) }

    fun doubleRows(): List<FriendlyResultRowDto> {
        val template = friendlyResultTemplate(matchSystem, homeParticipants.size, guestParticipants.size, match?.doublesCount ?: 4, match?.singlesCount ?: 12)
        val doubleIds = template.filter { it.type == "double" }.map { it.id }
        val existingById = resultRows.filter { it.id.isNotBlank() }.associateBy { it.id }
        val singles = resultRows.filter { it.type != "double" }
        val normalized = doubleIds.map { id ->
            val row = existingById[id] ?: FriendlyResultRowDto(id = id, type = "double", sets = List(5) { "" })
            row.copy(id = id, type = "double", sets = List(5) { row.sets.getOrNull(it).orEmpty() })
        }
        resultRows = normalized + singles
        return normalized
    }

    StableAlertDialog(
        onDismissRequest = onDismiss,
        title = { Text(if (match == null) "Freundschaftsspiel anlegen" else "Freundschaftsspiel bearbeiten") },
        text = {
            Column(Modifier.verticalScroll(rememberScrollState())) {
                error?.let { Text(it, color = MaterialTheme.colors.error) }
                LocalizedDatePickerField(date, { date = it }, label = "Datum", enabled = !readonly, modifier = Modifier.fillMaxWidth())
                OutlinedTextField(time, { time = it }, label = { Text("Uhrzeit") }, enabled = !readonly, modifier = Modifier.fillMaxWidth())
                OutlinedTextField(homeTeam, { homeTeam = it }, label = { Text("Heimteam") }, enabled = !readonly, modifier = Modifier.fillMaxWidth())
                OutlinedTextField(guestTeam, { guestTeam = it }, label = { Text("Gastteam") }, enabled = !readonly, modifier = Modifier.fillMaxWidth())
                OutlinedTextField(matchSystem, { matchSystem = it }, label = { Text("Spielsystem") }, enabled = !readonly, modifier = Modifier.fillMaxWidth())
                OutlinedTextField(winningSetsText, { winningSetsText = it.filter(Char::isDigit) }, label = { Text("Gewinnsätze") }, enabled = !readonly, modifier = Modifier.fillMaxWidth())
                Spacer(Modifier.height(8.dp))
                FriendlyParticipantEditor("Heim-Aufstellung", homeMemberOptions, homeParticipants, allowMembers = true, allowManual = false, readonly = readonly) { homeParticipants = it }
                Spacer(Modifier.height(8.dp))
                FriendlyParticipantEditor("Gast-Aufstellung", guestMemberOptions, guestParticipants, allowMembers = guestMemberOptions.isNotEmpty(), allowManual = guestMemberOptions.isEmpty(), readonly = readonly) { guestParticipants = it }
                val doubles = doubleRows()
                if (doubles.isNotEmpty()) {
                    Spacer(Modifier.height(8.dp))
                    Text("Doppel", fontWeight = FontWeight.SemiBold)
                    doubles.forEachIndexed { index, row ->
                        Card(Modifier.fillMaxWidth().padding(vertical = 4.dp), elevation = 1.dp) {
                            Column(Modifier.padding(8.dp)) {
                                Text("Doppel ${index + 1}", fontWeight = FontWeight.SemiBold)
                                FriendlyResultPlayerSelector(
                                    label = "Heim",
                                    row = row,
                                    fieldValue = row.homeName,
                                    sideLabels = friendlyResultSideLabels(homeParticipants, homeMemberOptions),
                                    rows = doubles,
                                    readonly = readonly,
                                    onChange = { value -> resultRows = resultRows.map { if (it.id == row.id) it.copy(homeName = value) else it } },
                                )
                                FriendlyResultPlayerSelector(
                                    label = "Gast",
                                    row = row,
                                    fieldValue = row.guestName,
                                    sideLabels = friendlyResultSideLabels(guestParticipants, guestMemberOptions),
                                    rows = doubles,
                                    readonly = readonly,
                                    onChange = { value -> resultRows = resultRows.map { if (it.id == row.id) it.copy(guestName = value) else it } },
                                )
                            }
                        }
                    }
                }
            }
        },
        confirmButton = {
            if (!readonly) TextButton(
                onClick = {
                    val winningSets = winningSetsText.toIntOrNull()?.takeIf { it > 0 } ?: 3
                    if (date.isBlank() || homeTeam.isBlank() || guestTeam.isBlank()) {
                        error = "Datum, Heimteam und Gastteam sind Pflichtfelder."
                        return@TextButton
                    }
                    onSave(
                        FriendlyMatchSaveBody(
                            date = date.trim(),
                            time = time.trim().ifBlank { null },
                            homeTeamName = homeTeam.trim(),
                            guestTeamName = guestTeam.trim(),
                            matchSystem = matchSystem.trim().ifBlank { "Braunschweiger System" },
                            winningSets = winningSets,
                            homeParticipants = homeParticipants,
                            guestParticipants = guestParticipants,
                            homeMatchPoints = match?.homeMatchPoints ?: 0,
                            guestMatchPoints = match?.guestMatchPoints ?: 0,
                            isCompleted = match?.isCompleted ?: false,
                            resultDetails = resultRows,
                        ),
                    )
                },
            ) { Text("Speichern") }
        },
        dismissButton = {
            Row {
                if (!readonly) onDelete?.let {
                    TextButton(onClick = it) { Text("Löschen") }
                }
                TextButton(onClick = onDismiss) { Text("Abbrechen") }
            }
        },
    )
}

@Composable
private fun FriendlyParticipantEditor(
    title: String,
    members: List<FriendlyMemberOption>,
    participants: List<FriendlyParticipantDto>,
    allowMembers: Boolean = true,
    allowManual: Boolean = true,
    readonly: Boolean = false,
    onChange: (List<FriendlyParticipantDto>) -> Unit,
) {
    var menuOpen by remember { mutableStateOf(false) }
    var manualName by remember { mutableStateOf("") }
    Column {
        Text(title, fontWeight = FontWeight.SemiBold)
        if (!readonly && allowMembers) Box(Modifier.fillMaxWidth()) {
            OutlinedButton(onClick = { menuOpen = true }, modifier = Modifier.fillMaxWidth()) {
                Text("Mitglied hinzufügen")
            }
            DropdownMenu(expanded = menuOpen, onDismissRequest = { menuOpen = false }) {
                members.filter { member -> participants.none { it.type == "member" && it.memberId == member.id } }.forEach { member ->
                    DropdownMenuItem(
                        onClick = {
                            menuOpen = false
                            if (participants.none { it.type == "member" && it.memberId == member.id }) {
                                onChange(participants + FriendlyParticipantDto(type = "member", memberId = member.id))
                            }
                        },
                    ) { Text(member.name) }
                }
            }
        }
        if (!readonly && allowManual) Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) {
            OutlinedTextField(
                manualName,
                { manualName = it },
                label = { Text("Manueller Name") },
                modifier = Modifier.weight(1f),
            )
            OutlinedButton(
                onClick = {
                    val parts = manualName.trim().split(Regex("\\s+")).filter { it.isNotBlank() }
                    if (parts.isNotEmpty()) {
                        onChange(participants + FriendlyParticipantDto(type = "manual", firstName = parts.first(), lastName = parts.drop(1).joinToString(" ")))
                        manualName = ""
                    }
                },
                modifier = Modifier.heightIn(min = ScheduleTouchMin),
            ) { Text("+") }
        }
        participants.forEachIndexed { index, participant ->
            Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
                Text(participantLabel(participant, members), modifier = Modifier.weight(1f))
                if (!readonly) TextButton(onClick = { onChange(participants.filterIndexed { i, _ -> i != index }) }) { Text("x") }
            }
        }
    }
}

@Composable
private fun FriendlyResultDialog(
    match: ScheduleMatchDto,
    memberOptions: List<FriendlyMemberOption>,
    readonly: Boolean,
    onDismiss: () -> Unit,
    onSave: (FriendlyMatchSaveBody) -> Unit,
    onComplete: (FriendlyMatchSaveBody) -> Unit,
) {
    var rows by remember(match.id) { mutableStateOf(buildFriendlyResultRows(match, memberOptions)) }
    var error by remember { mutableStateOf<String?>(null) }
    val score = friendlyScore(rows, match.winningSets)

    fun body(completed: Boolean = match.isCompleted): FriendlyMatchSaveBody =
        FriendlyMatchSaveBody(
            date = match.date?.take(10) ?: java.time.LocalDate.now().toString(),
            time = match.time?.take(5),
            homeTeamName = match.homeTeam?.name ?: "",
            guestTeamName = match.guestTeam?.name ?: "",
            matchSystem = match.matchSystem ?: "Braunschweiger System",
            winningSets = match.winningSets,
            homeParticipants = match.homeParticipants,
            guestParticipants = match.guestParticipants,
            homeMatchPoints = score.first,
            guestMatchPoints = score.second,
            isCompleted = completed,
            resultDetails = rows,
        )

    StableAlertDialog(
        onDismissRequest = onDismiss,
        title = { Text("Ergebniseingabe") },
        text = {
            Column(Modifier.verticalScroll(rememberScrollState())) {
                Text("${match.homeTeam?.name ?: "?"} : ${match.guestTeam?.name ?: "?"}")
                Text("Spielstand: ${score.first}:${score.second}", fontWeight = FontWeight.SemiBold)
                error?.let { Text(it, color = MaterialTheme.colors.error) }
                rows.forEachIndexed { index, row ->
                    Card(Modifier.fillMaxWidth().padding(vertical = 4.dp), elevation = 1.dp) {
                        Column(Modifier.padding(8.dp)) {
                            Text("${index + 1}. ${if (row.type == "double") "Doppel" else "Einzel"}", fontWeight = FontWeight.SemiBold)
                            Text("Heim: ${row.homeName.ifBlank { "-" }}", style = MaterialTheme.typography.caption)
                            Text("Gast: ${row.guestName.ifBlank { "-" }}", style = MaterialTheme.typography.caption)
                            Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
                                val state = friendlyRowState(row, match.winningSets)
                                (0 until 5).forEach { setIndex ->
                                    val disabled = state.second != null && setIndex > state.second!!
                                    OutlinedTextField(
                                        value = row.sets.getOrNull(setIndex).orEmpty(),
                                        onValueChange = { value ->
                                            val nextSets = row.sets.toMutableList().also {
                                                while (it.size < 5) it.add("")
                                                it[setIndex] = value
                                            }
                                            rows = rows.toMutableList().also { list ->
                                                list[index] = row.copy(sets = nextSets)
                                            }
                                        },
                                        label = { Text("${setIndex + 1}") },
                                        enabled = !readonly && !disabled,
                                        modifier = Modifier.weight(1f),
                                        singleLine = true,
                                    )
                                }
                            }
                            if (!readonly) OutlinedButton(
                                onClick = {
                                    val normalized = normalizeFriendlyRow(row, match.winningSets)
                                    if (normalized == null) {
                                        error = "Bitte gültige Sätze eingeben."
                                    } else {
                                        error = null
                                        val nextRows = rows.toMutableList().also { it[index] = normalized }
                                        rows = nextRows
                                        onSave(body(false).copy(resultDetails = nextRows, homeMatchPoints = friendlyScore(nextRows, match.winningSets).first, guestMatchPoints = friendlyScore(nextRows, match.winningSets).second))
                                    }
                                },
                                modifier = Modifier.fillMaxWidth(),
                            ) { Text(if (row.completed) "Gespeichert / abgeschlossen" else "Satz speichern") }
                        }
                    }
                }
            }
        },
        confirmButton = { if (!readonly) TextButton(onClick = { onComplete(body(true)) }) { Text("Abschließen") } },
        dismissButton = { TextButton(onClick = onDismiss) { Text("Schließen") } },
    )
}

@Composable
private fun FriendlyResultPlayerSelector(
    label: String,
    row: FriendlyResultRowDto,
    fieldValue: String,
    sideLabels: List<String>,
    rows: List<FriendlyResultRowDto>,
    readonly: Boolean,
    onChange: (String) -> Unit,
) {
    if (row.type == "double") {
        Column {
            FriendlyResultNameMenu(
                label = "$label 1",
                value = friendlyDoublePart(fieldValue, 0),
                options = friendlyResultNameOptions(sideLabels, rows, row, label, fieldValue, 0),
                readonly = readonly,
                onChange = { value -> onChange(friendlySetDoublePart(fieldValue, 0, value)) },
            )
            FriendlyResultNameMenu(
                label = "$label 2",
                value = friendlyDoublePart(fieldValue, 1),
                options = friendlyResultNameOptions(sideLabels, rows, row, label, fieldValue, 1),
                readonly = readonly,
                onChange = { value -> onChange(friendlySetDoublePart(fieldValue, 1, value)) },
            )
        }
    } else {
        FriendlyResultNameMenu(
            label = label,
            value = fieldValue,
            options = friendlyResultNameOptions(sideLabels, rows, row, label, fieldValue, null),
            readonly = readonly,
            onChange = onChange,
        )
    }
}

@Composable
private fun FriendlyResultNameMenu(
    label: String,
    value: String,
    options: List<String>,
    readonly: Boolean,
    onChange: (String) -> Unit,
) {
    var open by remember { mutableStateOf(false) }
    Box(Modifier.fillMaxWidth()) {
        OutlinedButton(
            enabled = !readonly,
            onClick = { open = true },
            modifier = Modifier.fillMaxWidth(),
        ) { Text(value.ifBlank { label }) }
        DropdownMenu(expanded = open, onDismissRequest = { open = false }) {
            DropdownMenuItem(onClick = { open = false; onChange("") }) { Text("-") }
            options.forEach { option ->
                DropdownMenuItem(onClick = { open = false; onChange(option) }) { Text(option) }
            }
        }
    }
}

private fun friendlyResultSideLabels(participants: List<FriendlyParticipantDto>, members: List<FriendlyMemberOption>): List<String> =
    participants.map { participantLabel(it, members) }.filter { it.isNotBlank() }

private fun friendlyDoublePart(value: String, index: Int): String =
    value.split("/").map { it.trim() }.getOrNull(index).orEmpty()

private fun friendlySetDoublePart(value: String, index: Int, part: String): String {
    val parts = mutableListOf(friendlyDoublePart(value, 0), friendlyDoublePart(value, 1))
    parts[index] = part
    return parts.filter { it.isNotBlank() }.joinToString(" / ")
}

private fun friendlyResultNameOptions(
    labels: List<String>,
    rows: List<FriendlyResultRowDto>,
    currentRow: FriendlyResultRowDto,
    label: String,
    currentValue: String,
    doubleIndex: Int?,
): List<String> {
    val fieldSelector: (FriendlyResultRowDto) -> String = if (label.startsWith("Heim")) { row -> row.homeName } else { row -> row.guestName }
    val used = mutableSetOf<String>()
    rows.filter { it != currentRow }.forEach { row ->
        val value = fieldSelector(row)
        if (row.type == "double") {
            friendlyDoublePart(value, 0).takeIf { it.isNotBlank() }?.let { used.add(it) }
            friendlyDoublePart(value, 1).takeIf { it.isNotBlank() }?.let { used.add(it) }
        } else if (value.isNotBlank()) {
            used.add(value)
        }
    }
    val current = if (doubleIndex == null) currentValue else friendlyDoublePart(currentValue, doubleIndex)
    if (doubleIndex != null) {
        friendlyDoublePart(currentValue, if (doubleIndex == 0) 1 else 0).takeIf { it.isNotBlank() }?.let { used.add(it) }
    }
    return labels.filter { it == current || it !in used }
}

private fun isFriendlyMatchLocked(match: ScheduleMatchDto?): Boolean {
    if (match?.isFriendly != true) return false
    if (match.isLocked) return true
    val date = match.date?.take(10) ?: return false
    return runCatching {
        val endsAt = java.time.LocalDate.parse(date).atTime(23, 59, 59)
        !endsAt.isAfter(java.time.LocalDateTime.now())
    }.getOrDefault(false)
}

private fun friendlyEligibleMemberIds(match: ScheduleMatchDto?): Set<Int>? {
    fun ids(value: List<Int>) = value.toSet()
    val ready = ids(match?.playersReady ?: emptyList())
    val planned = ids(match?.playersPlanned ?: emptyList())
    if (ready.isEmpty() || planned.isEmpty()) return null
    return ready.intersect(planned)
}

private fun friendlyFilteredMembers(match: ScheduleMatchDto?, members: List<FriendlyMemberOption>): List<FriendlyMemberOption> {
    val eligible = friendlyEligibleMemberIds(match) ?: return members
    return members.filter { it.id in eligible }
}

private fun FriendlyMatchSaveBody.toMatchLike(match: ScheduleMatchDto): ScheduleMatchDto =
    match.copy(
        homeMatchPoints = homeMatchPoints,
        guestMatchPoints = guestMatchPoints,
        isCompleted = isCompleted,
        resultDetails = resultDetails,
    )

private fun participantLabel(participant: FriendlyParticipantDto, members: List<FriendlyMemberOption>): String {
    if (participant.type == "member") {
        return members.find { it.id == participant.memberId }?.name ?: "Mitglied #${participant.memberId ?: 0}"
    }
    return listOf(participant.firstName, participant.lastName).filter { it.isNotBlank() }.joinToString(" ")
}

private data class FriendlyResultTemplateRow(
    val id: String,
    val type: String,
    val home: String,
    val guest: String,
)

private fun friendlyResultTemplate(
    matchSystem: String?,
    homeCount: Int,
    guestCount: Int,
    fallbackDoublesCount: Int,
    fallbackSinglesCount: Int,
): List<FriendlyResultTemplateRow> {
    val system = matchSystem.orEmpty().trim().lowercase()
    fun d(id: String, home: String, guest: String) = FriendlyResultTemplateRow(id, "double", home, guest)
    fun s(id: String, home: String, guest: String) = FriendlyResultTemplateRow(id, "single", home, guest)
    if ("bundessystem" in system) return listOf(
        d("d-1", "D1", "D1"), d("d-2", "D2", "D2"),
        s("s-1", "A1", "B1"), s("s-2", "A2", "B2"), s("s-3", "A3", "B3"), s("s-4", "A4", "B4"),
        s("s-5", "A1", "B2"), s("s-6", "A2", "B1"), s("s-7", "A3", "B4"), s("s-8", "A4", "B3"),
    )
    if ("werner" in system) return listOf(
        d("d-1", "D1", "D1"), d("d-2", "D2", "D2"),
        s("s-1", "A1", "B2"), s("s-2", "A2", "B1"), s("s-3", "A3", "B4"), s("s-4", "A4", "B3"),
        s("s-5", "A1", "B1"), s("s-6", "A2", "B2"), s("s-7", "A3", "B3"), s("s-8", "A4", "B4"),
    )
    if ("sechser" in system) return listOf(
        d("d-1", "D1", "D1"), d("d-2", "D2", "D2"), d("d-3", "D3", "D3"),
        s("s-1", "A1", "B2"), s("s-2", "A2", "B1"), s("s-3", "A1", "B1"), s("s-4", "A2", "B2"),
        s("s-5", "A3", "B4"), s("s-6", "A4", "B3"), s("s-7", "A3", "B3"), s("s-8", "A4", "B4"),
        s("s-9", "A5", "B6"), s("s-10", "A6", "B5"), s("s-11", "A5", "B5"), s("s-12", "A6", "B6"),
        d("d-4", "D1", "D1"),
    )
    if ("europaliga" in system) return listOf(
        d("d-1", "D1", "D1"), d("d-2", "D2", "D2"), d("d-3", "D3", "D3"),
        s("s-1", "A1", "B1"), s("s-2", "A2", "B2"), s("s-3", "A1", "B2"), s("s-4", "A2", "B1"),
        s("s-5", "A3", "B3"), s("s-6", "A4", "B4"), s("s-7", "A3", "B4"), s("s-8", "A4", "B3"),
        s("s-9", "A5", "B5"), s("s-10", "A6", "B6"), s("s-11", "A5", "B6"), s("s-12", "A6", "B5"),
    )
    if ("corbillon" in system) return listOf(
        s("s-1", "A1", "B1"), s("s-2", "A2", "B2"), d("d-1", "D1", "D1"), s("s-3", "A1", "B2"), s("s-4", "A2", "B1"),
    )
    if ("modifiziertes swaythling" in system) return listOf(
        s("s-1", "A1", "B2"), s("s-2", "A2", "B1"), s("s-3", "A3", "B3"), d("d-1", "D1", "D1"),
        s("s-4", "A1", "B1"), s("s-5", "A3", "B2"), s("s-6", "A2", "B3"),
    )
    if ("swaythling" in system) return listOf(
        s("s-1", "A1", "B1"), s("s-2", "A2", "B2"), s("s-3", "A3", "B3"), s("s-4", "A1", "B2"), s("s-5", "A2", "B1"),
    )
    if ("braunschweiger" in system) {
        if (homeCount >= 4 && guestCount >= 4) return listOf(
            d("d-1", "D1", "D1"), d("d-2", "D2", "D2"),
            s("s-1", "A1", "B1"), s("s-2", "A2", "B2"), s("s-3", "A3", "B3"), s("s-4", "A4", "B4"),
            s("s-5", "A1", "B2"), s("s-6", "A2", "B1"), s("s-7", "A3", "B4"), s("s-8", "A4", "B3"),
        )
        if (homeCount >= 4 && guestCount <= 3) return listOf(
            d("d-1", "D1", "D1"),
            s("s-1", "A3", "B3"), s("s-2", "A1", "B2"), s("s-3", "A2", "B1"), s("s-4", "A4", "B2"),
            s("s-5", "A1", "B1"), s("s-6", "A4", "B3"), s("s-7", "A2", "B2"), s("s-8", "A1", "B3"), s("s-9", "A3", "B1"),
        )
        if (homeCount <= 3 && guestCount >= 4) return listOf(
            d("d-1", "D1", "D1"),
            s("s-1", "A3", "B3"), s("s-2", "A2", "B1"), s("s-3", "A1", "B2"), s("s-4", "A2", "B4"),
            s("s-5", "A1", "B1"), s("s-6", "A3", "B4"), s("s-7", "A2", "B2"), s("s-8", "A3", "B1"), s("s-9", "A1", "B3"),
        )
        return listOf(
            d("d-1", "D1", "D1"),
            s("s-1", "A1", "B2"), s("s-2", "A2", "B1"), s("s-3", "A3", "B2"), s("s-4", "A2", "B3"),
            s("s-5", "A1", "B1"), s("s-6", "A3", "B3"), s("s-7", "A2", "B2"), s("s-8", "A3", "B1"), s("s-9", "A1", "B3"),
        )
    }
    val homeSlots = homeCount.coerceAtLeast(1)
    val guestSlots = guestCount.coerceAtLeast(1)
    return buildList {
        repeat(fallbackDoublesCount.coerceAtLeast(0)) { add(d("d-${it + 1}", "D${it + 1}", "D${it + 1}")) }
        repeat(fallbackSinglesCount.coerceAtLeast(0)) { add(s("s-${it + 1}", "A${(it % homeSlots) + 1}", "B${(it % guestSlots) + 1}")) }
    }
}

private fun buildFriendlyResultRows(match: ScheduleMatchDto, members: List<FriendlyMemberOption>): List<FriendlyResultRowDto> {
    val existing = match.resultDetails
    val generated = generateFriendlyResultRows(match, members)
    if (existing.isEmpty()) return generated

    val existingById = existing.filter { it.id.isNotBlank() }.associateBy { it.id }
    return generated.mapIndexed { index, generatedRow ->
        val existingRow = existingById[generatedRow.id] ?: existing.getOrNull(index)
        if (existingRow == null) {
            generatedRow
        } else {
            generatedRow.copy(
                sets = List(5) { existingRow.sets.getOrNull(it) ?: generatedRow.sets.getOrNull(it).orEmpty() },
                completed = existingRow.completed,
            )
        }
    }
}

private fun generateFriendlyResultRows(match: ScheduleMatchDto, members: List<FriendlyMemberOption>): List<FriendlyResultRowDto> {
    val home = match.homeParticipants.map { participantLabel(it, members) }.filter { it.isNotBlank() }
    val guest = match.guestParticipants.map { participantLabel(it, members) }.filter { it.isNotBlank() }
    fun player(list: List<String>, code: String): String {
        val number = Regex("[AB](\\d+)", RegexOption.IGNORE_CASE).find(code)?.groupValues?.getOrNull(1)?.toIntOrNull() ?: return ""
        return list.getOrNull(number - 1).orEmpty()
    }
    fun fallbackDouble(list: List<String>, index: Int): String {
        if (list.isEmpty()) return ""
        if (list.size == 1) return list.first()
        val pairs = listOf(0 to 1, 2 to 3, 0 to 2, 1 to 3)
        val pair = pairs.getOrElse(index) { (index * 2) to (index * 2 + 1) }
        val a = list[pair.first % list.size]
        val b = list[pair.second % list.size]
        return if (a == b) a else "$a / $b"
    }
    fun doubleName(list: List<String>, side: String, code: String): String {
        val number = Regex("D[A-Z]?(\\d+)", RegexOption.IGNORE_CASE).find(code)?.groupValues?.getOrNull(1)?.toIntOrNull() ?: 1
        val existing = match.resultDetails.find { it.id == "d-$number" }
        val value = if (side == "guest") existing?.guestName else existing?.homeName
        return value?.takeIf { it.isNotBlank() } ?: fallbackDouble(list, number - 1)
    }
    return friendlyResultTemplate(match.matchSystem, home.size, guest.size, match.doublesCount, match.singlesCount).map { row ->
        FriendlyResultRowDto(
            id = row.id,
            type = row.type,
            homeName = if (row.type == "double") doubleName(home, "home", row.home) else player(home, row.home),
            guestName = if (row.type == "double") doubleName(guest, "guest", row.guest) else player(guest, row.guest),
            sets = List(5) { "" },
        )
    }
}

private fun normalizeFriendlySet(value: String): String? {
    val raw = value.trim()
    if (raw.isBlank()) return ""
    if (":" in raw) {
        val parts = raw.split(":")
        if (parts.size != 2) return null
        val a = parts[0].toIntOrNull() ?: return null
        val b = parts[1].toIntOrNull() ?: return null
        if (a < 0 || b < 0 || kotlin.math.max(a, b) < 11 || kotlin.math.abs(a - b) < 2) return null
        return "$a:$b"
    }
    val losing = raw.removePrefix("-").toIntOrNull() ?: return null
    val winning = if (losing < 10) 11 else losing + 2
    return if (raw.startsWith("-")) "$losing:$winning" else "$winning:$losing"
}

private fun friendlyRowState(row: FriendlyResultRowDto, winningSets: Int): Pair<String?, Int?> {
    var home = 0
    var guest = 0
    val required = winningSets.coerceAtLeast(1)
    row.sets.forEachIndexed { index, set ->
        val normalized = normalizeFriendlySet(set) ?: return@forEachIndexed
        if (normalized.isBlank()) return@forEachIndexed
        val parts = normalized.split(":").mapNotNull { it.toIntOrNull() }
        if (parts.size == 2) {
            if (parts[0] > parts[1]) home += 1 else guest += 1
            if (home >= required || guest >= required) return (if (home > guest) "home" else "guest") to index
        }
    }
    return null to null
}

private fun normalizeFriendlyRow(row: FriendlyResultRowDto, winningSets: Int): FriendlyResultRowDto? {
    val normalized = row.sets.map { normalizeFriendlySet(it) ?: return null }.toMutableList()
    while (normalized.size < 5) normalized.add("")
    val temp = row.copy(sets = normalized)
    val state = friendlyRowState(temp, winningSets)
    if (state.first != null && state.second != null) {
        for (i in (state.second!! + 1) until normalized.size) normalized[i] = ""
    }
    return row.copy(sets = normalized, completed = state.first != null)
}

private fun friendlyScore(rows: List<FriendlyResultRowDto>, winningSets: Int): Pair<Int, Int> {
    var home = 0
    var guest = 0
    rows.forEach { row ->
        when (friendlyRowState(row, winningSets).first) {
            "home" -> home += 1
            "guest" -> guest += 1
        }
    }
    return home to guest
}

@Composable
private fun ScheduleScopeChip(label: String, selected: Boolean, onClick: () -> Unit) {
    OutlinedButton(
        onClick = onClick,
        modifier = Modifier.heightIn(min = 40.dp),
    ) {
        Text(label, fontWeight = if (selected) FontWeight.Bold else FontWeight.Normal, maxLines = 1)
    }
}

@Composable
private fun ScheduleMatchCard(
    match: ScheduleMatchDto,
    highlightClubName: String,
    showLeagueColumn: Boolean,
    onClick: () -> Unit,
) {
    val homeH = highlightClubName.isNotBlank() && match.homeTeam?.name?.contains(highlightClubName) == true
    val guestH = highlightClubName.isNotBlank() && match.guestTeam?.name?.contains(highlightClubName) == true
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .clickable(onClick = onClick),
        elevation = 1.dp,
    ) {
        Column(modifier = Modifier.padding(12.dp)) {
            Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
                Text(
                    TrainingStatsDerived.formatDateGerman(match.date),
                    fontWeight = FontWeight.SemiBold,
                )
                Text(match.time?.take(5) ?: "—", style = MaterialTheme.typography.caption)
            }
            Text(
                "${match.homeTeam?.name ?: "?"} : ${match.guestTeam?.name ?: "?"}",
                color = when {
                    homeH || guestH -> MaterialTheme.colors.primary
                    else -> MaterialTheme.colors.onSurface
                },
            )
            if (showLeagueColumn) {
                Text(match.leagueDetails?.name ?: "", style = MaterialTheme.typography.caption)
            }
            Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
                if (match.isCompleted) {
                    Text("${match.homeMatchPoints}:${match.guestMatchPoints}", fontWeight = FontWeight.Bold)
                } else {
                    Text("—", color = MaterialTheme.colors.onSurface.copy(alpha = 0.5f))
                }
            }
        }
    }
}
