package de.tsschulz.tt_tagebuch.app.ui

import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
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.defaultMinSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
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.Surface
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.mutableStateMapOf
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.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
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.util.OfficialTournamentEligibility
import de.tsschulz.tt_tagebuch.shared.api.models.OfficialParsedCompetitionDto
import de.tsschulz.tt_tagebuch.shared.api.models.OfficialParsedTournamentEnvelopeDto
import de.tsschulz.tt_tagebuch.shared.api.models.OfficialPatchTournamentBody
import de.tsschulz.tt_tagebuch.shared.api.models.OfficialParticipantStatusBody
import de.tsschulz.tt_tagebuch.shared.api.models.OfficialTournamentListRowDto
import de.tsschulz.tt_tagebuch.shared.api.models.OfficialUpsertParticipationBody
import de.tsschulz.tt_tagebuch.shared.api.models.Member
import de.tsschulz.tt_tagebuch.shared.api.models.canReadTournaments
import de.tsschulz.tt_tagebuch.shared.api.models.canWriteTournaments
import de.tsschulz.tt_tagebuch.shared.i18n.MobileStrings
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext


private enum class WorkspaceTab { Overview, Competitions, Results }

private val OfficialPanelBorder = Color(0xFFDBE3F0)
private val OfficialPanelTitle = Color(0xFF17366D)
private val OfficialPanelMuted = Color(0xFF64748B)
private val OfficialRowStripe = Color(0xFFF8FAFC)
private val OfficialSelectedBg = Color(0xFFEEF4FF)
private val OfficialSelectedBorder = Color(0xFFD1DEFD)

private val OfficialParticipationRangeOrder = listOf(
    OfficialParticipationRange.M3,
    OfficialParticipationRange.M6,
    OfficialParticipationRange.M12,
    OfficialParticipationRange.Y2,
    OfficialParticipationRange.PREV,
    OfficialParticipationRange.ALL,
)

private fun participationRangeLabel(r: OfficialParticipationRange, tr: (String, String) -> String): String =
    when (r) {
        OfficialParticipationRange.M3 -> tr("officialTournaments.last3Months", "Letzte 3 Monate")
        OfficialParticipationRange.M6 -> tr("officialTournaments.last6Months", "Letzte 6 Monate")
        OfficialParticipationRange.M12 -> tr("officialTournaments.last12Months", "Letzte 12 Monate")
        OfficialParticipationRange.Y2 -> tr("officialTournaments.last2Years", "Letzte 2 Jahre")
        OfficialParticipationRange.PREV -> tr("officialTournaments.previousSeason", "Vorsaison")
        OfficialParticipationRange.ALL -> tr("officialTournaments.all", "Alle")
    }

@Composable
private fun OfficialWorkspaceCard(
    modifier: Modifier = Modifier.fillMaxWidth(),
    content: @Composable ColumnScope.() -> Unit,
) {
    Surface(
        modifier = modifier,
        shape = RoundedCornerShape(12.dp),
        color = MaterialTheme.colors.surface,
        elevation = 2.dp,
        border = BorderStroke(1.dp, OfficialPanelBorder),
    ) {
        Column(
            Modifier.padding(horizontal = 16.dp, vertical = 14.dp),
            verticalArrangement = Arrangement.spacedBy(10.dp),
            content = content,
        )
    }
}

@Composable
private fun ColumnScope.OfficialPanelHeader(title: String, subtitle: String) {
    Text(
        title,
        style = MaterialTheme.typography.subtitle1,
        fontWeight = FontWeight.SemiBold,
        color = OfficialPanelTitle,
    )
    Text(
        subtitle,
        style = MaterialTheme.typography.body2,
        color = OfficialPanelMuted,
    )
}

@Composable
private fun TournamentListStatusBadge(st: TournamentListStatus) {
    val colors = when (st.className) {
        "is-active" -> Triple(Color(0xFFE8F1FF), Color(0xFF2453A6), Color(0xFFBFD2FF))
        "is-upcoming" -> Triple(Color(0xFFE6F6EA), Color(0xFF1E6B34), Color(0xFFB9E2C4))
        "is-past" -> Triple(Color(0xFFF2F4F7), Color(0xFF556070), Color(0xFFD8DEE8))
        else -> Triple(Color(0xFFFFF4DB), Color(0xFF8A5A00), Color(0xFFF2D08A))
    }
    Surface(
        shape = RoundedCornerShape(999.dp),
        color = colors.first,
        border = BorderStroke(1.dp, colors.third),
    ) {
        Text(
            st.label,
            modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp),
            style = MaterialTheme.typography.caption,
            fontWeight = FontWeight.SemiBold,
            color = colors.second,
        )
    }
}

@Composable
internal fun OfficialTournamentsWorkspaceScreen(
    clubId: Int,
    canWrite: Boolean,
    dependencies: AppDependencies,
    tr: (String, String) -> String,
    onClose: () -> Unit,
) {
    val scope = rememberCoroutineScope()
    val officialState by dependencies.officialTournamentsReadManager.state.collectAsState()
    val membersState by dependencies.membersManager.state.collectAsState()

    var selectedTournamentId by remember { mutableStateOf<Int?>(null) }
    var parsed by remember { mutableStateOf<OfficialParsedTournamentEnvelopeDto?>(null) }
    var parsedLoading by remember { mutableStateOf(false) }
    var parsedError by remember { mutableStateOf<String?>(null) }
    val participationMap = remember { mutableStateMapOf<String, ParticipationState>() }

    var tournamentSearch by remember { mutableStateOf("") }
    var showOlderTournaments by remember { mutableStateOf(false) }
    var editingTournamentId by remember { mutableStateOf<Int?>(null) }
    var editingTitle by remember { mutableStateOf("") }

    var showHistory by remember { mutableStateOf(true) }
    var participationRange by remember { mutableStateOf(OfficialParticipationRange.ALL) }
    var clubHistoryRows by remember { mutableStateOf<List<ClubHistoryRow>>(emptyList()) }
    var loadingClubParticipations by remember { mutableStateOf(false) }

    var pickedPdfBytes by remember { mutableStateOf<ByteArray?>(null) }
    var pickedPdfName by remember { mutableStateOf("") }
    var pdfUploading by remember { mutableStateOf(false) }

    var activeTab by remember { mutableStateOf(WorkspaceTab.Overview) }
    var participantsFilter by remember { mutableStateOf("all") }
    val expandedCompetitionKeys = remember { mutableStateMapOf<String, Boolean>() }

    var showMemberDialog by remember { mutableStateOf(false) }
    var selectedMemberIds by remember { mutableStateOf(setOf<Int>()) }
    var memberRecommendations by remember { mutableStateOf(mapOf<Int, Set<String>>()) }
    var dialogMemberId by remember { mutableStateOf<Int?>(null) }

    var busyAuto by remember { mutableStateOf(false) }
    var infoMessage by remember { mutableStateOf<String?>(null) }
    var infoTitle by remember { mutableStateOf("") }
    var showInfoDialog by remember { mutableStateOf(false) }

    var batchAction by remember { mutableStateOf<String?>(null) }
    var deleteTarget by remember { mutableStateOf<OfficialTournamentListRowDto?>(null) }

    fun memberNameById(id: Int): String {
        val m = membersState.members.find { it.id == id }
        return if (m != null) "${m.firstName} ${m.lastName}" else "#$id"
    }

    fun mapSnapshot(): Map<String, ParticipationState> = participationMap.toMap()

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

    LaunchedEffect(clubId, participationRange) {
        loadingClubParticipations = true
        runCatching {
            val buckets = withContext(Dispatchers.IO) {
                dependencies.officialTournamentsApi.listParticipationSummary(clubId)
            }
            clubHistoryRows = computeClubHistoryRows(buckets, participationRange)
        }
        loadingClubParticipations = false
    }

    LaunchedEffect(clubId, selectedTournamentId) {
        val id = selectedTournamentId ?: run {
            parsed = null
            parsedError = null
            return@LaunchedEffect
        }
        parsedLoading = true
        parsedError = null
        runCatching {
            dependencies.officialTournamentsApi.getParsed(clubId, id)
        }.onSuccess { parsed = it }
            .onFailure {
                parsed = null
                parsedError = it.message
            }
        parsedLoading = false
    }

    LaunchedEffect(parsed) {
        participationMap.clear()
        val p = parsed ?: return@LaunchedEffect
        for ((k, v) in buildParticipationMapFromApi(p.participation)) {
            participationMap[k] = v
        }
    }

    suspend fun reloadParsed(): OfficialParsedTournamentEnvelopeDto? {
        val id = selectedTournamentId ?: return null
        return withContext(Dispatchers.IO) {
            dependencies.officialTournamentsApi.getParsed(clubId, id)
        }.also { p ->
            parsed = p
        }
    }

    fun showInfo(title: String, message: String) {
        infoTitle = title
        infoMessage = message
        showInfoDialog = true
    }

    val filteredList = remember(officialState.tournaments, tournamentSearch, showOlderTournaments, selectedTournamentId) {
        filterTournamentList(officialState.tournaments, tournamentSearch, showOlderTournaments, selectedTournamentId)
    }

    val activeMembers = remember(membersState.members) {
        membersState.members.filter { it.active }.sortedWith { a, b -> OfficialTournamentEligibility.compareMembers(a, b) }
    }

    val androidContext = LocalContext.current
    val screenWide = LocalConfiguration.current.screenWidthDp >= 640
    val pickPdf = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri ->
        if (uri == null) return@rememberLauncherForActivityResult
        scope.launch {
            runCatching {
                withContext(Dispatchers.IO) {
                    val bytes = androidContext.contentResolver.openInputStream(uri)?.use { it.readBytes() }
                    val name = uri.lastPathSegment?.substringAfterLast(':')?.ifBlank { null }
                        ?: uri.lastPathSegment?.substringAfterLast('/')?.ifBlank { null }
                        ?: "turnier.pdf"
                    bytes to name
                }
            }.onSuccess { (bytes, name) ->
                if (bytes != null) {
                    pickedPdfBytes = bytes
                    pickedPdfName = name
                }
            }.onFailure { showInfo(tr("common.error", "Fehler"), it.message ?: "") }
        }
    }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState())
            .padding(horizontal = 16.dp, vertical = 12.dp),
        verticalArrangement = Arrangement.spacedBy(12.dp),
    ) {
        TextButton(onClick = onClose) { Text(tr("mobile.back", "Zurück")) }

        val adminTopRow = canWrite && screenWide
        val savedBlock: @Composable ColumnScope.() -> Unit = {
            OfficialTournamentSavedEventsBlock(
                tr = tr,
                officialStateLoading = officialState.isLoading,
                filteredList = filteredList,
                tournamentSearch = tournamentSearch,
                onTournamentSearchChange = { tournamentSearch = it },
                showOlderTournaments = showOlderTournaments,
                onShowOlderChange = { showOlderTournaments = it },
                totalTournaments = officialState.tournaments.size,
                selectedTournamentId = selectedTournamentId,
                onSelectTournament = { selectedTournamentId = it },
                onTabReset = { activeTab = WorkspaceTab.Overview },
                editingTournamentId = editingTournamentId,
                editingTitle = editingTitle,
                onEditingTitleChange = { editingTitle = it },
                onStartTitleEdit = { t ->
                    editingTournamentId = t.id
                    editingTitle = t.title ?: ""
                },
                onCancelTitleEdit = { editingTournamentId = null },
                canWrite = canWrite,
                onDeleteRequest = { deleteTarget = it },
                clubId = clubId,
                scope = scope,
                dependencies = dependencies,
                reloadParsed = ::reloadParsed,
                showInfo = ::showInfo,
            )
        }
        val importBlock: @Composable ColumnScope.() -> Unit = {
            OfficialTournamentImportBlock(
                tr = tr,
                pdfUploading = pdfUploading,
                pickedPdfBytes = pickedPdfBytes,
                pickedPdfName = pickedPdfName,
                onPickPdf = { pickPdf.launch("application/pdf") },
                onUpload = {
                    pickedPdfBytes?.let { bytes ->
                    scope.launch {
                        pdfUploading = true
                        runCatching {
                            val res = withContext(Dispatchers.IO) {
                                dependencies.officialTournamentsApi.uploadPdf(
                                    clubId,
                                    bytes,
                                    pickedPdfName.ifBlank { "turnier.pdf" },
                                )
                            }
                            val newId = res.id.toIntOrNull() ?: res.id.toDoubleOrNull()?.toInt()
                            if (newId != null) {
                                pickedPdfBytes = null
                                pickedPdfName = ""
                                dependencies.officialTournamentsReadManager.load(clubId)
                                selectedTournamentId = newId
                                activeTab = WorkspaceTab.Overview
                                showInfo(
                                    tr("common.success", "Erfolg"),
                                    tr("officialTournaments.uploadOk", "Turnier importiert."),
                                )
                            } else {
                                showInfo(tr("common.error", "Fehler"), tr("officialTournaments.uploadBadId", "Ungültige Antwort vom Server."))
                            }
                        }.onFailure { showInfo(tr("common.error", "Fehler"), it.message ?: "") }
                        pdfUploading = false
                    }
                    }
                },
            )
        }

        if (adminTopRow) {
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.spacedBy(12.dp),
            ) {
                Box(Modifier.weight(1f)) {
                    OfficialWorkspaceCard(Modifier.fillMaxWidth(), content = importBlock)
                }
                Box(Modifier.weight(1f)) {
                    OfficialWorkspaceCard(Modifier.fillMaxWidth(), content = savedBlock)
                }
            }
        } else {
            if (canWrite) {
                OfficialWorkspaceCard(content = importBlock)
            }
            OfficialWorkspaceCard(content = savedBlock)
        }

        OfficialWorkspaceCard {
            Row(
                Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceBetween,
                verticalAlignment = Alignment.CenterVertically,
            ) {
                Column(Modifier.weight(1f)) {
                    Text(
                        tr("officialTournaments.participations", "Turnierbeteiligungen"),
                        style = MaterialTheme.typography.subtitle1,
                        fontWeight = FontWeight.SemiBold,
                        color = OfficialPanelTitle,
                    )
                    Text(
                        historySummaryText(loadingClubParticipations, clubHistoryRows, participationRange),
                        style = MaterialTheme.typography.caption,
                        color = OfficialPanelMuted,
                    )
                }
                OutlinedButton(onClick = { showHistory = !showHistory }) {
                    Text(if (showHistory) tr("officialTournaments.hideHistory", "Ausblenden") else tr("officialTournaments.showHistory", "Einblenden"))
                }
            }
            if (showHistory) {
                var participationRangeMenuOpen by remember { mutableStateOf(false) }
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    verticalAlignment = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.spacedBy(12.dp),
                ) {
                    Text(
                        tr("officialTournaments.timeRange", "Zeitraum"),
                        style = MaterialTheme.typography.body2,
                        color = OfficialPanelMuted,
                    )
                    Box {
                        OutlinedButton(onClick = { participationRangeMenuOpen = true }) {
                            Text(participationRangeLabel(participationRange, tr))
                        }
                        DropdownMenu(
                            expanded = participationRangeMenuOpen,
                            onDismissRequest = { participationRangeMenuOpen = false },
                        ) {
                            OfficialParticipationRangeOrder.forEach { r ->
                                DropdownMenuItem(
                                    onClick = {
                                        participationRange = r
                                        participationRangeMenuOpen = false
                                    },
                                ) {
                                    Text(participationRangeLabel(r, tr))
                                }
                            }
                        }
                    }
                }
                if (loadingClubParticipations) {
                    CircularProgressIndicator(Modifier.padding(8.dp))
                } else {
                    HistoryTable(clubHistoryRows, tr)
                }
            }
        }

        val p = parsed
        val tid = selectedTournamentId
        if (p == null || tid == null || p.parsedData?.competitions.isNullOrEmpty()) {
            OfficialWorkspaceCard {
                if (parsedLoading) {
                    CircularProgressIndicator(Modifier.padding(8.dp))
                }
                parsedError?.let { Text(it, color = MaterialTheme.colors.error) }
                if (!parsedLoading && tid == null) {
                    Text(tr("officialTournaments.noSelectionTitle", "Kein aktives Turnier ausgewählt"), fontWeight = FontWeight.SemiBold, color = OfficialPanelTitle)
                    Text(
                        tr("officialTournaments.noSelectionBody", "Wähle oben ein gespeichertes Turnier aus."),
                        style = MaterialTheme.typography.body2,
                        color = OfficialPanelMuted,
                    )
                }
                if (!parsedLoading && tid != null && (p == null || p.parsedData?.competitions.isNullOrEmpty())) {
                    Text(
                        tr("officialTournaments.noParsedData", "Für dieses Turnier liegen keine Konkurrenzen vor oder die Daten konnten nicht geladen werden."),
                        style = MaterialTheme.typography.body2,
                        color = OfficialPanelMuted,
                    )
                }
            }
        } else {
            OfficialWorkspaceCard {
            val pd = p.parsedData!!
            val notices = workflowNotices(mapSnapshot())
            val (regSum, partSum) = registrationSummary(mapSnapshot())
            val pending = pendingAutoRegistrationCount(mapSnapshot())
            val groupsCount = participantsGroups(participantsRows(p, mapSnapshot(), "all") { memberNameById(it) }).size

            Text(tr("officialTournaments.activeTournament", "Aktives Turnier"), style = MaterialTheme.typography.overline)
            Text(pd.title ?: "–", fontWeight = FontWeight.Bold)
            Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.horizontalScroll(rememberScrollState())) {
                Text(pd.termin ?: "–", style = MaterialTheme.typography.caption)
                val loc = tournamentLocationLabel(pd)
                if (loc.isNotBlank()) Text(loc, style = MaterialTheme.typography.caption)
                val rdi = registrationDeadlineInfo(pd)
                Text(rdi.label, style = MaterialTheme.typography.caption, color = MaterialTheme.colors.primary)
            }
            Row(Modifier.horizontalScroll(rememberScrollState()), horizontalArrangement = Arrangement.spacedBy(6.dp)) {
                notices.forEach { n ->
                    Text(n.label, style = MaterialTheme.typography.caption, fontWeight = FontWeight.Medium, modifier = Modifier.padding(end = 6.dp))
                }
            }
            Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                StatMini(tr("officialTournaments.statPending", "Offene Meldungen"), pending.toString())
                StatMini(tr("officialTournaments.statRegistered", "Angemeldet"), regSum.toString())
                StatMini(tr("officialTournaments.statPlayed", "Teilgenommen"), partSum.toString())
                StatMini(tr("officialTournaments.statMembers", "Mitglieder (Übersicht)"), groupsCount.toString())
            }

            val primary = primaryHeaderAction(mapSnapshot())
            Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(6.dp)) {
                if (primary == "auto-register" && canWrite) {
                    Button(
                        onClick = {
                            scope.launch {
                                if (!canAutoRegister(tid, mapSnapshot(), pd)) return@launch
                                busyAuto = true
                                runCatching {
                                    val res = withContext(Dispatchers.IO) {
                                        dependencies.officialTournamentsApi.autoRegister(clubId, tid)
                                    }
                                    if (isAutoRegisterSuccess(res)) {
                                        reloadParsed()
                                        dependencies.officialTournamentsReadManager.load(clubId)
                                        showInfo(
                                            tr("common.success", "Erfolg"),
                                            autoRegisterMessage(res) ?: tr("officialTournaments.autoRegisterOk", "Automatische Anmeldung abgeschlossen."),
                                        )
                                    } else {
                                        showInfo(tr("common.error", "Fehler"), tr("officialTournaments.autoRegisterFail", "Anmeldung fehlgeschlagen."))
                                    }
                                }.onFailure { showInfo(tr("common.error", "Fehler"), it.message ?: "") }
                                busyAuto = false
                            }
                        },
                        enabled = !busyAuto && canAutoRegister(tid, mapSnapshot(), pd),
                    ) { Text(if (busyAuto) "…" else tr("officialTournaments.autoRegisterPrimary", "Automatisch anmelden")) }
                } else if (primary == "results") {
                    Button(onClick = { activeTab = WorkspaceTab.Results }) {
                        Text(tr("officialTournaments.resultsMaintain", "Ergebnisse pflegen"))
                    }
                } else {
                    Button(
                        onClick = { showMemberDialog = true },
                        enabled = activeMembers.isNotEmpty(),
                    ) { Text(tr("officialTournaments.selectMembers", "Mitglieder auswählen")) }
                }
                if (primary != "members" && canWrite) {
                    OutlinedButton(onClick = { showMemberDialog = true }, enabled = activeMembers.isNotEmpty()) {
                        Text(tr("officialTournaments.selectMembers", "Mitglieder auswählen"))
                    }
                }
                if (primary != "auto-register" && canWrite) {
                    OutlinedButton(
                        onClick = {
                            scope.launch {
                                if (!canAutoRegister(tid, mapSnapshot(), pd)) return@launch
                                busyAuto = true
                                runCatching {
                                    val res = withContext(Dispatchers.IO) {
                                        dependencies.officialTournamentsApi.autoRegister(clubId, tid)
                                    }
                                    if (isAutoRegisterSuccess(res)) {
                                        reloadParsed()
                                        dependencies.officialTournamentsReadManager.load(clubId)
                                        showInfo(tr("common.success", "Erfolg"), autoRegisterMessage(res) ?: "")
                                    }
                                }.onFailure { showInfo(tr("common.error", "Fehler"), it.message ?: "") }
                                busyAuto = false
                            }
                        },
                        enabled = !busyAuto && canAutoRegister(tid, mapSnapshot(), pd),
                    ) { Text(if (busyAuto) "…" else tr("officialTournaments.autoRegister", "Automatisch anmelden")) }
                }
                if (primary != "results") {
                    OutlinedButton(
                        onClick = { activeTab = WorkspaceTab.Results },
                        enabled = regSum > 0,
                    ) { Text(tr("officialTournaments.results", "Ergebnisse")) }
                }
            }

            Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(4.dp)) {
                TabButton(tr("officialTournaments.tabOverview", "Übersicht"), activeTab == WorkspaceTab.Overview) { activeTab = WorkspaceTab.Overview }
                TabButton(tr("officialTournaments.competitions", "Konkurrenzen"), activeTab == WorkspaceTab.Competitions) { activeTab = WorkspaceTab.Competitions }
                TabButton(tr("officialTournaments.results", "Ergebnisse"), activeTab == WorkspaceTab.Results) { activeTab = WorkspaceTab.Results }
            }

            when (activeTab) {
                WorkspaceTab.Overview -> OverviewTabContent(
                    clubId = clubId,
                    tournamentId = tid,
                    parsed = p,
                    members = membersState.members,
                    participationMap = participationMap,
                    canWrite = canWrite,
                    participantsFilter = participantsFilter,
                    onFilterChange = { participantsFilter = it },
                    dependencies = dependencies,
                    tr = tr,
                    memberNameById = ::memberNameById,
                    scope = scope,
                    onReload = { scope.launch { reloadParsed() } },
                    onShowInfo = ::showInfo,
                    onBatchRequest = { batchAction = it },
                )
                WorkspaceTab.Competitions -> CompetitionsTabContent(
                    parsed = p,
                    members = membersState.members,
                    participationMap = participationMap,
                    expanded = expandedCompetitionKeys,
                    clubId = clubId,
                    tournamentId = tid,
                    canWrite = canWrite,
                    dependencies = dependencies,
                    tr = tr,
                    memberNameById = ::memberNameById,
                    scope = scope,
                    onReload = { scope.launch { reloadParsed() } },
                    onShowInfo = ::showInfo,
                )
                WorkspaceTab.Results -> ResultsTabContent(
                    clubId = clubId,
                    tournamentId = tid,
                    parsed = p,
                    participationMap = participationMap,
                    canWrite = canWrite,
                    dependencies = dependencies,
                    tr = tr,
                    memberNameById = ::memberNameById,
                    scope = scope,
                    onReload = { scope.launch { reloadParsed() } },
                    onShowInfo = ::showInfo,
                )
            }
            }
        }
    }

    if (showMemberDialog) {
        MemberSelectionDialog(
            activeMembers = activeMembers,
            selectedIds = selectedMemberIds,
            onSelectedIdsChange = { selectedMemberIds = it },
            dialogMemberId = dialogMemberId,
            onDialogMemberIdChange = { dialogMemberId = it },
            parsed = parsed,
            recommendations = memberRecommendations,
            onRecommendationsChange = { memberRecommendations = it },
            onDismiss = { showMemberDialog = false },
            tr = tr,
        )
    }

    if (showInfoDialog && infoMessage != null) {
        StableAlertDialog(
            onDismissRequest = { showInfoDialog = false },
            title = { Text(infoTitle) },
            text = { Text(infoMessage!!) },
            confirmButton = { TextButton(onClick = { showInfoDialog = false }) { Text(tr("common.ok", "OK")) } },
        )
    }

    batchAction?.let { action ->
        val pBatch = parsed
        if (pBatch == null || selectedTournamentId == null) return@let
        val rows = when (action) {
            "register" -> participantsRows(pBatch, mapSnapshot(), "open") { memberNameById(it) }.filter { it.wants && !it.registered && !it.participated }
            "participate" -> participantsRows(pBatch, mapSnapshot(), "registered") { memberNameById(it) }
            "reset" -> participantsRows(pBatch, mapSnapshot(), "participated") { memberNameById(it) }
            else -> emptyList()
        }
        StableAlertDialog(
            onDismissRequest = { batchAction = null },
            title = { Text(tr("officialTournaments.batchTitle", "Sammelaktion")) },
            text = { Text(tr("officialTournaments.batchConfirm", "{n} Einträge wirklich ausführen?").replace("{n}", rows.size.toString())) },
            confirmButton = {
                TextButton(
                    onClick = {
                        val act = batchAction
                        batchAction = null
                        if (act == null) return@TextButton
                        scope.launch {
                            val tournamentIdForBatch = selectedTournamentId ?: return@launch
                            var ok = 0
                            val failures = mutableListOf<String>()
                            for (row in rows) {
                                runCatching {
                                    val parts = row.key.split("-")
                                    val cid = parts[0].toInt()
                                    val mid = parts[1].toInt()
                                    val res = withContext(Dispatchers.IO) {
                                        dependencies.officialTournamentsApi.updateParticipantStatus(
                                            clubId,
                                            tournamentIdForBatch,
                                            OfficialParticipantStatusBody(
                                                competitionId = cid,
                                                memberId = mid,
                                                action = when (act) {
                                                    "register" -> "register"
                                                    "participate" -> "participate"
                                                    else -> "reset"
                                                },
                                            ),
                                        )
                                    }
                                    val st = res.status
                                    if (res.success && st != null) {
                                        participationMap[row.key] = ParticipationState(st.wants, st.registered, st.participated, st.placement)
                                        ok++
                                    }
                                }.onFailure { failures.add("${row.memberName} / ${row.competitionName}") }
                            }
                            if (failures.isNotEmpty()) {
                                showInfo(tr("officialTournaments.batchPartial", "Teilweise erfolgreich"), "$ok / ${rows.size}\n${failures.joinToString("\n")}")
                            } else {
                                showInfo(tr("common.success", "Erfolg"), tr("officialTournaments.batchDone", "{n} Einträge aktualisiert.").replace("{n}", ok.toString()))
                            }
                        }
                    },
                ) { Text(tr("common.confirm", "Bestätigen")) }
            },
            dismissButton = { TextButton(onClick = { batchAction = null }) { Text(tr("common.cancel", "Abbrechen")) } },
        )
    }

    deleteTarget?.let { target ->
        StableAlertDialog(
            onDismissRequest = { deleteTarget = null },
            title = { Text(tr("officialTournaments.deleteTournamentTitle", "Turnier löschen")) },
            text = { Text("${target.title ?: ""} (ID ${target.id})") },
            confirmButton = {
                TextButton(
                    onClick = {
                        val id = target.id
                        deleteTarget = null
                        scope.launch {
                            runCatching {
                                withContext(Dispatchers.IO) { dependencies.officialTournamentsApi.deleteTournament(clubId, id) }
                                if (selectedTournamentId == id) selectedTournamentId = null
                                dependencies.officialTournamentsReadManager.load(clubId)
                                showInfo(tr("common.success", "Erfolg"), tr("common.deleted", "Gelöscht."))
                            }.onFailure { showInfo(tr("common.error", "Fehler"), it.message ?: "") }
                        }
                    },
                ) { Text(tr("common.delete", "Löschen")) }
            },
            dismissButton = { TextButton(onClick = { deleteTarget = null }) { Text(tr("common.cancel", "Abbrechen")) } },
        )
    }
}

@Composable
private fun ColumnScope.OfficialTournamentImportBlock(
    tr: (String, String) -> String,
    pdfUploading: Boolean,
    pickedPdfBytes: ByteArray?,
    pickedPdfName: String,
    onPickPdf: () -> Unit,
    onUpload: () -> Unit,
) {
    OfficialPanelHeader(
        title = tr("officialTournaments.importTitle", "Turnier importieren"),
        subtitle = tr("officialTournaments.importHint", "PDF hochladen und als neues offizielles Turnier anlegen."),
    )
    Row(
        horizontalArrangement = Arrangement.spacedBy(8.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        OutlinedButton(onClick = onPickPdf, enabled = !pdfUploading) {
            Text(tr("officialTournaments.choosePdf", "PDF auswählen"))
        }
        Button(
            onClick = onUpload,
            enabled = pickedPdfBytes != null && !pdfUploading,
        ) {
            Text(if (pdfUploading) "…" else tr("officialTournaments.uploadPdf", "PDF hochladen"))
        }
    }
    if (pickedPdfName.isNotBlank()) {
        Text(
            pickedPdfName,
            style = MaterialTheme.typography.caption,
            color = OfficialPanelMuted,
        )
    }
}

@Composable
private fun ColumnScope.OfficialTournamentSavedEventsBlock(
    tr: (String, String) -> String,
    officialStateLoading: Boolean,
    filteredList: List<OfficialTournamentListRowDto>,
    tournamentSearch: String,
    onTournamentSearchChange: (String) -> Unit,
    showOlderTournaments: Boolean,
    onShowOlderChange: (Boolean) -> Unit,
    totalTournaments: Int,
    selectedTournamentId: Int?,
    onSelectTournament: (Int) -> Unit,
    onTabReset: () -> Unit,
    editingTournamentId: Int?,
    editingTitle: String,
    onEditingTitleChange: (String) -> Unit,
    onStartTitleEdit: (OfficialTournamentListRowDto) -> Unit,
    onCancelTitleEdit: () -> Unit,
    canWrite: Boolean,
    onDeleteRequest: (OfficialTournamentListRowDto) -> Unit,
    clubId: Int,
    scope: kotlinx.coroutines.CoroutineScope,
    dependencies: AppDependencies,
    reloadParsed: suspend () -> OfficialParsedTournamentEnvelopeDto?,
    showInfo: (String, String) -> Unit,
) {
    OfficialPanelHeader(
        title = tr("officialTournaments.savedEvents", "Gespeicherte Veranstaltungen"),
        subtitle = tr("officialTournaments.savedEventsHint", "Turnier auswählen, umbenennen oder löschen."),
    )
    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.spacedBy(8.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        OutlinedTextField(
            value = tournamentSearch,
            onValueChange = onTournamentSearchChange,
            modifier = Modifier.weight(1f),
            singleLine = true,
            label = { Text(tr("officialTournaments.searchTournament", "Turnier suchen")) },
        )
        Row(verticalAlignment = Alignment.CenterVertically) {
            Checkbox(checked = showOlderTournaments, onCheckedChange = onShowOlderChange)
            Text(tr("officialTournaments.showPast", "Vergangene anzeigen"), style = MaterialTheme.typography.caption)
        }
        Text("${filteredList.size} / $totalTournaments", style = MaterialTheme.typography.caption)
    }
    if (officialStateLoading) {
        CircularProgressIndicator(Modifier.padding(8.dp))
    } else if (filteredList.isEmpty()) {
        Text(
            if (tournamentSearch.isNotBlank()) tr("officialTournaments.noMatchingList", "Keine passenden Turniere gefunden.")
            else tr("officialTournaments.noEvents", "Keine importierten Turniere."),
            color = OfficialPanelMuted,
        )
    } else {
        filteredList.forEach { t ->
            val selected = selectedTournamentId == t.id
            val st = tournamentListStatus(selectedTournamentId, t)
            val titleMuted = st.className == "is-past" && !selected
            Surface(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(vertical = 2.dp),
                shape = RoundedCornerShape(8.dp),
                color = if (selected) OfficialSelectedBg else Color.Transparent,
                border = if (selected) BorderStroke(1.dp, OfficialSelectedBorder) else null,
            ) {
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(horizontal = 6.dp, vertical = 4.dp),
                    verticalAlignment = Alignment.CenterVertically,
                ) {
                    if (editingTournamentId == t.id && canWrite) {
                        OutlinedTextField(
                            value = editingTitle,
                            onValueChange = onEditingTitleChange,
                            modifier = Modifier.weight(1f),
                            singleLine = true,
                            label = { Text(tr("officialTournaments.tournamentTitle", "Titel")) },
                        )
                        TextButton(
                            onClick = {
                                val newTitle = editingTitle.trim()
                                onCancelTitleEdit()
                                scope.launch {
                                    if (newTitle != (t.title ?: "").trim()) {
                                        runCatching {
                                            withContext(Dispatchers.IO) {
                                                dependencies.officialTournamentsApi.patchTournament(
                                                    clubId,
                                                    t.id,
                                                    OfficialPatchTournamentBody(title = newTitle.ifBlank { null }),
                                                )
                                            }
                                            dependencies.officialTournamentsReadManager.load(clubId)
                                            if (selectedTournamentId == t.id) reloadParsed()
                                        }.onFailure { showInfo(tr("common.error", "Fehler"), it.message ?: "") }
                                    }
                                }
                            },
                        ) { Text(tr("common.save", "OK")) }
                    } else {
                        Text(
                            t.title ?: (tr("officialTournaments.tournament", "Turnier") + " #${t.id}"),
                            modifier = Modifier
                                .weight(1f)
                                .clickable {
                                    onSelectTournament(t.id)
                                    onTabReset()
                                },
                            fontWeight = if (selected) FontWeight.Bold else FontWeight.Normal,
                            color = if (titleMuted) OfficialPanelMuted else MaterialTheme.colors.onSurface,
                            maxLines = 2,
                            overflow = TextOverflow.Ellipsis,
                        )
                        val dateLine = tournamentListRawDate(t).ifBlank { null }
                        if (dateLine != null) {
                            Text(
                                " — $dateLine",
                                style = MaterialTheme.typography.caption,
                                color = if (titleMuted) OfficialPanelMuted else MaterialTheme.colors.onSurface.copy(alpha = 0.75f),
                            )
                        }
                        TournamentListStatusBadge(st)
                        if (canWrite) {
                            TextButton(onClick = { onStartTitleEdit(t) }) { Text("✏️") }
                            TextButton(onClick = { onDeleteRequest(t) }) { Text("🗑️") }
                        }
                    }
                }
            }
        }
    }
}

@Composable
private fun StatMini(label: String, value: String) {
    Column(Modifier.padding(4.dp)) {
        Text(label, style = MaterialTheme.typography.caption)
        Text(value, fontWeight = FontWeight.Bold)
    }
}

@Composable
private fun TabButton(label: String, selected: Boolean, onClick: () -> Unit) {
    TextButton(onClick = onClick) {
        Text(label, fontWeight = if (selected) FontWeight.Bold else FontWeight.Normal)
    }
}

@Composable
private fun HistoryTable(rows: List<ClubHistoryRow>, tr: (String, String) -> String) {
    val headerBg = MaterialTheme.colors.primary
    val headerFg = MaterialTheme.colors.onPrimary
    Column(Modifier.fillMaxWidth()) {
        Row(
            Modifier
                .fillMaxWidth()
                .background(headerBg)
                .padding(horizontal = 10.dp, vertical = 10.dp),
            horizontalArrangement = Arrangement.spacedBy(8.dp),
        ) {
            Text(
                tr("officialTournaments.member", "Mitglied"),
                Modifier.widthIn(88.dp),
                fontWeight = FontWeight.SemiBold,
                style = MaterialTheme.typography.body2,
                color = headerFg,
            )
            Text(
                tr("officialTournaments.tournament", "Turnier"),
                Modifier.weight(1f),
                fontWeight = FontWeight.SemiBold,
                style = MaterialTheme.typography.body2,
                color = headerFg,
            )
            Text(
                tr("officialTournaments.competition", "Konkurrenz"),
                Modifier.weight(1f),
                fontWeight = FontWeight.SemiBold,
                style = MaterialTheme.typography.body2,
                color = headerFg,
            )
            Text(
                tr("officialTournaments.date", "Datum"),
                Modifier.widthIn(72.dp),
                fontWeight = FontWeight.SemiBold,
                style = MaterialTheme.typography.body2,
                color = headerFg,
            )
            Text(
                tr("officialTournaments.placement", "Platzierung"),
                Modifier.widthIn(52.dp),
                fontWeight = FontWeight.SemiBold,
                style = MaterialTheme.typography.body2,
                color = headerFg,
            )
        }
        rows.forEachIndexed { i, r ->
            Row(
                Modifier
                    .fillMaxWidth()
                    .background(if (i % 2 == 1) OfficialRowStripe else Color.White)
                    .padding(horizontal = 10.dp, vertical = 8.dp),
                horizontalArrangement = Arrangement.spacedBy(8.dp),
            ) {
                Text(
                    r.memberName ?: "–",
                    Modifier.widthIn(88.dp),
                    style = MaterialTheme.typography.body2,
                    maxLines = 2,
                    overflow = TextOverflow.Ellipsis,
                )
                Text(
                    r.tournamentName ?: "–",
                    Modifier.weight(1f),
                    style = MaterialTheme.typography.body2,
                    maxLines = 2,
                    overflow = TextOverflow.Ellipsis,
                )
                Text(
                    r.competitionName ?: "–",
                    Modifier.weight(1f),
                    style = MaterialTheme.typography.body2,
                    maxLines = 2,
                    overflow = TextOverflow.Ellipsis,
                )
                Text(formatIsoDateForDisplay(r.date), Modifier.widthIn(72.dp), style = MaterialTheme.typography.body2)
                Text(r.placement ?: "–", Modifier.widthIn(52.dp), style = MaterialTheme.typography.body2)
            }
            Divider(color = OfficialPanelBorder.copy(alpha = 0.6f))
        }
    }
}

@Composable
private fun OverviewTabContent(
    clubId: Int,
    tournamentId: Int,
    parsed: OfficialParsedTournamentEnvelopeDto,
    members: List<Member>,
    participationMap: MutableMap<String, ParticipationState>,
    canWrite: Boolean,
    participantsFilter: String,
    onFilterChange: (String) -> Unit,
    dependencies: AppDependencies,
    tr: (String, String) -> String,
    memberNameById: (Int) -> String,
    scope: kotlinx.coroutines.CoroutineScope,
    onReload: () -> Unit,
    onShowInfo: (String, String) -> Unit,
    onBatchRequest: (String) -> Unit,
) {
    val mapSnap = participationMap.toMap()
    val rows = remember(parsed, mapSnap, participantsFilter) {
        participantsRows(parsed, mapSnap, participantsFilter, memberNameById)
    }
    val groups = remember(rows) { participantsGroups(rows) }

    Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
        Text(tr("officialTournaments.participantsHeading", "Teilnehmer"), fontWeight = FontWeight.SemiBold)
        Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
            Text(tr("officialTournaments.statusFilter", "Status:"), style = MaterialTheme.typography.caption)
            listOf("all", "open", "registered", "participated").forEach { key ->
                val label = when (key) {
                    "all" -> tr("officialTournaments.filterAll", "Ohne Filter")
                    "open" -> tr("officialTournaments.filterOpen", "Offen")
                    "registered" -> tr("officialTournaments.filterRegistered", "Angemeldet")
                    else -> tr("officialTournaments.filterPlayed", "Hat gespielt")
                }
                TextButton(onClick = { onFilterChange(key) }) {
                    Text(label, fontWeight = if (participantsFilter == key) FontWeight.Bold else FontWeight.Normal)
                }
            }
        }
        if (canWrite) {
            Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
                if (participantsFilter == "open") {
                    OutlinedButton(
                        onClick = { onBatchRequest("register") },
                        enabled = rows.any { it.wants },
                    ) { Text(tr("officialTournaments.batchRegister", "Alle Interessierten als angemeldet markieren"), style = MaterialTheme.typography.caption) }
                }
                if (participantsFilter == "registered") {
                    OutlinedButton(onClick = { onBatchRequest("participate") }, enabled = rows.isNotEmpty()) {
                        Text(tr("officialTournaments.batchParticipate", "Alle als teilgenommen markieren"), style = MaterialTheme.typography.caption)
                    }
                }
                if (participantsFilter == "participated") {
                    OutlinedButton(onClick = { onBatchRequest("reset") }, enabled = rows.isNotEmpty()) {
                        Text(tr("officialTournaments.batchReset", "Alle zurücksetzen"), style = MaterialTheme.typography.caption)
                    }
                }
            }
        }

        if (groups.isEmpty()) {
            Text(tr("officialTournaments.noFilterRows", "Keine Einträge für den gewählten Filter."), style = MaterialTheme.typography.caption)
        } else {
            groups.forEach { g ->
                Text(g.memberName, fontWeight = FontWeight.SemiBold, modifier = Modifier.padding(top = 8.dp))
                g.items.forEach { item ->
                    ParticipantItemRow(
                        clubId = clubId,
                        tournamentId = tournamentId,
                        item = item,
                        groupMemberId = g.memberId,
                        canWrite = canWrite,
                        dependencies = dependencies,
                        participationMap = participationMap,
                        scope = scope,
                        onReload = onReload,
                        onShowInfo = onShowInfo,
                        tr = tr,
                        onMarkInterested = {
                            scope.launch {
                                val comps = parsed.parsedData?.competitions.orEmpty()
                                for (c in comps) {
                                    val m = members.find { it.id == g.memberId } ?: continue
                                    if (!OfficialTournamentEligibility.isEligibleForCompetition(m, c, parsed)) continue
                                    val key = participationKey(c.id, g.memberId)
                                    val cur = getParticipation(participationMap, c.id, g.memberId)
                                    if (cur.wants || cur.registered || cur.participated) continue
                                    runCatching {
                                        withContext(Dispatchers.IO) {
                                            dependencies.officialTournamentsApi.upsertParticipation(
                                                clubId,
                                                tournamentId,
                                                OfficialUpsertParticipationBody(
                                                    competitionId = c.id,
                                                    memberId = g.memberId,
                                                    wants = true,
                                                    registered = false,
                                                    participated = false,
                                                    placement = null,
                                                ),
                                            )
                                        }
                                        participationMap[key] = ParticipationState(true, false, false, null)
                                    }.onFailure { onShowInfo(tr("common.error", "Fehler"), it.message ?: "") }
                                }
                                onReload()
                            }
                        },
                    )
                    Divider()
                }
            }
        }
    }
}

@Composable
private fun ParticipantItemRow(
    clubId: Int,
    tournamentId: Int,
    item: ParticipantTableRow,
    groupMemberId: Int,
    canWrite: Boolean,
    dependencies: AppDependencies,
    participationMap: MutableMap<String, ParticipationState>,
    scope: kotlinx.coroutines.CoroutineScope,
    onReload: () -> Unit,
    onShowInfo: (String, String) -> Unit,
    tr: (String, String) -> String,
    onMarkInterested: () -> Unit,
) {
    var placementDraft by remember(item.key, item.placement) { mutableStateOf(item.placement.orEmpty()) }
    Row(Modifier.fillMaxWidth().padding(vertical = 4.dp), horizontalArrangement = Arrangement.spacedBy(6.dp)) {
        Column(
            Modifier
                .weight(1f)
                .defaultMinSize(minWidth = 160.dp),
        ) {
            Text(item.competitionName, style = MaterialTheme.typography.body2)
            Text(item.start, style = MaterialTheme.typography.caption, color = MaterialTheme.colors.onSurface.copy(alpha = 0.7f))
        }
        Column(Modifier.widthIn(100.dp)) {
            val status = when {
                item.participated -> tr("officialTournaments.statusPlayed", "Hat gespielt")
                item.registered -> tr("officialTournaments.statusRegistered", "Angemeldet")
                item.wants -> tr("officialTournaments.statusWants", "Möchte teilnehmen")
                else -> tr("officialTournaments.statusNone", "Nicht interessiert")
            }
            Text(status, style = MaterialTheme.typography.caption)
        }
        Column(Modifier.widthIn(120.dp)) {
            if (!canWrite) return@Column
            when {
                !item.participated && !item.registered && !item.wants -> {
                    TextButton(onClick = onMarkInterested) {
                        Text(tr("officialTournaments.interested", "Interessiert"), style = MaterialTheme.typography.caption)
                    }
                }
                !item.participated && !item.registered && item.wants -> {
                    StatusActionButton(clubId, tournamentId, item, "register", tr("officialTournaments.registeredShort", "Angemeldet"), participationMap, scope, dependencies, onReload, onShowInfo, tr)
                }
                item.registered && !item.participated -> {
                    StatusActionButton(clubId, tournamentId, item, "participate", tr("officialTournaments.playedShort", "Teilgenommen"), participationMap, scope, dependencies, onReload, onShowInfo, tr)
                }
                item.participated -> {
                    StatusActionButton(clubId, tournamentId, item, "reset", tr("officialTournaments.resetShort", "Zurücksetzen"), participationMap, scope, dependencies, onReload, onShowInfo, tr)
                }
                else -> Text(tr("officialTournaments.noAction", "Keine Aktion"), style = MaterialTheme.typography.caption)
            }
        }
        Column(Modifier.wrapContentWidth(Alignment.End)) {
            if (item.participated && canWrite) {
                Row(
                    verticalAlignment = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.spacedBy(4.dp),
                ) {
                    OutlinedTextField(
                        value = placementDraft,
                        onValueChange = { placementDraft = it },
                        modifier = Modifier.width(72.dp),
                        singleLine = true,
                        label = { Text(tr("officialTournaments.placement", "Platz"), style = MaterialTheme.typography.caption) },
                    )
                    TextButton(
                        onClick = {
                            scope.launch {
                                val parts = item.key.split("-")
                                val cid = parts[0].toInt()
                                val mid = parts[1].toInt()
                                val cur = getParticipation(participationMap, cid, mid)
                                runCatching {
                                    withContext(Dispatchers.IO) {
                                        dependencies.officialTournamentsApi.upsertParticipation(
                                            clubId,
                                            tournamentId,
                                            OfficialUpsertParticipationBody(
                                                competitionId = cid,
                                                memberId = mid,
                                                wants = cur.wants,
                                                registered = cur.registered,
                                                participated = cur.participated,
                                                placement = placementDraft.trim().ifBlank { null },
                                            ),
                                        )
                                    }
                                    participationMap[item.key] = cur.copy(placement = placementDraft.trim().ifBlank { null })
                                }.onFailure { onShowInfo(tr("common.error", "Fehler"), it.message ?: "") }
                            }
                        },
                    ) { Text("OK") }
                }
            } else {
                Text(item.placement ?: "–", style = MaterialTheme.typography.caption)
            }
        }
    }
}

@Composable
private fun StatusActionButton(
    clubId: Int,
    tournamentId: Int,
    item: ParticipantTableRow,
    action: String,
    label: String,
    participationMap: MutableMap<String, ParticipationState>,
    scope: kotlinx.coroutines.CoroutineScope,
    dependencies: AppDependencies,
    onReload: () -> Unit,
    onShowInfo: (String, String) -> Unit,
    tr: (String, String) -> String,
) {
    TextButton(
        onClick = {
            scope.launch {
                val parts = item.key.split("-")
                val cid = parts[0].toInt()
                val mid = parts[1].toInt()
                runCatching {
                    val res = withContext(Dispatchers.IO) {
                        dependencies.officialTournamentsApi.updateParticipantStatus(
                            clubId,
                            tournamentId,
                            OfficialParticipantStatusBody(competitionId = cid, memberId = mid, action = action),
                        )
                    }
                    val st = res.status
                    if (res.success && st != null) {
                        participationMap[item.key] = ParticipationState(st.wants, st.registered, st.participated, st.placement)
                    }
                }.onFailure { onShowInfo(tr("common.error", "Fehler"), it.message ?: "") }
            }
        },
    ) { Text(label, style = MaterialTheme.typography.caption) }
}

@Composable
private fun CompetitionsTabContent(
    parsed: OfficialParsedTournamentEnvelopeDto,
    members: List<Member>,
    participationMap: MutableMap<String, ParticipationState>,
    expanded: MutableMap<String, Boolean>,
    clubId: Int,
    tournamentId: Int,
    canWrite: Boolean,
    dependencies: AppDependencies,
    tr: (String, String) -> String,
    memberNameById: (Int) -> String,
    scope: kotlinx.coroutines.CoroutineScope,
    onReload: () -> Unit,
    onShowInfo: (String, String) -> Unit,
) {
    val comps = parsed.parsedData?.competitions.orEmpty()
    Text(tr("officialTournaments.competitions", "Konkurrenzen"), fontWeight = FontWeight.SemiBold)
    comps.forEachIndexed { idx, c ->
        val ek = "c-$idx"
        val exp = expanded[ek] == true
        Row(verticalAlignment = Alignment.CenterVertically) {
            TextButton(onClick = { expanded[ek] = !exp }) { Text(if (exp) "▾" else "▸") }
            Column(Modifier.weight(1f)) {
                Text(c.ageClassCompetition ?: c.altersklasseWettbewerb ?: "–", style = MaterialTheme.typography.body2)
                Text(c.startTime ?: c.startzeit ?: "–", style = MaterialTheme.typography.caption)
            }
            val sum = competitionStatusSummary(c, parsed, members, participationMap.toMap())
            Text("${sum.interested} / ${sum.registered} / ${sum.participated}", style = MaterialTheme.typography.caption)
            Text(c.entryFee ?: c.startgeld ?: "–", style = MaterialTheme.typography.caption, modifier = Modifier.widthIn(48.dp))
        }
        if (exp) {
            Column(Modifier.padding(start = 32.dp, bottom = 8.dp)) {
                Text("${tr("officialTournaments.deadlineDate", "Meldeschluss")}: ${formatIsoDateForDisplay(c.registrationDeadlineDate ?: c.meldeschlussDatum)}", style = MaterialTheme.typography.caption)
                Text("${tr("officialTournaments.deadlineOnline", "Online bis")}: ${formatIsoDateForDisplay(c.registrationDeadlineOnline ?: c.meldeschlussOnline)}", style = MaterialTheme.typography.caption)
                Text("${tr("officialTournaments.cutoffDate", "Stichtag")}: ${formatIsoDateForDisplay(c.cutoffDate ?: c.stichtag)}", style = MaterialTheme.typography.caption)
                Text("${tr("officialTournaments.openTo", "Offen für")}: ${c.openTo ?: c.offenFuer ?: "–"}", style = MaterialTheme.typography.caption)
                Text("${tr("officialTournaments.preliminaryRound", "Vorrunde")}: ${c.preliminaryRound ?: c.vorrunde ?: "–"}", style = MaterialTheme.typography.caption)
                Text("${tr("officialTournaments.finalRound", "Endrunde")}: ${c.finalRound ?: c.endrunde ?: "–"}", style = MaterialTheme.typography.caption)
                Text(tr("officialTournaments.activeRegistrations", "Aktive Meldungen"), fontWeight = FontWeight.SemiBold, modifier = Modifier.padding(top = 6.dp))
                val activeRows = activeCompetitionMemberRows(c, parsed, members, participationMap.toMap(), memberNameById)
                if (activeRows.isEmpty()) {
                    Text(tr("officialTournaments.noActiveRegs", "Noch keine aktiven Meldungen in dieser Konkurrenz."), style = MaterialTheme.typography.caption)
                } else {
                    activeRows.forEach { row ->
                        Column(Modifier.fillMaxWidth().padding(vertical = 4.dp)) {
                            Row(verticalAlignment = Alignment.CenterVertically) {
                                Text(row.memberName, Modifier.weight(1f), style = MaterialTheme.typography.caption)
                                Text(row.statusText, style = MaterialTheme.typography.caption, modifier = Modifier.padding(horizontal = 4.dp))
                                if (canWrite) {
                                    when {
                                        row.wants && !row.registered && !row.participated -> {
                                            SmallStatusBtn(clubId, tournamentId, row.key, "register", tr("officialTournaments.registeredShort", "Angemeldet"), participationMap, scope, dependencies, onShowInfo, tr)
                                        }
                                        row.registered && !row.participated -> {
                                            SmallStatusBtn(clubId, tournamentId, row.key, "participate", tr("officialTournaments.playedShort", "Teilgenommen"), participationMap, scope, dependencies, onShowInfo, tr)
                                        }
                                        row.participated -> {
                                            SmallStatusBtn(clubId, tournamentId, row.key, "reset", tr("officialTournaments.resetShort", "Zurücksetzen"), participationMap, scope, dependencies, onShowInfo, tr)
                                        }
                                    }
                                }
                            }
                            if (row.participated && canWrite) {
                                CompetitionPlacementRow(
                                    clubId = clubId,
                                    tournamentId = tournamentId,
                                    rowKey = row.key,
                                    initialPlacement = row.placement,
                                    wants = row.wants,
                                    registered = row.registered,
                                    participated = row.participated,
                                    participationMap = participationMap,
                                    dependencies = dependencies,
                                    scope = scope,
                                    onShowInfo = onShowInfo,
                                    tr = tr,
                                )
                            }
                        }
                    }
                }
            }
        }
        Divider()
    }
}

@Composable
private fun CompetitionPlacementRow(
    clubId: Int,
    tournamentId: Int,
    rowKey: String,
    initialPlacement: String?,
    wants: Boolean,
    registered: Boolean,
    participated: Boolean,
    participationMap: MutableMap<String, ParticipationState>,
    dependencies: AppDependencies,
    scope: kotlinx.coroutines.CoroutineScope,
    onShowInfo: (String, String) -> Unit,
    tr: (String, String) -> String,
) {
    var placementDraft by remember(rowKey, initialPlacement) { mutableStateOf(initialPlacement.orEmpty()) }
    Row(Modifier.fillMaxWidth().padding(top = 4.dp), verticalAlignment = Alignment.CenterVertically) {
        OutlinedTextField(
            value = placementDraft,
            onValueChange = { placementDraft = it },
            modifier = Modifier.width(88.dp),
            singleLine = true,
            label = { Text(tr("officialTournaments.placement", "Platzierung"), style = MaterialTheme.typography.caption) },
        )
        TextButton(
            onClick = {
                scope.launch {
                    val cid = rowKey.substringBeforeLast('-').toInt()
                    val mid = rowKey.substringAfterLast('-').toInt()
                    runCatching {
                        withContext(Dispatchers.IO) {
                            dependencies.officialTournamentsApi.upsertParticipation(
                                clubId,
                                tournamentId,
                                OfficialUpsertParticipationBody(
                                    competitionId = cid,
                                    memberId = mid,
                                    wants = wants,
                                    registered = registered,
                                    participated = participated,
                                    placement = placementDraft.trim().ifBlank { null },
                                ),
                            )
                        }
                        val cur = getParticipation(participationMap, cid, mid)
                        participationMap[rowKey] = cur.copy(placement = placementDraft.trim().ifBlank { null })
                    }.onFailure { onShowInfo(tr("common.error", "Fehler"), it.message ?: "") }
                }
            },
        ) { Text("OK") }
    }
}

@Composable
private fun SmallStatusBtn(
    clubId: Int,
    tournamentId: Int,
    rowKey: String,
    action: String,
    label: String,
    participationMap: MutableMap<String, ParticipationState>,
    scope: kotlinx.coroutines.CoroutineScope,
    dependencies: AppDependencies,
    onShowInfo: (String, String) -> Unit,
    tr: (String, String) -> String,
) {
    TextButton(
        onClick = {
            scope.launch {
                val cid = rowKey.substringBeforeLast('-').toInt()
                val mid = rowKey.substringAfterLast('-').toInt()
                runCatching {
                    val res = withContext(Dispatchers.IO) {
                        dependencies.officialTournamentsApi.updateParticipantStatus(
                            clubId,
                            tournamentId,
                            OfficialParticipantStatusBody(competitionId = cid, memberId = mid, action = action),
                        )
                    }
                    val st = res.status
                    if (res.success && st != null) {
                        participationMap[rowKey] = ParticipationState(st.wants, st.registered, st.participated, st.placement)
                    }
                }.onFailure { onShowInfo(tr("common.error", "Fehler"), it.message ?: "") }
            }
        },
    ) { Text(label, style = MaterialTheme.typography.caption) }
}

@Composable
private fun ResultsTabContent(
    clubId: Int,
    tournamentId: Int,
    parsed: OfficialParsedTournamentEnvelopeDto,
    participationMap: MutableMap<String, ParticipationState>,
    canWrite: Boolean,
    dependencies: AppDependencies,
    tr: (String, String) -> String,
    memberNameById: (Int) -> String,
    scope: kotlinx.coroutines.CoroutineScope,
    onReload: () -> Unit,
    onShowInfo: (String, String) -> Unit,
) {
    

    val rows = remember(parsed, participationMap.toMap()) {
        resultsRows(parsed, participationMap.toMap(), memberNameById)
    }
    val groups = remember(rows) { resultsGroups(rows) }
    Text(tr("officialTournaments.results", "Ergebnisse"), fontWeight = FontWeight.SemiBold)
    if (groups.isEmpty()) {
        Text(tr("officialTournaments.noResults", "Keine Ergebnisse vorhanden."), style = MaterialTheme.typography.caption)
    } else {
        groups.forEach { g ->
            Text(g.memberName, fontWeight = FontWeight.SemiBold, modifier = Modifier.padding(top = 8.dp))
            g.items.forEach { item ->
                ParticipantItemRow(
                    clubId = clubId,
                    tournamentId = tournamentId,
                    item = item,
                    groupMemberId = g.memberId,
                    canWrite = canWrite,
                    dependencies = dependencies,
                    participationMap = participationMap,
                    scope = scope,
                    onReload = onReload,
                    onShowInfo = onShowInfo,
                    tr = tr,
                    onMarkInterested = {},
                )
                Divider()
            }
        }
    }

    
}

@Composable
private fun MemberSelectionDialog(
    activeMembers: List<Member>,
    selectedIds: Set<Int>,
    onSelectedIdsChange: (Set<Int>) -> Unit,
    dialogMemberId: Int?,
    onDialogMemberIdChange: (Int?) -> Unit,
    parsed: OfficialParsedTournamentEnvelopeDto?,
    recommendations: Map<Int, Set<String>>,
    onRecommendationsChange: (Map<Int, Set<String>>) -> Unit,
    onDismiss: () -> Unit,
    tr: (String, String) -> String,
) {
    Dialog(onDismissRequest = onDismiss) {
        Surface(shape = MaterialTheme.shapes.medium, elevation = 8.dp) {
            Column(
                Modifier
                    .padding(16.dp)
                    .heightIn(max = 520.dp)
                    .verticalScroll(rememberScrollState()),
            ) {
                Text(tr("officialTournaments.selectMembers", "Mitglieder auswählen"), fontWeight = FontWeight.SemiBold)
                Row {
                    TextButton(onClick = { onSelectedIdsChange(activeMembers.map { it.id }.toSet()) }) {
                        Text(tr("officialTournaments.selectAll", "Alle"))
                    }
                    TextButton(onClick = { onSelectedIdsChange(emptySet()) }) { Text(tr("officialTournaments.deselectAll", "Keine")) }
                }
                activeMembers.forEach { m ->
                    Row(verticalAlignment = Alignment.CenterVertically) {
                        Checkbox(
                            checked = selectedIds.contains(m.id),
                            onCheckedChange = {
                                onDialogMemberIdChange(m.id)
                                if (it) onSelectedIdsChange(selectedIds + m.id) else onSelectedIdsChange(selectedIds - m.id)
                            },
                        )
                        Text("${m.firstName} ${m.lastName}", modifier = Modifier.clickable { onDialogMemberIdChange(m.id) })
                    }
                    if (dialogMemberId == m.id && parsed != null) {
                        val comps = remember(parsed, m) { competitionsForMember(parsed, m) }
                        comps.forEach { cr ->
                            val rec = recommendations[m.id]?.contains(cr.key) == true
                            Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(start = 40.dp)) {
                                Checkbox(
                                    checked = rec,
                                    onCheckedChange = { checked ->
                                        val set = recommendations[m.id].orEmpty().toMutableSet()
                                        if (checked) set.add(cr.key) else set.remove(cr.key)
                                        onRecommendationsChange(recommendations + (m.id to set))
                                    },
                                )
                                Column {
                                    Text(cr.name, style = MaterialTheme.typography.caption)
                                    Text("${formatIsoDateForDisplay(cr.date)} ${cr.time} · ${cr.entryFee}", style = MaterialTheme.typography.caption)
                                }
                            }
                        }
                    }
                }
                TextButton(onClick = onDismiss, modifier = Modifier.align(Alignment.End)) {
                    Text(tr("common.close", "Schließen"))
                }
            }
        }
    }
}

/**
 * Einstieg aus dem Hauptmenü (Nav-Rail / untere Leiste): offizielle Turniere & Turnierteilnahmen.
 */
@Composable
internal fun OfficialTournamentsMenuRoute(
    dependencies: AppDependencies,
    onNavigateBack: () -> Unit,
) {
    val languageCode = LocalLanguageCode.current
    fun translate(key: String, fallback: String) = MobileStrings.get(languageCode, key, fallback)
    val clubState by dependencies.clubManager.state.collectAsState()
    val clubId = clubState.currentClubId ?: return
    val perms = clubState.currentPermissions
    if (perms?.canReadTournaments() != true) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(24.dp),
        ) {
            Text(translate("mobile.noTournamentAccess", "Keine Berechtigung für Turniere."))
            TextButton(onClick = onNavigateBack) { Text(translate("mobile.back", "Zurück")) }
        }
        return
    }
    OfficialTournamentsWorkspaceScreen(
        clubId = clubId,
        canWrite = perms.canWriteTournaments(),
        dependencies = dependencies,
        tr = ::translate,
        onClose = onNavigateBack,
    )
}
