package de.tsschulz.tt_tagebuch.app.ui

import android.content.ClipData
import android.content.Context
import android.content.Intent
import android.media.MediaPlayer
import android.net.Uri
import android.widget.Toast
import androidx.activity.compose.BackHandler
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.result.PickVisualMediaRequest
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.fillMaxHeight
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.aspectRatio
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.BottomNavigation
import androidx.compose.material.BottomNavigationItem
import androidx.compose.material.Button
import androidx.compose.material.Card
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Checkbox
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.Surface
import androidx.compose.material.Switch
import androidx.compose.material.Tab
import androidx.compose.material.TabRow
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.ExpandLess
import androidx.compose.material.icons.filled.ExpandMore
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInParent
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlin.math.max
import de.tsschulz.tt_tagebuch.app.AppDependencies
import de.tsschulz.tt_tagebuch.R
import de.tsschulz.tt_tagebuch.BuildConfig
import de.tsschulz.tt_tagebuch.app.pdf.sharePdfFile
import de.tsschulz.tt_tagebuch.app.pdf.writeMembersPhoneListPdf
import de.tsschulz.tt_tagebuch.app.pdf.writeTrainingDaySummaryPdf
import de.tsschulz.tt_tagebuch.app.pdf.writeTrainingPlanPdf
import de.tsschulz.tt_tagebuch.shared.api.memberProfileImagePath
import de.tsschulz.tt_tagebuch.shared.api.toAbsoluteUrl
import de.tsschulz.tt_tagebuch.shared.api.MobileFeedbackBody
import de.tsschulz.tt_tagebuch.shared.api.models.MemberGroupPhotoDto
import de.tsschulz.tt_tagebuch.shared.api.models.canReadApprovals
import de.tsschulz.tt_tagebuch.shared.api.models.canReadClubPermissions
import de.tsschulz.tt_tagebuch.shared.api.models.canReadClubSettings
import de.tsschulz.tt_tagebuch.shared.api.models.canReadPredefinedActivities
import de.tsschulz.tt_tagebuch.shared.api.models.canReadDiary
import de.tsschulz.tt_tagebuch.shared.api.models.canReadTeams
import de.tsschulz.tt_tagebuch.shared.api.models.canReadMembers
import de.tsschulz.tt_tagebuch.shared.api.models.canReadSchedule
import de.tsschulz.tt_tagebuch.shared.api.models.canReadStatistics
import de.tsschulz.tt_tagebuch.shared.api.models.canReadTournaments
import de.tsschulz.tt_tagebuch.shared.api.models.canWriteDiary
import de.tsschulz.tt_tagebuch.shared.api.models.canWriteMembers
import de.tsschulz.tt_tagebuch.shared.api.models.canWriteMyTischtennis
import de.tsschulz.tt_tagebuch.shared.api.models.mainActivityImagePath
import de.tsschulz.tt_tagebuch.shared.api.models.nestedActivityImagePath
import de.tsschulz.tt_tagebuch.shared.api.models.AccidentReportDto
import de.tsschulz.tt_tagebuch.shared.api.models.Club
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryDate
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryFreeformActivity
import de.tsschulz.tt_tagebuch.shared.api.models.AddDiaryPlanGroupActivityRequest
import de.tsschulz.tt_tagebuch.shared.api.models.CreateDiaryPlanActivityRequest
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryDateActivityItem
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryPlanGroup
import de.tsschulz.tt_tagebuch.shared.api.models.UpdateDiaryPlanActivityRequest
import de.tsschulz.tt_tagebuch.shared.api.models.UpdateNestedPlanGroupActivityRequest
import de.tsschulz.tt_tagebuch.shared.api.models.GroupActivitySummary
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryMemberNoteDto
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryMemberTagLinkDto
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryTag
import de.tsschulz.tt_tagebuch.shared.api.models.PredefinedActivityDto
import de.tsschulz.tt_tagebuch.shared.api.models.displayLabel as predefinedDtoDisplayLabel
import de.tsschulz.tt_tagebuch.shared.api.models.tagDefinitionId
import de.tsschulz.tt_tagebuch.shared.api.models.tagDisplayName
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryTrainingParticipant
import de.tsschulz.tt_tagebuch.shared.api.models.isPresentParticipant
import de.tsschulz.tt_tagebuch.shared.api.models.displayLabel
import de.tsschulz.tt_tagebuch.shared.api.models.displayTitle
import de.tsschulz.tt_tagebuch.shared.api.models.drawingDataForDisplay
import de.tsschulz.tt_tagebuch.shared.api.models.memberLabel
import de.tsschulz.tt_tagebuch.shared.api.models.MemberActivityStatDto
import de.tsschulz.tt_tagebuch.shared.api.models.MemberContactDto
import de.tsschulz.tt_tagebuch.shared.api.models.MemberContactSetBody
import de.tsschulz.tt_tagebuch.shared.api.models.MemberDataQualityRequirements
import de.tsschulz.tt_tagebuch.shared.api.models.MemberLastParticipationDto
import de.tsschulz.tt_tagebuch.shared.api.models.TrainingGroupDto
import de.tsschulz.tt_tagebuch.shared.api.models.TrainingTimeDto
import de.tsschulz.tt_tagebuch.shared.api.models.toSetBody
import de.tsschulz.tt_tagebuch.shared.api.models.Member
import de.tsschulz.tt_tagebuch.shared.api.models.MemberSetBody
import de.tsschulz.tt_tagebuch.shared.api.models.UserClubPermissions
import de.tsschulz.tt_tagebuch.shared.i18n.MobileStrings
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import android.util.Log

/** Ab dieser Fensterbreite (dp): seitliche Navigation wie auf Tablet/Web. */
private const val MAIN_NAV_RAIL_MIN_WIDTH_DP = 600

private val ScreenHorizontalPadding = 20.dp
private val TouchMinHeight = 48.dp

private fun visibleMainTabs(perms: UserClubPermissions?): List<MainTab> =
    MainTab.entries.filter { tab ->
        when (tab) {
            MainTab.Home, MainTab.Settings -> true
            MainTab.Diary -> perms == null || perms.canReadDiary()
            MainTab.Members -> perms == null || perms.canReadMembers()
            MainTab.Schedule, MainTab.FriendlyMatches -> perms == null || perms.canReadSchedule()
            MainTab.Calendar ->
                perms == null ||
                    perms.canReadDiary() || perms.canReadSchedule() || perms.canReadTournaments()
            MainTab.Tournaments, MainTab.OfficialParticipations -> perms == null || perms.canReadTournaments()
            MainTab.Stats -> perms == null || perms.canReadStatistics()
        }
    }

private const val AUTH_ROUTE_LOGIN = "login"
private const val AUTH_ROUTE_REGISTER = "register"
private const val AUTH_ROUTE_FORGOT = "forgot"
private const val AUTH_ROUTE_RESET = "reset"
private const val AUTH_ROUTE_ACTIVATE = "activate"

private enum class MainTab {
    Home,
    Diary,
    Members,
    Schedule,
    FriendlyMatches,
    Calendar,
    Stats,
    Tournaments,
    OfficialParticipations,
    Settings,
}

private enum class DiaryDetailTab {
    Plan,
    Participants,
    Activities,
}

private enum class ParticipantToolbarFilter {
    All,
    Present,
    Absent,
    Excused,
    Cancelled,
    Test,
}

@Composable
fun AppRoot(dependencies: AppDependencies) {
    val authState by dependencies.authManager.state.collectAsState()
    val clubState by dependencies.clubManager.state.collectAsState()
    val languageState by dependencies.languageManager.state.collectAsState()

    LaunchedEffect(Unit) {
        dependencies.languageManager.hydrate()
        dependencies.authManager.hydrate()
        dependencies.clubManager.hydrate()
    }
    LaunchedEffect(Unit) {
        dependencies.unauthorizedEvents.collectLatest { count ->
            if (count > 0) {
                dependencies.authManager.clearLocal()
                dependencies.clubManager.clearSelection()
                dependencies.diaryManager.clear()
                dependencies.membersManager.clear()
                dependencies.trainingStatsManager.clear()
                dependencies.scheduleManager.clear()
                dependencies.pendingApprovalsManager.clear()
                dependencies.permissionsAdminManager.clear()
                dependencies.apiLogsManager.clear()
                dependencies.clubInternalTournamentsManager.clear()
                dependencies.officialTournamentsReadManager.clear()
            }
        }
    }

    CompositionLocalProvider(LocalLanguageCode provides languageState.currentLanguageCode) {
        var openMemberPortraitCropRequested by remember { mutableStateOf(false) }
        var openMemberGalleryRequested by remember { mutableStateOf(false) }
        Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {
            when {
                authState.isHydrating -> LoadingScreen(tr("mobile.appLoading", "App wird geladen"))
                !authState.isLoggedIn -> AuthFlowHost(dependencies = dependencies)
                clubState.currentClubId == null -> ClubSelectScreen(dependencies)
                else -> MainTabs(
                    dependencies,
                    openMemberPortraitCropRequested = openMemberPortraitCropRequested,
                    onConsumeOpenMemberPortraitCrop = { openMemberPortraitCropRequested = false },
                    onRequestOpenMemberPortraitCrop = { openMemberPortraitCropRequested = true },
                    openMemberGalleryRequested = openMemberGalleryRequested,
                    onConsumeOpenMemberGallery = { openMemberGalleryRequested = false },
                    onRequestOpenMemberGallery = { openMemberGalleryRequested = true },
                )
            }
        }
    }
}

@Composable
private fun MainTabs(
    dependencies: AppDependencies,
    openMemberPortraitCropRequested: Boolean,
    onConsumeOpenMemberPortraitCrop: () -> Unit,
    onRequestOpenMemberPortraitCrop: () -> Unit,
    openMemberGalleryRequested: Boolean,
    onConsumeOpenMemberGallery: () -> Unit,
    onRequestOpenMemberGallery: () -> Unit,
) {
    var selectedTab by rememberSaveable { mutableStateOf(MainTab.Home) }
    var diarySelectedEntryId by remember { mutableStateOf<Int?>(null) }
    var membersNestedOpen by remember { mutableStateOf(false) }
    var billingOrdersSection by remember { mutableStateOf<BillingOrdersSection?>(null) }
    var settingsClubAdminRequest by remember { mutableStateOf<ClubAdminSettingsSection?>(null) }
    var settingsStammdatenRequest by remember { mutableStateOf<ClubStammdatenSection?>(null) }
    val useWideMainNav = LocalConfiguration.current.screenWidthDp >= MAIN_NAV_RAIL_MIN_WIDTH_DP
    val clubState by dependencies.clubManager.state.collectAsState()
    val visibleTabs = visibleMainTabs(clubState.currentPermissions)
    val networkConnected by dependencies.networkConnectivity.connected.collectAsState()
    var lastNetworkConnected by remember { mutableStateOf(true) }
    var feedbackOpen by remember { mutableStateOf(false) }
    val currentScreenTitle = tabTitle(selectedTab)

    /** Tagebuch- und Mitglieder-Listen vorladen, sobald Verein + Rechte feststehen. */
    LaunchedEffect(clubState.currentClubId, clubState.currentPermissions) {
        val id = clubState.currentClubId ?: return@LaunchedEffect
        val perms = clubState.currentPermissions ?: return@LaunchedEffect
        dependencies.applicationScope.launch {
            if (perms.canReadDiary()) {
                dependencies.diaryManager.loadDates(id)
            }
            if (perms.canReadMembers()) {
                dependencies.membersManager.loadMembers(id)
            }
        }
    }

    DisposableEffect(clubState.currentClubId) {
        val id = clubState.currentClubId
        if (id != null) {
            dependencies.socketService.connect(id)
        }
        onDispose {
            dependencies.socketService.disconnect()
        }
    }

    LaunchedEffect(clubState.currentClubId, clubState.currentPermissions) {
        val id = clubState.currentClubId ?: return@LaunchedEffect
        val perms = clubState.currentPermissions ?: return@LaunchedEffect
        dependencies.socketService.events.collectLatest { (event, data) ->
            when (event) {
                "member:changed" -> {
                    if (perms.canReadMembers()) {
                        dependencies.membersManager.loadMembers(id)
                    }
                    if (perms.canReadDiary()) {
                        dependencies.diaryManager.loadDates(id)
                    }
                }
                "participant:added",
                "participant:removed",
                "participant:updated",
                "diary:note:added",
                "diary:note:updated",
                "diary:note:deleted",
                "diary:tag:added",
                "diary:tag:removed",
                "diary:date:updated",
                "activity:member:added",
                "activity:member:removed",
                "activity:changed",
                "group:changed",
                -> {
                    if (perms.canReadDiary()) {
                        dependencies.diaryManager.loadDates(id)
                    }
                    if (event.startsWith("participant:") && perms.canReadStatistics()) {
                        dependencies.trainingStatsManager.loadStats(id)
                    }
                }
                "schedule:match:updated",
                "schedule:match-report:submitted",
                -> {
                    if (perms.canReadSchedule()) {
                        dependencies.scheduleManager.refresh(id)
                    }
                }
                "tournament:changed" -> {
                    if (perms.canReadTournaments()) {
                        dependencies.clubInternalTournamentsManager.loadList(id)
                        dependencies.clubInternalTournamentsManager.state.value.selectedId?.let { tournamentId ->
                            dependencies.clubInternalTournamentsManager.loadDetail(id, tournamentId)
                        }
                        dependencies.officialTournamentsReadManager.load(id)
                    }
                }
            }
        }
    }

    /** Nach Netzwerk-Wiederkehr Listen neu laden (Server ist Quelle der Wahrheit). */
    LaunchedEffect(networkConnected, clubState.currentClubId, clubState.currentPermissions) {
        val id = clubState.currentClubId ?: return@LaunchedEffect
        val perms = clubState.currentPermissions ?: return@LaunchedEffect
        val wasConnected = lastNetworkConnected
        lastNetworkConnected = networkConnected
        if (!wasConnected && networkConnected) {
            dependencies.applicationScope.launch {
                if (perms.canReadDiary()) {
                    dependencies.diaryManager.loadDates(id)
                }
                if (perms.canReadMembers()) {
                    dependencies.membersManager.loadMembers(id)
                }
            }
        }
    }

    LaunchedEffect(selectedTab) {
        if (selectedTab != MainTab.Settings) {
            billingOrdersSection = null
            settingsClubAdminRequest = null
            settingsStammdatenRequest = null
        }
    }

    LaunchedEffect(clubState.currentPermissions, selectedTab) {
        val tabs = visibleMainTabs(clubState.currentPermissions)
        if (!tabs.contains(selectedTab)) {
            selectedTab = MainTab.Home
        }
    }

    val isNestedDetail = when (selectedTab) {
        MainTab.Diary -> diarySelectedEntryId != null
        MainTab.Members -> membersNestedOpen
        else -> false
    }

    fun selectMainTab(tab: MainTab) {
        if (tab != selectedTab) {
            Log.d("DiaryListDebug", "selectMainTab: clearing diarySelectedEntryId (was=$diarySelectedEntryId)")
            diarySelectedEntryId = null
            membersNestedOpen = false
        }
        selectedTab = tab
    }

    fun setDiarySelectedEntryId(id: Int?) {
        Log.d("DiaryListDebug", "setDiarySelectedEntryId: -> $id (previous=$diarySelectedEntryId)")
        diarySelectedEntryId = id
    }

    /** Wenn jemand die Mitglieder‑Galerie anfordert, wechsle zu Members und öffne die Nested‑Ansicht. */
    LaunchedEffect(openMemberGalleryRequested) {
        if (openMemberGalleryRequested) {
            selectMainTab(MainTab.Members)
            membersNestedOpen = true
            onConsumeOpenMemberGallery()
        }
    }

    if (useWideMainNav) {
        Row(modifier = Modifier.fillMaxSize()) {
            MainNavigationRail(
                selectedTab = selectedTab,
                onTabSelected = { selectMainTab(it) },
                permissions = clubState.currentPermissions,
                dependencies = dependencies,
                onOpenClubAdminInSettings = { section ->
                    billingOrdersSection = null
                    settingsStammdatenRequest = null
                    settingsClubAdminRequest = section
                    selectMainTab(MainTab.Settings)
                },
                onOpenStammdatenInSettings = { section ->
                    billingOrdersSection = null
                    settingsClubAdminRequest = null
                    settingsStammdatenRequest = section
                    selectMainTab(MainTab.Settings)
                },
                onOpenGlobalOrders = {
                    settingsClubAdminRequest = null
                    settingsStammdatenRequest = null
                    billingOrdersSection = BillingOrdersSection.Orders
                    selectMainTab(MainTab.Settings)
                },
                onOpenBilling = {
                    settingsClubAdminRequest = null
                    settingsStammdatenRequest = null
                    billingOrdersSection = BillingOrdersSection.Billing
                    selectMainTab(MainTab.Settings)
                },
            )
            Divider(
                color = MaterialTheme.colors.onSurface.copy(alpha = 0.12f),
                modifier = Modifier.fillMaxHeight().width(1.dp),
            )
            Box(modifier = Modifier.weight(1f).fillMaxHeight()) {
                    MainTabContent(
                    selectedTab = selectedTab,
                    dependencies = dependencies,
                    onNavigateTab = { selectMainTab(it) },
                    diarySelectedEntryId = diarySelectedEntryId,
                    onDiarySelectedEntryId = { setDiarySelectedEntryId(it) },
                        onMembersNestedOpenChange = { membersNestedOpen = it },
                        onOpenMemberPortraitCrop = onRequestOpenMemberPortraitCrop,
                        onOpenMembersGallery = onRequestOpenMemberGallery,
                        openMemberPortraitCropRequested = openMemberPortraitCropRequested,
                        onConsumeOpenMemberPortraitCrop = onConsumeOpenMemberPortraitCrop,
                        openMemberGalleryRequested = openMemberGalleryRequested,
                        onConsumeOpenMemberGallery = onConsumeOpenMemberGallery,
                    billingOrdersSection = billingOrdersSection,
                    onBillingOrdersSectionChange = { billingOrdersSection = it },
                    settingsClubAdminRequest = settingsClubAdminRequest,
                    onConsumeSettingsClubAdminRequest = { settingsClubAdminRequest = null },
                    settingsStammdatenRequest = settingsStammdatenRequest,
                    onConsumeSettingsStammdatenRequest = { settingsStammdatenRequest = null },
                )
                MobileFeedbackButton(
                    modifier = Modifier.align(Alignment.TopEnd).padding(12.dp),
                    onClick = { feedbackOpen = true },
                )
            }
        }
    } else {
        Column(modifier = Modifier.fillMaxSize()) {
            Box(modifier = Modifier.weight(1f).fillMaxWidth()) {
                MainTabContent(
                    selectedTab = selectedTab,
                    dependencies = dependencies,
                    onNavigateTab = { selectMainTab(it) },
                    diarySelectedEntryId = diarySelectedEntryId,
                    onDiarySelectedEntryId = { setDiarySelectedEntryId(it) },
                    onMembersNestedOpenChange = { membersNestedOpen = it },
                    onOpenMemberPortraitCrop = onRequestOpenMemberPortraitCrop,
                    onOpenMembersGallery = onRequestOpenMemberGallery,
                    openMemberPortraitCropRequested = openMemberPortraitCropRequested,
                    onConsumeOpenMemberPortraitCrop = onConsumeOpenMemberPortraitCrop,
                    openMemberGalleryRequested = openMemberGalleryRequested,
                    onConsumeOpenMemberGallery = onConsumeOpenMemberGallery,
                    billingOrdersSection = billingOrdersSection,
                    onBillingOrdersSectionChange = { billingOrdersSection = it },
                    settingsClubAdminRequest = settingsClubAdminRequest,
                    onConsumeSettingsClubAdminRequest = { settingsClubAdminRequest = null },
                    settingsStammdatenRequest = settingsStammdatenRequest,
                    onConsumeSettingsStammdatenRequest = { settingsStammdatenRequest = null },
                )
                MobileFeedbackButton(
                    modifier = Modifier.align(Alignment.TopEnd).padding(12.dp),
                    onClick = { feedbackOpen = true },
                )
            }
            BottomNavigation(
                backgroundColor = MaterialTheme.colors.surface,
                contentColor = MaterialTheme.colors.onSurface,
                elevation = 8.dp,
                modifier = Modifier.navigationBarsPadding(),
            ) {
                visibleTabs.forEach { tab ->
                    BottomNavigationItem(
                        icon = { MainBottomTabIcon(tab = tab, contentDescription = tabTitle(tab)) },
                        label = {
                            Text(
                                tabTitle(tab),
                                maxLines = 1,
                                overflow = TextOverflow.Ellipsis,
                                fontSize = 12.sp,
                            )
                        },
                        selected = selectedTab == tab,
                        alwaysShowLabel = false,
                        selectedContentColor = MaterialTheme.colors.primary,
                        unselectedContentColor = MaterialTheme.colors.onSurface.copy(alpha = 0.55f),
                        onClick = { selectMainTab(tab) },
                    )
                }
            }
        }
    }

    if (feedbackOpen) {
        MobileFeedbackDialog(
            dependencies = dependencies,
            screenTitle = currentScreenTitle,
            onDismiss = { feedbackOpen = false },
        )
    }
}

@Composable
private fun MobileFeedbackButton(
    modifier: Modifier = Modifier,
    onClick: () -> Unit,
) {
    OutlinedButton(
        onClick = onClick,
        modifier = modifier.heightIn(min = 40.dp),
    ) {
        Text("Feedback")
    }
}

@Composable
private fun MobileFeedbackDialog(
    dependencies: AppDependencies,
    screenTitle: String,
    onDismiss: () -> Unit,
) {
    val clubState by dependencies.clubManager.state.collectAsState()
    val scope = rememberCoroutineScope()
    val context = LocalContext.current
    var message by remember { mutableStateOf("") }
    var sending by remember { mutableStateOf(false) }
    var error by remember { mutableStateOf<String?>(null) }

    StableAlertDialog(
        onDismissRequest = { if (!sending) onDismiss() },
        title = { Text("Feedback senden") },
        text = {
            Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                Text("Seite: $screenTitle", style = MaterialTheme.typography.caption)
                OutlinedTextField(
                    value = message,
                    onValueChange = { message = it },
                    label = { Text("Was funktioniert nicht oder könnte besser sein?") },
                    minLines = 5,
                    enabled = !sending,
                    modifier = Modifier.fillMaxWidth(),
                )
                error?.let { Text(it, color = MaterialTheme.colors.error) }
            }
        },
        confirmButton = {
            TextButton(
                enabled = !sending && message.trim().isNotBlank(),
                onClick = {
                    scope.launch {
                        sending = true
                        error = null
                        val result = runCatching {
                            dependencies.feedbackApi.send(
                                MobileFeedbackBody(
                                    message = message.trim(),
                                    screen = screenTitle,
                                    clubId = clubState.currentClubId,
                                    appVersion = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
                                    backendBaseUrl = BuildConfig.BACKEND_BASE_URL,
                                ),
                            )
                        }
                        sending = false
                        if (result.isSuccess) {
                            Toast.makeText(context, "Feedback gesendet", Toast.LENGTH_SHORT).show()
                            onDismiss()
                        } else {
                            error = result.exceptionOrNull()?.message ?: "Feedback konnte nicht gesendet werden."
                        }
                    }
                },
            ) { Text(if (sending) "Sende..." else "Senden") }
        },
        dismissButton = {
            TextButton(enabled = !sending, onClick = onDismiss) { Text("Abbrechen") }
        },
    )
}

@Composable
private fun MainTabContent(
    selectedTab: MainTab,
    dependencies: AppDependencies,
    onNavigateTab: (MainTab) -> Unit,
    diarySelectedEntryId: Int?,
    onDiarySelectedEntryId: (Int?) -> Unit,
    onMembersNestedOpenChange: (Boolean) -> Unit,
    onOpenMemberPortraitCrop: () -> Unit,
    onOpenMembersGallery: () -> Unit,
    openMemberPortraitCropRequested: Boolean,
    onConsumeOpenMemberPortraitCrop: () -> Unit,
    openMemberGalleryRequested: Boolean,
    onConsumeOpenMemberGallery: () -> Unit,
    billingOrdersSection: BillingOrdersSection?,
    onBillingOrdersSectionChange: (BillingOrdersSection?) -> Unit,
    settingsClubAdminRequest: ClubAdminSettingsSection?,
    onConsumeSettingsClubAdminRequest: () -> Unit,
    settingsStammdatenRequest: ClubStammdatenSection?,
    onConsumeSettingsStammdatenRequest: () -> Unit,
) {
    when (selectedTab) {
        MainTab.Home -> HomeScreen(dependencies = dependencies)
        MainTab.Diary -> DiaryListScreen(
            dependencies = dependencies,
            selectedEntryId = diarySelectedEntryId,
            onSelectedEntryId = onDiarySelectedEntryId,
            onOpenMemberPortraitCrop = onOpenMemberPortraitCrop,
            onOpenMembersGallery = onOpenMembersGallery,
        )
        MainTab.Members -> MembersScreen(
            dependencies = dependencies,
            onNestedOpenChange = onMembersNestedOpenChange,
            openMemberPortraitCropRequested = openMemberPortraitCropRequested,
            onConsumeOpenMemberPortraitCrop = onConsumeOpenMemberPortraitCrop,
            openMemberGalleryRequested = openMemberGalleryRequested,
            onConsumeOpenMemberGallery = onConsumeOpenMemberGallery,
        )
        MainTab.Schedule -> ScheduleScreen(dependencies)
        MainTab.FriendlyMatches -> ScheduleScreen(dependencies, friendlyOnly = true)
        MainTab.Calendar -> CalendarScreen(
            dependencies = dependencies,
            onOpenDiaryDate = { id ->
                // Wichtig: zuerst zum Tagebuch wechseln, danach die Auswahl setzen.
                // Wenn die Auswahl zuerst gesetzt wird, wird sie in selectMainTab()
                // zurückgesetzt, weil dort die Auswahl beim Tabwechsel gelöscht wird.
                onNavigateTab(MainTab.Diary)
                onDiarySelectedEntryId(id)
            },
            onOpenDiaryTab = { onNavigateTab(MainTab.Diary) },
            onOpenSchedule = { onNavigateTab(MainTab.Schedule) },
            onOpenFriendlyMatches = { onNavigateTab(MainTab.FriendlyMatches) },
            onOpenTournaments = { onNavigateTab(MainTab.Tournaments) },
            onOpenOfficialParticipations = { onNavigateTab(MainTab.OfficialParticipations) },
        )
        MainTab.Tournaments -> TournamentsScreen(dependencies)
        MainTab.OfficialParticipations -> OfficialTournamentsMenuRoute(
            dependencies = dependencies,
            onNavigateBack = { onNavigateTab(MainTab.Tournaments) },
        )
        MainTab.Stats -> TrainingStatsScreen(dependencies)
        MainTab.Settings -> SettingsScreen(
            dependencies = dependencies,
            billingOrdersSection = billingOrdersSection,
            onBillingOrdersSectionChange = onBillingOrdersSectionChange,
            settingsClubAdminRequest = settingsClubAdminRequest,
            onConsumeSettingsClubAdminRequest = onConsumeSettingsClubAdminRequest,
            settingsStammdatenRequest = settingsStammdatenRequest,
            onConsumeSettingsStammdatenRequest = onConsumeSettingsStammdatenRequest,
        )
    }
}

@Composable
private fun HomeScreen(
    dependencies: AppDependencies,
) {
    val clubState by dependencies.clubManager.state.collectAsState()
    val clubId = clubState.currentClubId ?: return
    val clubName = remember(clubState.clubs, clubId) {
        clubState.clubs.find { it.id == clubId }?.name ?: "Verein"
    }
    var clubDetail by remember { mutableStateOf<Club?>(null) }
    var detailError by remember { mutableStateOf<String?>(null) }
    var detailLoading by remember { mutableStateOf(false) }
    LaunchedEffect(clubId) {
        detailLoading = true
        detailError = null
        clubDetail = runCatching { dependencies.clubManager.fetchClubDetail(clubId) }
            .onFailure { detailError = it.message }
            .getOrNull()
        detailLoading = false
    }
    Column(
        modifier = Modifier
            .fillMaxSize()
            .imePadding()
            .navigationBarsPadding()
            .padding(horizontal = ScreenHorizontalPadding, vertical = 16.dp)
            .verticalScroll(rememberScrollState()),
    ) {
        Header(tr("home.welcomeShort", "Willkommen"))
        Text(
            clubName,
            style = MaterialTheme.typography.subtitle1,
            fontWeight = FontWeight.SemiBold,
            modifier = Modifier.padding(bottom = 8.dp),
        )
        Text(
            tr("home.startNavHintWide", "Nutze die Navigation links für Mitglieder, Tagebuch, Kalender und weitere Bereiche."),
            style = MaterialTheme.typography.body2,
            color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f),
            modifier = Modifier.padding(bottom = 16.dp),
        )
        Text(
            tr("home.startNavHintNarrow", "Auf dem Smartphone findest du die Hauptbereiche in der unteren Leiste; weitere Punkte unter „Mehr“."),
            style = MaterialTheme.typography.caption,
            color = MaterialTheme.colors.onSurface.copy(alpha = 0.65f),
            modifier = Modifier.padding(bottom = 16.dp),
        )

        SectionTitle(tr("home.clubSection", "Verein"))
        ErrorText(detailError)
        when {
            detailLoading -> LoadingInline()
            clubDetail != null -> {
                val c = clubDetail!!
                Card(modifier = Modifier.fillMaxWidth(), elevation = 1.dp) {
                    Column(modifier = Modifier.padding(12.dp)) {
                        DetailLine(tr("club.name", "Name"), c.name)
                        c.greetingText?.takeIf { it.isNotBlank() }?.let {
                            Text(it, style = MaterialTheme.typography.body2, modifier = Modifier.padding(top = 8.dp))
                        }
                        c.associationMemberNumber?.takeIf { it.isNotBlank() }?.let {
                            DetailLine(tr("club.associationNumber", "Verbands-Nr."), it)
                        }
                        c.myTischtennisFedNickname?.takeIf { it.isNotBlank() }?.let {
                            DetailLine("MyTischtennis", it)
                        }
                    }
                }
            }
        }
    }
}

@Composable
private fun MainNavigationRail(
    selectedTab: MainTab,
    onTabSelected: (MainTab) -> Unit,
    permissions: UserClubPermissions?,
    dependencies: AppDependencies,
    onOpenClubAdminInSettings: (ClubAdminSettingsSection) -> Unit,
    onOpenStammdatenInSettings: (ClubStammdatenSection) -> Unit,
    onOpenGlobalOrders: () -> Unit,
    onOpenBilling: () -> Unit,
    modifier: Modifier = Modifier,
) {
    var expandedDaily by rememberSaveable { mutableStateOf(true) }
    var expandedCompetitions by rememberSaveable { mutableStateOf(true) }
    var expandedClubSettings by rememberSaveable { mutableStateOf(true) }

    Column(
        modifier = modifier
            .width(228.dp)
            .fillMaxHeight()
            .background(TtAppColors.NavRailBackground)
            .padding(horizontal = 8.dp, vertical = 8.dp),
    ) {
        NavRailRootItem(
            selected = selectedTab == MainTab.Home,
            emoji = "🏠",
            label = tr("navigation.home", "Start"),
            onClick = { onTabSelected(MainTab.Home) },
        )
        Column(
            modifier = Modifier
                .weight(1f)
                .verticalScroll(rememberScrollState()),
        ) {
            if (permissions != null) {
                val p = permissions
                NavRailSectionHeader(
                    title = tr("navigation.dailyBusiness", "Tagesgeschäft"),
                    expanded = expandedDaily,
                    onToggle = { expandedDaily = !expandedDaily },
                )
                if (expandedDaily) {
                    if (p.canReadMembers()) {
                        NavRailLeafItem(
                            emoji = "👥",
                            label = tr("navigation.members", "Mitglieder"),
                            selected = selectedTab == MainTab.Members,
                            onClick = { onTabSelected(MainTab.Members) },
                        )
                    }
                    if (p.canReadDiary()) {
                        NavRailLeafItem(
                            emoji = "📝",
                            label = tr("navigation.diary", "Tagebuch"),
                            selected = selectedTab == MainTab.Diary,
                            onClick = { onTabSelected(MainTab.Diary) },
                        )
                    }
                    if (p.canReadDiary() || p.canReadSchedule() || p.canReadTournaments()) {
                        NavRailLeafItem(
                            emoji = "📆",
                            label = tr("navigation.calendar", "Kalender"),
                            selected = selectedTab == MainTab.Calendar,
                            onClick = { onTabSelected(MainTab.Calendar) },
                        )
                    }
                    if (p.canReadApprovals()) {
                        NavRailLeafItem(
                            emoji = "⏳",
                            label = tr("navigation.approvals", "Freigaben"),
                            selected = false,
                            onClick = { onOpenClubAdminInSettings(ClubAdminSettingsSection.Pending) },
                        )
                    }
                    if (p.canReadStatistics()) {
                        NavRailLeafItem(
                            emoji = "📊",
                            label = tr("navigation.statistics", "Statistik"),
                            selected = selectedTab == MainTab.Stats,
                            onClick = { onTabSelected(MainTab.Stats) },
                        )
                    }
                }

                NavRailSectionHeader(
                    title = tr("navigation.competitions", "Wettbewerb"),
                    expanded = expandedCompetitions,
                    onToggle = { expandedCompetitions = !expandedCompetitions },
                )
                if (expandedCompetitions) {
                    if (p.canReadTournaments()) {
                        NavRailLeafItem(
                            emoji = "🏆",
                            label = tr("navigation.clubTournaments", "Turniere"),
                            selected = selectedTab == MainTab.Tournaments,
                            onClick = { onTabSelected(MainTab.Tournaments) },
                        )
                        NavRailLeafItem(
                            emoji = "📋",
                            label = tr("navigation.tournamentParticipations", "Turnierteilnahmen"),
                            selected = selectedTab == MainTab.OfficialParticipations,
                            onClick = { onTabSelected(MainTab.OfficialParticipations) },
                        )
                    }
                    if (p.canReadSchedule()) {
                        NavRailLeafItem(
                            emoji = "📅",
                            label = tr("navigation.schedule", "Terminplan"),
                            selected = selectedTab == MainTab.Schedule,
                            onClick = { onTabSelected(MainTab.Schedule) },
                        )
                        NavRailLeafItem(
                            emoji = "🤝",
                            label = "Freundschaftsspiele",
                            selected = selectedTab == MainTab.FriendlyMatches,
                            onClick = { onTabSelected(MainTab.FriendlyMatches) },
                        )
                    }
                }

                NavRailSectionHeader(
                    title = tr("navigation.settings", "Einstellungen"),
                    expanded = expandedClubSettings,
                    onToggle = { expandedClubSettings = !expandedClubSettings },
                )
                if (expandedClubSettings) {
                    if (p.canReadClubSettings()) {
                        NavRailLeafItem(
                            emoji = "🏛️",
                            label = tr("navigation.clubSettings", "Vereinseinstellungen"),
                            selected = false,
                            onClick = { onOpenStammdatenInSettings(ClubStammdatenSection.ClubSettings) },
                        )
                    }
                    if (p.canReadPredefinedActivities()) {
                        NavRailLeafItem(
                            emoji = "🎯",
                            label = tr("navigation.predefinedActivities", "Vordefinierte Aktivitäten"),
                            selected = false,
                            onClick = { onOpenStammdatenInSettings(ClubStammdatenSection.PredefinedActivities) },
                        )
                    }
                    if (p.canReadTeams()) {
                        NavRailLeafItem(
                            emoji = "🧩",
                            label = tr("navigation.teamManagement", "Team-Verwaltung"),
                            selected = false,
                            onClick = { onOpenStammdatenInSettings(ClubStammdatenSection.Teams) },
                        )
                    }
                    if (p.canReadMembers()) {
                        NavRailLeafItem(
                            emoji = "🧾",
                            label = tr("navigation.billing", "Abrechnung"),
                            selected = false,
                            onClick = onOpenBilling,
                        )
                    }
                    NavRailLeafItem(
                        emoji = "📦",
                        label = tr("orders.globalTitle", "Bestellungen"),
                        selected = false,
                        onClick = onOpenGlobalOrders,
                    )
                }
            } else {
                Text(
                    tr("mobile.permissionsLoading", "Berechtigungen werden geladen …"),
                    style = MaterialTheme.typography.caption,
                    color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
                    modifier = Modifier.padding(8.dp),
                )
            }
        }
        NavRailRootItem(
            selected = selectedTab == MainTab.Settings,
            emoji = "⚙️",
            label = tr("mobile.more", "Mehr"),
            onClick = { onTabSelected(MainTab.Settings) },
        )
    }
}

@Composable
private fun NavRailRootItem(
    selected: Boolean,
    emoji: String,
    label: String,
    onClick: () -> Unit,
) {
    Surface(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 4.dp)
            .clickable(onClick = onClick),
        color = if (selected) {
            MaterialTheme.colors.primary.copy(alpha = 0.12f)
        } else {
            MaterialTheme.colors.surface
        },
        elevation = 0.dp,
    ) {
        Column(
            modifier = Modifier.padding(vertical = 10.dp, horizontal = 4.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
        ) {
            Text(
                text = emoji,
                style = MaterialTheme.typography.h6.copy(fontSize = 22.sp),
                color = if (selected) {
                    MaterialTheme.colors.primary
                } else {
                    MaterialTheme.colors.onSurface.copy(alpha = 0.75f)
                },
                modifier = Modifier.semantics { contentDescription = label },
            )
            Spacer(modifier = Modifier.height(4.dp))
            Text(
                text = label,
                style = MaterialTheme.typography.caption,
                color = if (selected) {
                    MaterialTheme.colors.primary
                } else {
                    MaterialTheme.colors.onSurface.copy(alpha = 0.87f)
                },
            )
        }
    }
}

@Composable
private fun NavRailSectionHeader(
    title: String,
    expanded: Boolean,
    onToggle: () -> Unit,
) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .clickable(onClick = onToggle)
            .padding(vertical = 8.dp, horizontal = 4.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Icon(
            imageVector = if (expanded) Icons.Filled.ExpandLess else Icons.Filled.ExpandMore,
            contentDescription = if (expanded) {
                tr("mobile.collapseSection", "Abschnitt einklappen") + ": $title"
            } else {
                tr("mobile.expandSection", "Abschnitt ausklappen") + ": $title"
            },
            modifier = Modifier.size(18.dp),
            tint = MaterialTheme.colors.onSurface.copy(alpha = 0.7f),
        )
        Text(
            text = title,
            style = MaterialTheme.typography.caption,
            fontWeight = FontWeight.Bold,
            color = MaterialTheme.colors.onSurface.copy(alpha = 0.85f),
            modifier = Modifier.padding(start = 4.dp),
        )
    }
}

@Composable
private fun NavRailLeafItem(
    label: String,
    selected: Boolean,
    onClick: () -> Unit,
    emoji: String? = null,
) {
    Surface(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 2.dp, horizontal = 4.dp)
            .semantics(mergeDescendants = true) {
                contentDescription = label
            }
            .clickable(onClick = onClick),
        color = if (selected) {
            MaterialTheme.colors.primary.copy(alpha = 0.1f)
        } else {
            Color.Transparent
        },
        elevation = 0.dp,
    ) {
        Row(
            modifier = Modifier.padding(vertical = 8.dp, horizontal = 12.dp),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            emoji?.let { sym ->
                Text(
                    text = sym,
                    style = MaterialTheme.typography.body2.copy(fontSize = 16.sp),
                    modifier = Modifier.padding(end = 8.dp),
                )
            }
            Text(
                text = label,
                style = MaterialTheme.typography.body2,
                fontWeight = if (selected) FontWeight.SemiBold else FontWeight.Normal,
                color = if (selected) MaterialTheme.colors.primary else MaterialTheme.colors.onSurface,
                modifier = Modifier.weight(1f),
                maxLines = 3,
            )
        }
    }
}

@Composable
private fun MainBottomTabIcon(tab: MainTab, contentDescription: String) {
    Text(
        text = mainTabEmoji(tab),
        style = MaterialTheme.typography.body2.copy(fontSize = 20.sp),
        modifier = Modifier.semantics { this.contentDescription = contentDescription },
    )
}

private fun mainTabEmoji(tab: MainTab): String = when (tab) {
    MainTab.Home -> "🏠"
    MainTab.Members -> "👥"
    MainTab.Diary -> "📝"
    MainTab.Calendar -> "📆"
    MainTab.Stats -> "📊"
    MainTab.Tournaments -> "🏆"
    MainTab.OfficialParticipations -> "📋"
    MainTab.Schedule -> "📅"
    MainTab.FriendlyMatches -> "🤝"
    MainTab.Settings -> "⚙️"
}

@Composable
private fun ScreenTopBar(
    title: String,
    onBack: () -> Unit,
    hint: String? = null,
) {
    val backLabel = tr("common.back", "Zurück")
    Column(modifier = Modifier.fillMaxWidth()) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .heightIn(min = 56.dp)
                .padding(vertical = 2.dp),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            IconButton(
                onClick = onBack,
                modifier = Modifier.size(48.dp),
            ) {
                Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = backLabel)
            }
            Text(title, style = MaterialTheme.typography.h6, modifier = Modifier.weight(1f))
        }
        hint?.let {
            Text(
                it,
                style = MaterialTheme.typography.caption,
                color = MaterialTheme.colors.onSurface.copy(alpha = 0.65f),
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(start = 4.dp, end = 4.dp, bottom = 8.dp),
            )
        }
    }
}

@Composable
private fun AuthFlowHost(dependencies: AppDependencies) {
    var route by rememberSaveable { mutableStateOf(AUTH_ROUTE_LOGIN) }
    val goLogin = { route = AUTH_ROUTE_LOGIN }
    when (route) {
        AUTH_ROUTE_REGISTER -> RegisterScreen(dependencies = dependencies, onBack = goLogin)
        AUTH_ROUTE_FORGOT -> ForgotPasswordScreen(dependencies = dependencies, onBack = goLogin)
        AUTH_ROUTE_RESET -> ResetPasswordScreen(dependencies = dependencies, onBack = goLogin)
        AUTH_ROUTE_ACTIVATE -> ActivateAccountScreen(dependencies = dependencies, onBack = goLogin)
        else -> LoginScreen(
            dependencies = dependencies,
            onLogin = { email, password ->
                dependencies.authManager.login(email, password)
                dependencies.clubManager.loadClubs()
            },
            onRegister = { route = AUTH_ROUTE_REGISTER },
            onForgotPassword = { route = AUTH_ROUTE_FORGOT },
            onResetPasswordWithToken = { route = AUTH_ROUTE_RESET },
            onActivateAccount = { route = AUTH_ROUTE_ACTIVATE },
        )
    }
}

@Composable
private fun LoginScreen(
    dependencies: AppDependencies,
    onLogin: suspend (String, String) -> Unit,
    onRegister: () -> Unit = {},
    onForgotPassword: () -> Unit = {},
    onResetPasswordWithToken: () -> Unit = {},
    onActivateAccount: () -> Unit = {},
) {
    var email by rememberSaveable { mutableStateOf("") }
    var password by rememberSaveable { mutableStateOf("") }
    var error by rememberSaveable { mutableStateOf<String?>(null) }
    var isLoading by rememberSaveable { mutableStateOf(false) }
    val requiredMessage = tr("mobile.emailAndPasswordRequired", "E-Mail und Passwort sind erforderlich")
    val loginFailedMessage = tr("mobile.loginFailed", "Login fehlgeschlagen")

    FormScaffold(
        title = tr("app.title", "Trainingstagebuch"),
        subtitle = tr("mobile.loginSubtitle", "Melde dich mit E-Mail und Passwort an – wie in der Web-App."),
    ) {
        OutlinedTextField(
            value = email,
            onValueChange = { email = it },
            label = { Text("E-Mail") },
            modifier = Modifier.fillMaxWidth(),
            singleLine = true,
        )
        OutlinedTextField(
            value = password,
            onValueChange = { password = it },
            label = { Text("Passwort") },
            modifier = Modifier.fillMaxWidth(),
            singleLine = true,
            visualTransformation = PasswordVisualTransformation(),
        )
        ErrorText(error)
        Button(
            onClick = {
                if (email.isBlank() || password.isBlank()) {
                    error = requiredMessage
                    return@Button
                }
                isLoading = true
                error = null
                dependencies.applicationScope.launch {
                    runCatching { onLogin(email.trim(), password) }
                        .onFailure { error = it.message ?: loginFailedMessage }
                    isLoading = false
                }
            },
            enabled = !isLoading,
            modifier = Modifier
                .fillMaxWidth()
                .heightIn(min = TouchMinHeight),
        ) {
            Text(if (isLoading) tr("mobile.loginInProgress", "Anmelden...") else tr("auth.login", "Login"))
        }
        TextButton(onClick = onRegister, modifier = Modifier.fillMaxWidth()) {
            Text(tr("auth.register", "Registrieren"))
        }
        TextButton(onClick = onForgotPassword, modifier = Modifier.fillMaxWidth()) {
            Text(tr("auth.forgotPassword", "Passwort vergessen"))
        }
        TextButton(onClick = onResetPasswordWithToken, modifier = Modifier.fillMaxWidth()) {
            Text(tr("mobile.resetPasswordWithToken", "Neues Passwort (Code aus E-Mail)"))
        }
        TextButton(onClick = onActivateAccount, modifier = Modifier.fillMaxWidth()) {
            Text(tr("mobile.activateAccount", "Account aktivieren"))
        }
    }
}

@Composable
private fun RegisterScreen(dependencies: AppDependencies, onBack: () -> Unit) {
    var email by rememberSaveable { mutableStateOf("") }
    var password by rememberSaveable { mutableStateOf("") }
    var error by rememberSaveable { mutableStateOf<String?>(null) }
    var isLoading by rememberSaveable { mutableStateOf(false) }
    var success by rememberSaveable { mutableStateOf(false) }
    val scroll = rememberScrollState()
    val emailPwdRequired = tr("mobile.emailAndPasswordRequired", "E-Mail und Passwort sind erforderlich")
    val registerFailed = tr("auth.registerFailed", "Registrierung fehlgeschlagen")

    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(scroll)
            .imePadding()
            .navigationBarsPadding()
            .padding(horizontal = ScreenHorizontalPadding, vertical = 20.dp),
    ) {
        ScreenTopBar(title = tr("auth.register", "Registrieren"), onBack = onBack)
        Spacer(modifier = Modifier.height(16.dp))
        if (!success) {
            OutlinedTextField(
                value = email,
                onValueChange = { email = it },
                label = { Text("E-Mail") },
                modifier = Modifier.fillMaxWidth(),
                singleLine = true,
            )
            OutlinedTextField(
                value = password,
                onValueChange = { password = it },
                label = { Text("Passwort") },
                modifier = Modifier.fillMaxWidth(),
                singleLine = true,
                visualTransformation = PasswordVisualTransformation(),
            )
            ErrorText(error)
            Button(
                onClick = {
                    if (email.isBlank() || password.isBlank()) {
                        error = emailPwdRequired
                        return@Button
                    }
                    isLoading = true
                    error = null
                    dependencies.applicationScope.launch {
                        runCatching { dependencies.publicAuthApi.register(email.trim(), password) }
                            .onSuccess { success = true }
                            .onFailure { error = it.message ?: registerFailed }
                        isLoading = false
                    }
                },
                enabled = !isLoading,
                modifier = Modifier
                    .fillMaxWidth()
                    .heightIn(min = TouchMinHeight),
            ) {
                Text(if (isLoading) "…" else tr("auth.register", "Registrieren"))
            }
        } else {
            Text(
                tr("mobile.registerCheckEmail", "Bitte bestätige deine E-Mail-Adresse über den Link in der Nachricht."),
                modifier = Modifier.padding(vertical = 8.dp),
            )
            Button(
                onClick = onBack,
                modifier = Modifier
                    .fillMaxWidth()
                    .heightIn(min = TouchMinHeight),
            ) {
                Text(tr("auth.toLogin", "Zum Login"))
            }
        }
    }
}

@Composable
private fun ForgotPasswordScreen(dependencies: AppDependencies, onBack: () -> Unit) {
    var email by rememberSaveable { mutableStateOf("") }
    var error by rememberSaveable { mutableStateOf<String?>(null) }
    var isLoading by rememberSaveable { mutableStateOf(false) }
    var sent by rememberSaveable { mutableStateOf(false) }
    val scroll = rememberScrollState()
    val emailRequired = tr("mobile.emailRequired", "E-Mail ist erforderlich")
    val resetRequestFailed = tr("auth.resetRequestFailed", "Anfrage fehlgeschlagen")

    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(scroll)
            .imePadding()
            .navigationBarsPadding()
            .padding(horizontal = ScreenHorizontalPadding, vertical = 20.dp),
    ) {
        ScreenTopBar(title = tr("auth.forgotPassword", "Passwort vergessen"), onBack = onBack)
        Spacer(modifier = Modifier.height(16.dp))
        if (!sent) {
            Text(tr("auth.forgotPasswordDescription", "Wir senden dir einen Link zum Zurücksetzen."), modifier = Modifier.padding(bottom = 8.dp))
            OutlinedTextField(
                value = email,
                onValueChange = { email = it },
                label = { Text("E-Mail") },
                modifier = Modifier.fillMaxWidth(),
                singleLine = true,
            )
            ErrorText(error)
            Button(
                onClick = {
                    if (email.isBlank()) {
                        error = emailRequired
                        return@Button
                    }
                    isLoading = true
                    error = null
                    dependencies.applicationScope.launch {
                        runCatching { dependencies.publicAuthApi.forgotPassword(email.trim()) }
                            .onSuccess { sent = true }
                            .onFailure { error = it.message ?: resetRequestFailed }
                        isLoading = false
                    }
                },
                enabled = !isLoading,
                modifier = Modifier
                    .fillMaxWidth()
                    .heightIn(min = TouchMinHeight),
            ) {
                Text(if (isLoading) tr("auth.sending", "Senden…") else tr("auth.sendResetLink", "Link senden"))
            }
        } else {
            Text(tr("auth.resetEmailSent", "Wenn ein Konto existiert, erhältst du eine E-Mail."), modifier = Modifier.padding(vertical = 8.dp))
            Button(
                onClick = onBack,
                modifier = Modifier
                    .fillMaxWidth()
                    .heightIn(min = TouchMinHeight),
            ) {
                Text(tr("auth.toLogin", "Zum Login"))
            }
        }
    }
}

@Composable
private fun ResetPasswordScreen(dependencies: AppDependencies, onBack: () -> Unit) {
    var token by rememberSaveable { mutableStateOf("") }
    var password by rememberSaveable { mutableStateOf("") }
    var password2 by rememberSaveable { mutableStateOf("") }
    var error by rememberSaveable { mutableStateOf<String?>(null) }
    var isLoading by rememberSaveable { mutableStateOf(false) }
    var success by rememberSaveable { mutableStateOf(false) }
    val scroll = rememberScrollState()
    val tokenPwdRequired = tr("mobile.tokenAndPasswordRequired", "Code und Passwort sind erforderlich")
    val passwordMismatch = tr("mobile.passwordMismatch", "Passwörter stimmen nicht überein")
    val resetFailed = tr("mobile.resetFailed", "Zurücksetzen fehlgeschlagen")

    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(scroll)
            .imePadding()
            .navigationBarsPadding()
            .padding(horizontal = ScreenHorizontalPadding, vertical = 20.dp),
    ) {
        ScreenTopBar(title = tr("mobile.resetPasswordTitle", "Neues Passwort setzen"), onBack = onBack)
        Spacer(modifier = Modifier.height(16.dp))
        if (!success) {
            OutlinedTextField(
                value = token,
                onValueChange = { token = it },
                label = { Text(tr("mobile.resetToken", "Code aus der E-Mail")) },
                modifier = Modifier.fillMaxWidth(),
                singleLine = true,
            )
            OutlinedTextField(
                value = password,
                onValueChange = { password = it },
                label = { Text(tr("auth.password", "Neues Passwort")) },
                modifier = Modifier.fillMaxWidth(),
                singleLine = true,
                visualTransformation = PasswordVisualTransformation(),
            )
            OutlinedTextField(
                value = password2,
                onValueChange = { password2 = it },
                label = { Text(tr("mobile.passwordRepeat", "Passwort wiederholen")) },
                modifier = Modifier.fillMaxWidth(),
                singleLine = true,
                visualTransformation = PasswordVisualTransformation(),
            )
            ErrorText(error)
            Button(
                onClick = {
                    if (token.isBlank() || password.isBlank()) {
                        error = tokenPwdRequired
                        return@Button
                    }
                    if (password != password2) {
                        error = passwordMismatch
                        return@Button
                    }
                    isLoading = true
                    error = null
                    dependencies.applicationScope.launch {
                        runCatching { dependencies.publicAuthApi.resetPassword(token.trim(), password) }
                            .onSuccess { success = true }
                            .onFailure { error = it.message ?: resetFailed }
                        isLoading = false
                    }
                },
                enabled = !isLoading,
                modifier = Modifier
                    .fillMaxWidth()
                    .heightIn(min = TouchMinHeight),
            ) {
                Text(tr("mobile.savePassword", "Passwort speichern"))
            }
        } else {
            Text(tr("mobile.passwordChanged", "Passwort geändert. Bitte neu anmelden."), modifier = Modifier.padding(vertical = 8.dp))
            Button(
                onClick = onBack,
                modifier = Modifier
                    .fillMaxWidth()
                    .heightIn(min = TouchMinHeight),
            ) {
                Text(tr("auth.toLogin", "Zum Login"))
            }
        }
    }
}

@Composable
private fun ActivateAccountScreen(dependencies: AppDependencies, onBack: () -> Unit) {
    var code by rememberSaveable { mutableStateOf("") }
    var error by rememberSaveable { mutableStateOf<String?>(null) }
    var isLoading by rememberSaveable { mutableStateOf(false) }
    var success by rememberSaveable { mutableStateOf(false) }
    val scroll = rememberScrollState()
    val codeRequired = tr("mobile.codeRequired", "Code ist erforderlich")
    val activateFailed = tr("mobile.activateFailed", "Aktivierung fehlgeschlagen")

    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(scroll)
            .imePadding()
            .navigationBarsPadding()
            .padding(horizontal = ScreenHorizontalPadding, vertical = 20.dp),
    ) {
        ScreenTopBar(title = tr("mobile.activateAccount", "Account aktivieren"), onBack = onBack)
        Spacer(modifier = Modifier.height(16.dp))
        if (!success) {
            OutlinedTextField(
                value = code,
                onValueChange = { code = it },
                label = { Text(tr("mobile.activationCode", "Aktivierungscode")) },
                modifier = Modifier.fillMaxWidth(),
                singleLine = true,
            )
            ErrorText(error)
            Button(
                onClick = {
                    if (code.isBlank()) {
                        error = codeRequired
                        return@Button
                    }
                    isLoading = true
                    error = null
                    dependencies.applicationScope.launch {
                        runCatching { dependencies.publicAuthApi.activate(code.trim()) }
                            .onSuccess { success = true }
                            .onFailure { error = it.message ?: activateFailed }
                        isLoading = false
                    }
                },
                enabled = !isLoading,
                modifier = Modifier
                    .fillMaxWidth()
                    .heightIn(min = TouchMinHeight),
            ) {
                Text(tr("mobile.activateSubmit", "Aktivieren"))
            }
        } else {
            Text(tr("mobile.activateSuccess", "Account aktiviert. Du kannst dich anmelden."), modifier = Modifier.padding(vertical = 8.dp))
            Button(
                onClick = onBack,
                modifier = Modifier
                    .fillMaxWidth()
                    .heightIn(min = TouchMinHeight),
            ) {
                Text(tr("auth.toLogin", "Zum Login"))
            }
        }
    }
}

@Composable
private fun ClubSelectScreen(dependencies: AppDependencies) {
    val clubState by dependencies.clubManager.state.collectAsState()
    var requestedClubId by rememberSaveable { mutableStateOf<Int?>(null) }
    var showCreateClub by rememberSaveable { mutableStateOf(false) }
    var newClubName by rememberSaveable { mutableStateOf("") }

    LaunchedEffect(Unit) {
        dependencies.clubManager.loadClubs()
    }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .imePadding()
            .navigationBarsPadding()
            .padding(horizontal = ScreenHorizontalPadding, vertical = 16.dp),
    ) {
        Header(tr("club.select", "Club auswählen"))
        Text(
            tr("club.selectHelp", "Tippe auf „Auswählen“, um mit diesem Verein zu arbeiten. Ohne Zugriff kannst du eine Anfrage stellen."),
            style = MaterialTheme.typography.body2,
            color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f),
            modifier = Modifier.padding(bottom = 12.dp),
        )
        OutlinedButton(
            onClick = { showCreateClub = !showCreateClub },
            enabled = !clubState.isLoading,
            modifier = Modifier
                .fillMaxWidth()
                .heightIn(min = TouchMinHeight),
        ) {
            Text(if (showCreateClub) tr("mobile.cancel", "Abbrechen") else tr("club.createNew", "Neuen Verein anlegen"))
        }
        if (showCreateClub) {
            OutlinedTextField(
                value = newClubName,
                onValueChange = { newClubName = it },
                label = { Text(tr("club.newName", "Name des Vereins")) },
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 8.dp),
                singleLine = true,
                enabled = !clubState.isLoading,
            )
            Button(
                onClick = {
                    val n = newClubName.trim()
                    if (n.isEmpty()) return@Button
                    dependencies.applicationScope.launch {
                        dependencies.clubManager.createClub(n)
                        newClubName = ""
                        showCreateClub = false
                    }
                },
                enabled = !clubState.isLoading && newClubName.isNotBlank(),
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 8.dp)
                    .heightIn(min = TouchMinHeight),
            ) { Text(tr("club.createSubmit", "Verein erstellen")) }
        }
        if (clubState.isLoading) LoadingInline()
        ErrorText(clubState.error)
        LazyColumn(
            modifier = Modifier.weight(1f),
            verticalArrangement = Arrangement.spacedBy(10.dp),
        ) {
            items(clubState.clubs) { club ->
                Card(modifier = Modifier.fillMaxWidth(), elevation = 2.dp) {
                    Column(modifier = Modifier.padding(16.dp)) {
                        Text(club.name, style = MaterialTheme.typography.subtitle1, fontWeight = FontWeight.SemiBold)
                        Spacer(modifier = Modifier.height(10.dp))
                        Row(
                            horizontalArrangement = Arrangement.spacedBy(8.dp),
                            modifier = Modifier.fillMaxWidth(),
                        ) {
                            Button(
                                onClick = { dependencies.applicationScope.launch { dependencies.clubManager.selectClub(club.id) } },
                                modifier = Modifier
                                    .weight(1f)
                                    .heightIn(min = TouchMinHeight),
                            ) {
                                Text(tr("mobile.select", "Auswählen"))
                            }
                            OutlinedButton(
                                onClick = {
                                    dependencies.applicationScope.launch {
                                        dependencies.clubManager.requestAccess(club.id)
                                        requestedClubId = club.id
                                    }
                                },
                                modifier = Modifier
                                    .weight(1f)
                                    .heightIn(min = TouchMinHeight),
                            ) {
                                Text(
                                    if (requestedClubId == club.id) tr("mobile.clubRequested", "Angefragt") else tr("mobile.clubRequest", "Zugriff anfragen"),
                                    maxLines = 2,
                                )
                            }
                        }
                    }
                }
            }
        }
        Spacer(modifier = Modifier.height(12.dp))
        OutlinedButton(
            onClick = { dependencies.applicationScope.launch { dependencies.authManager.logout() } },
            modifier = Modifier
                .fillMaxWidth()
                .heightIn(min = TouchMinHeight),
        ) {
            Text(tr("auth.logout", "Abmelden"))
        }
    }
}

@Composable
private fun DiaryListScreen(
    dependencies: AppDependencies,
    selectedEntryId: Int?,
    onSelectedEntryId: (Int?) -> Unit,
    onOpenMemberPortraitCrop: () -> Unit,
    onOpenMembersGallery: () -> Unit,
) {
    val clubState by dependencies.clubManager.state.collectAsState()
    val diaryState by dependencies.diaryManager.state.collectAsState()
    val languageState by dependencies.languageManager.state.collectAsState()
    val androidCtx = LocalContext.current
    val clubId = clubState.currentClubId ?: return
    var dayMenuExpanded by rememberSaveable { mutableStateOf(false) }
    var showNewDateDialog by rememberSaveable { mutableStateOf(false) }
    var newDiaryDateStr by rememberSaveable { mutableStateOf("") }
    var newDiaryStart by rememberSaveable { mutableStateOf("") }
    var newDiaryEnd by rememberSaveable { mutableStateOf("") }
    var newDiaryExcludeFromBilling by rememberSaveable { mutableStateOf(false) }
    val newDateScope = rememberCoroutineScope()
    var newDateScheduleGroups by remember { mutableStateOf<List<TrainingGroupDto>>(emptyList()) }
    var newDateScheduleLoading by remember { mutableStateOf(false) }
    var newDateScheduleLoadError by remember { mutableStateOf<String?>(null) }
    var newDateGroupMenuExpanded by remember { mutableStateOf(false) }
    var selectedNewDateGroupId by remember { mutableStateOf<Int?>(null) }
    var quickCreateBusy by remember { mutableStateOf(false) }
    var autoSelectedEntryForClubId by rememberSaveable(clubId) { mutableStateOf<Int?>(null) }
    var suppressAutoSelect by rememberSaveable(clubId) { mutableStateOf(false) }

    val diaryDatesNormKey = remember(diaryState.dates) {
        diaryState.dates.map { it.date.take(10).trim() }.sorted().joinToString("|")
    }
    val existingDiaryDatesNorm = remember(diaryDatesNormKey) {
        diaryDatesNormKey.split("|").filter { it.isNotBlank() }.toSet()
    }
    val newDateSlotSuggestion = remember(selectedNewDateGroupId, newDateScheduleGroups, existingDiaryDatesNorm) {
        val gid = selectedNewDateGroupId ?: return@remember null
        val g = newDateScheduleGroups.find { it.id == gid } ?: return@remember null
        nextDiarySlotFromTrainingTimes(g.trainingTimes, existingDiaryDatesNorm)
    }

    /** Sobald eine Gruppe gewählt ist und ein Vorschlag existiert: Felder wie in der Web-App füllen (nächster freier Wochentag). */
    LaunchedEffect(selectedNewDateGroupId, newDateSlotSuggestion, showNewDateDialog) {
        if (!showNewDateDialog) return@LaunchedEffect
        val sug = newDateSlotSuggestion
        if (selectedNewDateGroupId != null && sug != null) {
            newDiaryDateStr = sug.date
            newDiaryStart = sug.trainingStart
            newDiaryEnd = sug.trainingEnd
        }
    }

    LaunchedEffect(showNewDateDialog, clubId) {
        if (!showNewDateDialog) return@LaunchedEffect
        newDateScheduleLoadError = null
        newDateScheduleLoading = true
        newDateScheduleGroups = runCatching { dependencies.membersManager.trainingScheduleGroups(clubId) }
            .fold(
                onSuccess = { it },
                onFailure = { t ->
                    newDateScheduleLoadError = t.message ?: t.javaClass.simpleName
                    emptyList()
                },
            )
        newDateScheduleLoading = false
    }

    LaunchedEffect(clubId) {
        // Lade die Tagebuch-Daten. Früher wurde hier die Auswahl zurückgesetzt
        // (onSelectedEntryId(null)), was dazu führte, dass eine vom Kalender
        // vorgewählte `diarySelectedEntryId` nicht sofort gerendert wurde.
        dependencies.diaryManager.loadDates(clubId)
    }

    LaunchedEffect(diaryState.dates, selectedEntryId) {
        try {
            if (selectedEntryId != null) {
                val found = diaryState.dates.any { it.id == selectedEntryId }
                if (found) {
                    // Force parent/state sync in case order of events produced a stale state.
                    onSelectedEntryId(selectedEntryId)
                }
            } else if (!suppressAutoSelect && autoSelectedEntryForClubId == null) {
                // No selection set — if we have dates, default to the first one so details show immediately.
                if (diaryState.dates.isNotEmpty()) {
                    val firstId = diaryState.dates.first().id
                    autoSelectedEntryForClubId = firstId
                    onSelectedEntryId(firstId)
                }
            }
        } catch (t: Throwable) {
            // ignore
        }
    }

    val selectedEntry = diaryState.dates.firstOrNull { it.id == selectedEntryId }
    if (selectedEntry != null) {
        DiaryDetailScreen(
            clubId = clubId,
            entry = selectedEntry,
            dependencies = dependencies,
            onBack = {
                suppressAutoSelect = true
                onSelectedEntryId(null)
            },
            onOpenMemberPortraitCrop = onOpenMemberPortraitCrop,
            onOpenMembersGallery = onOpenMembersGallery,
        )
        return
    }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .imePadding()
            .navigationBarsPadding()
            .padding(horizontal = ScreenHorizontalPadding, vertical = 16.dp),
    ) {
        Header(tr("diary.title", "Tagebuch"))
        if (diaryState.fromOfflineCache) {
            Text(
                tr("mobile.offlineCacheHint", "Keine Verbindung – zuletzt geladene Daten."),
                style = MaterialTheme.typography.caption,
                color = MaterialTheme.colors.onSurface.copy(alpha = 0.65f),
                modifier = Modifier.padding(bottom = 8.dp),
            )
        }
        Text(
            tr("diary.listHintCompact", "Trainingstag wählen und direkt bearbeiten."),
            style = MaterialTheme.typography.body2,
            color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f),
            modifier = Modifier.padding(bottom = 12.dp),
        )
        Row(
            horizontalArrangement = Arrangement.spacedBy(10.dp),
            modifier = Modifier.fillMaxWidth(),
        ) {
            Box(modifier = Modifier.weight(1f)) {
                OutlinedButton(
                    onClick = { dayMenuExpanded = true },
                    modifier = Modifier.fillMaxWidth().heightIn(min = TouchMinHeight),
                ) {
                    val label = selectedEntryId?.let { id ->
                        diaryState.dates.firstOrNull { it.id == id }?.let { "${formatDate(it.date)}  ${formatTimeRange(it.trainingStart, it.trainingEnd)}" }
                    } ?: diaryState.dates.firstOrNull()?.let { "${formatDate(it.date)}  ${formatTimeRange(it.trainingStart, it.trainingEnd)}" }
                        ?: tr("diary.pickDay", "Trainingstag wählen")
                    Text(label, maxLines = 2)
                }
                DropdownMenu(expanded = dayMenuExpanded, onDismissRequest = { dayMenuExpanded = false }) {
                    diaryState.dates.forEach { d ->
                        DropdownMenuItem(onClick = {
                            dayMenuExpanded = false
                            onSelectedEntryId(d.id)
                        }) {
                            Text("${formatDate(d.date)}  ${formatTimeRange(d.trainingStart, d.trainingEnd)}")
                        }
                    }
                }
            }
            Button(
                onClick = {
                    dependencies.diaryManager.clearError()
                    selectedNewDateGroupId = null
                    newDateGroupMenuExpanded = false
                    val tmpl = diaryState.dates.firstOrNull()
                    newDiaryDateStr = todayIsoDate()
                    newDiaryStart = diaryTimeForFormField(tmpl?.trainingStart).ifBlank { "17:30" }
                    newDiaryEnd = diaryTimeForFormField(tmpl?.trainingEnd).ifBlank { "19:30" }
                    newDiaryExcludeFromBilling = false
                    showNewDateDialog = true
                },
                modifier = Modifier.heightIn(min = TouchMinHeight),
            ) { Text(tr("mobile.new", "Neu")) }
            OutlinedButton(
                onClick = {
                    if (quickCreateBusy || diaryState.isLoading) return@OutlinedButton
                    newDateScope.launch {
                        quickCreateBusy = true
                        dependencies.diaryManager.clearError()
                        val lang = languageState.currentLanguageCode
                        val groups = runCatching { dependencies.membersManager.trainingScheduleGroups(clubId) }.fold(
                            onSuccess = { it },
                            onFailure = { t ->
                                quickCreateBusy = false
                                Toast.makeText(
                                    androidCtx,
                                    t.message?.takeIf { it.isNotBlank() }
                                        ?: MobileStrings.get(lang, "diary.quickCreateFailed", "Der Trainingstag konnte nicht angelegt werden."),
                                    Toast.LENGTH_LONG,
                                ).show()
                                return@launch
                            },
                        )
                        val slot = findNextQuickDiarySlotAcrossGroups(groups, existingDiaryDatesNorm)
                        if (slot == null) {
                            quickCreateBusy = false
                            Toast.makeText(
                                androidCtx,
                                MobileStrings.get(lang, "diary.quickCreateNoSlot", "Kein freier Trainingstermin gefunden."),
                                Toast.LENGTH_LONG,
                            ).show()
                            return@launch
                        }
                        val id = dependencies.diaryManager.createDate(
                            clubId,
                            slot.date,
                            diaryTimeFieldToApi(slot.trainingStart),
                            diaryTimeFieldToApi(slot.trainingEnd),
                            excludeFromBilling = false,
                        )
                        quickCreateBusy = false
                        if (id != null) {
                            onSelectedEntryId(id)
                        } else {
                            val err = dependencies.diaryManager.state.value.error
                                ?: MobileStrings.get(lang, "diary.quickCreateFailed", "Der Trainingstag konnte nicht angelegt werden.")
                            Toast.makeText(androidCtx, err, Toast.LENGTH_LONG).show()
                        }
                    }
                },
                enabled = !quickCreateBusy && !diaryState.isLoading,
                modifier = Modifier.heightIn(min = TouchMinHeight),
            ) { Text(tr("diary.quickCreate", "Schnellanlegen")) }
        }
        if (diaryState.isLoading) LoadingInline()
        ErrorText(diaryState.error)
        if (!diaryState.isLoading && diaryState.dates.isEmpty()) {
            EmptyText(tr("mobile.noDiaryEntries", "Noch keine Tagebuch-Einträge"))
        }
        Spacer(modifier = Modifier.weight(1f))
    }

    if (showNewDateDialog) {
        StableAlertDialog(
            onDismissRequest = {
                if (!diaryState.isLoading) {
                    showNewDateDialog = false
                    selectedNewDateGroupId = null
                    newDateGroupMenuExpanded = false
                }
            },
            title = { Text(tr("diary.createNewDate", "Neuen Trainingstag anlegen")) },
            text = {
                Column(
                    modifier = Modifier
                        .fillMaxWidth()
                        .verticalScroll(rememberScrollState()),
                ) {
                    Text(
                        tr("diary.selectTrainingGroup", "Trainingsgruppe"),
                        style = MaterialTheme.typography.caption,
                        color = MaterialTheme.colors.onSurface.copy(alpha = 0.75f),
                    )
                    Spacer(modifier = Modifier.height(6.dp))
                    if (newDateScheduleLoading) {
                        CircularProgressIndicator(
                            modifier = Modifier
                                .size(28.dp)
                                .padding(vertical = 4.dp),
                            strokeWidth = 3.dp,
                        )
                    }
                    newDateScheduleLoadError?.let { err ->
                        Text(
                            err,
                            color = MaterialTheme.colors.error,
                            style = MaterialTheme.typography.caption,
                            modifier = Modifier.padding(bottom = 8.dp),
                        )
                    }
                    Box(modifier = Modifier.fillMaxWidth()) {
                        OutlinedButton(
                            onClick = { newDateGroupMenuExpanded = true },
                            enabled = !diaryState.isLoading && !newDateScheduleLoading,
                            modifier = Modifier.fillMaxWidth(),
                        ) {
                            val label = selectedNewDateGroupId?.let { gid ->
                                newDateScheduleGroups.find { it.id == gid }?.name?.takeIf { it.isNotBlank() }
                            } ?: tr("diary.selectTrainingGroupPlaceholder", "Gruppe wählen (optional)")
                            Text(label, maxLines = 2, overflow = TextOverflow.Ellipsis)
                        }
                        DropdownMenu(
                            expanded = newDateGroupMenuExpanded,
                            onDismissRequest = { newDateGroupMenuExpanded = false },
                        ) {
                            DropdownMenuItem(
                                onClick = {
                                    selectedNewDateGroupId = null
                                    newDateGroupMenuExpanded = false
                                },
                            ) {
                                Text(tr("diary.noTrainingGroupManual", "Keine / manuell"))
                            }
                            newDateScheduleGroups.forEach { g ->
                                DropdownMenuItem(
                                    onClick = {
                                        selectedNewDateGroupId = g.id
                                        newDateGroupMenuExpanded = false
                                    },
                                ) {
                                    Text(g.name.ifBlank { "Gruppe ${g.id}" })
                                }
                            }
                        }
                    }
                    newDateSlotSuggestion?.let { sug ->
                        Spacer(modifier = Modifier.height(8.dp))
                        Text(
                            tr("diary.suggestion", "Vorschlag"),
                            style = MaterialTheme.typography.caption,
                            fontWeight = FontWeight.SemiBold,
                        )
                        Text(
                            "${tr("diary.nextAppointment", "Nächster Termin")}: ${formatDate(sug.date)}  ${sug.trainingStart} – ${sug.trainingEnd}",
                            style = MaterialTheme.typography.body2,
                            modifier = Modifier.padding(top = 4.dp),
                        )
                        TextButton(
                            onClick = {
                                newDiaryDateStr = sug.date
                                newDiaryStart = sug.trainingStart
                                newDiaryEnd = sug.trainingEnd
                            },
                            enabled = !diaryState.isLoading,
                        ) { Text(tr("diary.applySuggestion", "Vorschlag übernehmen")) }
                    }
                    Divider(modifier = Modifier.padding(vertical = 12.dp))
                    LocalizedDatePickerField(
                        value = newDiaryDateStr,
                        onValueChange = { newDiaryDateStr = it },
                        label = tr("diary.date", "Datum"),
                        enabled = !diaryState.isLoading,
                        modifier = Modifier.fillMaxWidth(),
                    )
                    TextButton(
                        onClick = {
                            newDiaryDateStr = todayIsoDate()
                        },
                        enabled = !diaryState.isLoading,
                    ) { Text(tr("diary.today", "Heute")) }
                    OutlinedTextField(
                        value = newDiaryStart,
                        onValueChange = { newDiaryStart = it },
                        label = { Text(tr("diary.trainingStart", "Trainingsbeginn (HH:mm)")) },
                        singleLine = true,
                        enabled = !diaryState.isLoading,
                        modifier = Modifier.fillMaxWidth(),
                    )
                    OutlinedTextField(
                        value = newDiaryEnd,
                        onValueChange = { newDiaryEnd = it },
                        label = { Text(tr("diary.trainingEnd", "Trainingsende (HH:mm)")) },
                        singleLine = true,
                        enabled = !diaryState.isLoading,
                        modifier = Modifier.fillMaxWidth(),
                    )
                    Row(
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(top = 8.dp),
                        verticalAlignment = Alignment.CenterVertically,
                        horizontalArrangement = Arrangement.SpaceBetween,
                    ) {
                        Text(tr("diary.excludeFromBilling", "Nicht abrechnen"))
                        Switch(
                            checked = newDiaryExcludeFromBilling,
                            onCheckedChange = { newDiaryExcludeFromBilling = it },
                            enabled = !diaryState.isLoading,
                        )
                    }
                    diaryState.error?.let { err ->
                        Text(
                            err,
                            color = MaterialTheme.colors.error,
                            style = MaterialTheme.typography.caption,
                            modifier = Modifier.padding(top = 8.dp),
                        )
                    }
                }
            },
            confirmButton = {
                Button(
                    onClick = {
                        newDateScope.launch {
                            val id = dependencies.diaryManager.createDate(
                                clubId,
                                newDiaryDateStr.trim(),
                                diaryTimeFieldToApi(newDiaryStart),
                                diaryTimeFieldToApi(newDiaryEnd),
                                newDiaryExcludeFromBilling,
                            )
                            if (id != null) {
                                showNewDateDialog = false
                                selectedNewDateGroupId = null
                                newDateGroupMenuExpanded = false
                                onSelectedEntryId(id)
                            }
                        }
                    },
                    enabled = newDiaryDateStr.trim().length >= 8 && !diaryState.isLoading,
                ) { Text(tr("diary.createDate", "Anlegen")) }
            },
            dismissButton = {
                TextButton(
                    onClick = {
                        showNewDateDialog = false
                        selectedNewDateGroupId = null
                        newDateGroupMenuExpanded = false
                    },
                    enabled = !diaryState.isLoading,
                ) { Text(tr("messages.cancel", "Abbrechen")) }
            },
        )
    }
}

@Composable
private fun DiaryDetailScreen(
    clubId: Int,
    entry: DiaryDate,
    dependencies: AppDependencies,
    onBack: () -> Unit,
    onOpenMemberPortraitCrop: () -> Unit,
    onOpenMembersGallery: () -> Unit,
) {
    BackHandler(onBack = onBack)

    val diaryState by dependencies.diaryManager.state.collectAsState()
    val membersState by dependencies.membersManager.state.collectAsState()
    val clubState by dependencies.clubManager.state.collectAsState()
    val canWriteDiary = clubState.currentPermissions?.canWriteDiary() == true
    val canReadDiary = clubState.currentPermissions?.canReadDiary() == true
    val canReadMembers = clubState.currentPermissions?.canReadMembers() == true
    val canWriteMembers = clubState.currentPermissions?.canWriteMembers() == true
    var showEdit by rememberSaveable { mutableStateOf(false) }
    var showDeleteDateConfirm by remember { mutableStateOf(false) }
    var deleteGroupCandidate by remember { mutableStateOf<DiaryPlanGroup?>(null) }
    var note by rememberSaveable { mutableStateOf("") }
    var tagName by rememberSaveable { mutableStateOf("") }
    var planItems by remember { mutableStateOf<List<DiaryDateActivityItem>>(emptyList()) }
    var planLoading by remember { mutableStateOf(false) }
    var planError by remember { mutableStateOf<String?>(null) }
    var planGroups by remember { mutableStateOf<List<DiaryPlanGroup>>(emptyList()) }
    var planMutating by remember { mutableStateOf(false) }
    var planActionError by remember { mutableStateOf<String?>(null) }
    var expandedPlanActionsItemId by remember { mutableStateOf<Int?>(null) }
    var showAddPlanActivity by rememberSaveable { mutableStateOf(false) }
    var showAddPlanGroupActivity by rememberSaveable { mutableStateOf(false) }
    var showAddTrainingGroup by rememberSaveable { mutableStateOf(false) }
    var editingTrainingGroup by remember { mutableStateOf<DiaryPlanGroup?>(null) }
    var editTrainingGroupName by remember { mutableStateOf("") }
    var editTrainingGroupLead by remember { mutableStateOf("") }
    var newTrainingGroupCount by rememberSaveable { mutableStateOf("1") }
    var newPlanActivityName by rememberSaveable { mutableStateOf("") }
    var newPlanDuration by rememberSaveable { mutableStateOf("") }
    var newPlanDurationText by rememberSaveable { mutableStateOf("") }
    var newPlanIsTimeblock by rememberSaveable { mutableStateOf(false) }
    var newPlanTargetGroupId by rememberSaveable { mutableStateOf<Int?>(null) }
    var newPlanGroupActivityGroupId by rememberSaveable { mutableStateOf<Int?>(null) }
    var newTrainingGroupName by rememberSaveable { mutableStateOf("") }
    var newTrainingGroupLead by rememberSaveable { mutableStateOf("") }
    var newScopedActivityName by rememberSaveable { mutableStateOf("") }
    var newScopedDuration by rememberSaveable { mutableStateOf("") }
    var newScopedDurationText by rememberSaveable { mutableStateOf("") }
    var editingPlanItem by remember { mutableStateOf<DiaryDateActivityItem?>(null) }
    var editingNestedPlanItem by remember { mutableStateOf<GroupActivitySummary?>(null) }
    var drawingPlanItem by remember { mutableStateOf<DiaryDateActivityItem?>(null) }
    var drawingNestedPlanItem by remember { mutableStateOf<GroupActivitySummary?>(null) }
    var drawingViewer by remember { mutableStateOf<Pair<String, String>?>(null) }
    var creatingDrawingActivity by remember { mutableStateOf(false) }
    var editPlanName by remember { mutableStateOf("") }
    var editPlanDuration by remember { mutableStateOf("") }
    var editPlanDurationText by remember { mutableStateOf("") }
    var editPlanGroupId by remember { mutableStateOf<Int?>(null) }
    var assigningParticipantsItem by remember { mutableStateOf<DiaryDateActivityItem?>(null) }
    var assigningParticipantIds by remember { mutableStateOf(setOf<Int>()) }
    var assignParticipantsBusy by remember { mutableStateOf(false) }
    var assignParticipantsError by remember { mutableStateOf<String?>(null) }
    var assigningPlanItem by remember { mutableStateOf<DiaryDateActivityItem?>(null) }
    var assignPlanGroupId by remember { mutableStateOf<Int?>(null) }
    var participants by remember { mutableStateOf<List<DiaryTrainingParticipant>>(emptyList()) }
    var participantsLoading by remember { mutableStateOf(false) }
    var participantsError by remember { mutableStateOf<String?>(null) }
    var participantMutating by remember { mutableStateOf(false) }
    var participantGroupMenuMemberId by remember { mutableStateOf<Int?>(null) }
    var showQuickAddMember by remember { mutableStateOf(false) }
    var accidents by remember { mutableStateOf<List<AccidentReportDto>>(emptyList()) }
    var accidentSectionError by remember { mutableStateOf<String?>(null) }
    var newAccidentMemberId by rememberSaveable { mutableStateOf<Int?>(null) }
    var newAccidentDescription by rememberSaveable { mutableStateOf("") }
    var accidentMemberMenu by remember { mutableStateOf(false) }
    var accidentSubmitBusy by remember { mutableStateOf(false) }
    var groupPhotos by remember { mutableStateOf<List<MemberGroupPhotoDto>>(emptyList()) }
    var gallerySectionExpanded by rememberSaveable { mutableStateOf(false) }
    var planImageViewerUrl by remember { mutableStateOf<String?>(null) }
    var newGroupPhotoTitle by rememberSaveable { mutableStateOf("") }
    var newGroupPhotoDescription by rememberSaveable { mutableStateOf("") }
    var groupPhotoBusy by remember { mutableStateOf(false) }
    var freeformActivities by remember { mutableStateOf<List<DiaryFreeformActivity>>(emptyList()) }
    var newFreeformDescription by rememberSaveable { mutableStateOf("") }
    var freeformBusy by remember { mutableStateOf(false) }
    var expandedMemberAssignActivityId by remember { mutableStateOf<Int?>(null) }
    var memberAssignMap by remember { mutableStateOf<Map<Int, Set<Int>>>(emptyMap()) }
    var memberAssignBusy by remember { mutableStateOf(false) }
    var memberNotesSheetMember by remember { mutableStateOf<Member?>(null) }
    var memberNotesList by remember { mutableStateOf<List<DiaryMemberNoteDto>>(emptyList()) }
    var memberTagsList by remember { mutableStateOf<List<DiaryMemberTagLinkDto>>(emptyList()) }
    var memberNotesAvailableTags by remember { mutableStateOf<List<DiaryTag>>(emptyList()) }
    var memberNotesBusy by remember { mutableStateOf(false) }
    var memberNotesError by remember { mutableStateOf<String?>(null) }
    var newMemberContextNote by rememberSaveable { mutableStateOf("") }
    var newMemberContextTagName by rememberSaveable { mutableStateOf("") }
    var memberTagPickMenu by remember { mutableStateOf(false) }
    var newPlanPredefQuery by rememberSaveable { mutableStateOf("") }
    var newPlanPredefResults by remember { mutableStateOf<List<PredefinedActivityDto>>(emptyList()) }
    var newPlanPredefLoading by remember { mutableStateOf(false) }
    var newPlanPickedPredefined by remember { mutableStateOf<PredefinedActivityDto?>(null) }
    var newPlanStandardPredefExpanded by rememberSaveable { mutableStateOf(false) }
    var newPlanStandardPredefList by remember { mutableStateOf<List<PredefinedActivityDto>>(emptyList()) }
    var scopedPredefQuery by rememberSaveable { mutableStateOf("") }
    var scopedPredefResults by remember { mutableStateOf<List<PredefinedActivityDto>>(emptyList()) }
    var scopedPredefLoading by remember { mutableStateOf(false) }
    var scopedPickedPredefined by remember { mutableStateOf<PredefinedActivityDto?>(null) }
    var scopedStandardPredefExpanded by rememberSaveable { mutableStateOf(false) }
    var scopedStandardPredefList by remember { mutableStateOf<List<PredefinedActivityDto>>(emptyList()) }
    var editPlanPredefQuery by rememberSaveable { mutableStateOf("") }
    var editPlanPredefResults by remember { mutableStateOf<List<PredefinedActivityDto>>(emptyList()) }
    var editPlanPredefLoading by remember { mutableStateOf(false) }
    var editPlanPickedPredefinedId by remember { mutableStateOf<Int?>(null) }
    val scroll = rememberScrollState()
    val scope = rememberCoroutineScope()
    val androidContext = LocalContext.current

    var activeDiaryTab by rememberSaveable(entry.id) { mutableStateOf(DiaryDetailTab.Plan) }
    var planGroupFilter by rememberSaveable(entry.id) { mutableStateOf<Int?>(null) }
    var participantSearchQuery by rememberSaveable(entry.id) { mutableStateOf("") }
    var participantFilter by rememberSaveable(entry.id) { mutableStateOf(ParticipantToolbarFilter.All) }

    var yParticipants by remember { mutableStateOf<Int?>(null) }
    var yAccidents by remember { mutableStateOf<Int?>(null) }
    var yGallery by remember { mutableStateOf<Int?>(null) }
    var yDayActivities by remember { mutableStateOf<Int?>(null) }
    var yTrainingPlan by remember { mutableStateOf<Int?>(null) }
    val pickGroupPhoto = rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
        if (uri == null) return@rememberLauncherForActivityResult
        dependencies.applicationScope.launch {
            groupPhotoBusy = true
            try {
                val bytes = androidContext.contentResolver.openInputStream(uri)?.use { it.readBytes() } ?: return@launch
                dependencies.diaryManager.uploadMemberGroupPhoto(
                    clubId,
                    bytes,
                    newGroupPhotoTitle.ifBlank { "Gruppenfoto" },
                    newGroupPhotoDescription,
                )
                groupPhotos = dependencies.diaryManager.listMemberGroupPhotos(clubId)
            } catch (_: Throwable) {
            } finally {
                groupPhotoBusy = false
            }
        }
    }

    // Member portrait picker (used as quick "Aktivitätsbild" upload)
    var pendingMemberPortraitId by remember { mutableStateOf<Int?>(null) }
    val pickMemberPortrait = rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
        if (uri == null || pendingMemberPortraitId == null) return@rememberLauncherForActivityResult
        val mid = pendingMemberPortraitId ?: return@rememberLauncherForActivityResult
        dependencies.applicationScope.launch {
            try {
                val bytes = androidContext.contentResolver.openInputStream(uri)?.use { it.readBytes() } ?: return@launch
                dependencies.membersManager.uploadMemberPortrait(clubId, mid, bytes)
                dependencies.membersManager.loadMembers(clubId)
            } catch (_: Throwable) {
            } finally {
                pendingMemberPortraitId = null
            }
        }
    }

    // Quick dialogs: orders and training stats for a member
    var ordersForMemberId by remember { mutableStateOf<Int?>(null) }
    var ordersList by remember { mutableStateOf<List<de.tsschulz.tt_tagebuch.shared.api.models.MemberOrderDto>>(emptyList()) }
    var ordersLoading by remember { mutableStateOf(false) }
    var ordersError by remember { mutableStateOf<String?>(null) }

    var statsForMemberId by remember { mutableStateOf<Int?>(null) }
    var statsList by remember { mutableStateOf<List<MemberActivityStatDto>>(emptyList()) }
    var statsLastParticipations by remember { mutableStateOf<List<MemberLastParticipationDto>>(emptyList()) }
    var statsLoading by remember { mutableStateOf(false) }
    var statsError by remember { mutableStateOf<String?>(null) }

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

    LaunchedEffect(entry.id) {
        expandedMemberAssignActivityId = null
        memberAssignMap = emptyMap()
    }

    LaunchedEffect(clubId, entry.id) {
        planLoading = true
        participantsLoading = true
        planError = null
        participantsError = null
        try {
            coroutineScope {
                val activities = async { dependencies.diaryManager.fetchDateActivities(clubId, entry.id) }
                val groups = async { runCatching { dependencies.diaryManager.listTrainingGroups(clubId, entry.id) }.getOrElse { emptyList() } }
                val parts = async { dependencies.diaryManager.listTrainingParticipants(entry.id) }
                val freeform = async { runCatching { dependencies.diaryManager.listFreeformActivities(entry.id) }.getOrElse { emptyList() } }
                val accList = async { runCatching { dependencies.diaryManager.listAccidents(clubId, entry.id) } }
                val photos = async { runCatching { dependencies.diaryManager.listMemberGroupPhotos(clubId) }.getOrElse { emptyList() } }
                planItems = activities.await()
                val loadedGroups = groups.await()
                planGroups = loadedGroups
                participants = parts.await()
                freeformActivities = freeform.await()
                groupPhotos = photos.await()
                accList.await().fold(
                    onSuccess = {
                        accidents = it
                        accidentSectionError = null
                    },
                    onFailure = { t ->
                        accidents = emptyList()
                        accidentSectionError = t.message
                    },
                )
            }
        } catch (t: Throwable) {
            val msg = t.message ?: "Daten konnten nicht geladen werden"
            planError = msg
            participantsError = msg
            planItems = emptyList()
            planGroups = emptyList()
            participants = emptyList()
            freeformActivities = emptyList()
            accidents = emptyList()
            groupPhotos = emptyList()
        }
        planLoading = false
        participantsLoading = false
    }

    LaunchedEffect(clubId, entry.id) {
        dependencies.socketService.events.collectLatest { (event, data) ->
            val eventDateId = data.optInt("dateId", -1)
            val affectsThisDate = eventDateId == entry.id
            when (event) {
                "member:changed" -> {
                    dependencies.membersManager.loadMembers(clubId)
                    participants = dependencies.diaryManager.listTrainingParticipants(entry.id)
                }
                "participant:added",
                "participant:removed",
                "participant:updated",
                -> if (affectsThisDate) {
                    participants = dependencies.diaryManager.listTrainingParticipants(entry.id)
                    dependencies.membersManager.loadMembers(clubId)
                }
                "activity:changed",
                "group:changed",
                -> if (affectsThisDate) {
                    coroutineScope {
                        val activities = async { dependencies.diaryManager.fetchDateActivities(clubId, entry.id) }
                        val groups = async {
                            runCatching { dependencies.diaryManager.listTrainingGroups(clubId, entry.id) }
                                .getOrElse { emptyList() }
                        }
                        planItems = activities.await()
                        planGroups = groups.await()
                    }
                }
                "activity:member:added",
                "activity:member:removed",
                -> if (affectsThisDate) {
                    planItems = dependencies.diaryManager.fetchDateActivities(clubId, entry.id)
                }
                "diary:note:added",
                "diary:note:updated",
                "diary:note:deleted",
                "diary:tag:added",
                "diary:tag:removed",
                "diary:date:updated",
                -> if (affectsThisDate) {
                    dependencies.diaryManager.loadDates(clubId)
                }
            }
        }
    }

    val refreshPlanAfterMutation: () -> Unit = {
        dependencies.applicationScope.launch {
            planMutating = true
            planActionError = null
            try {
                coroutineScope {
                    val activities = async { dependencies.diaryManager.fetchDateActivities(clubId, entry.id) }
                    val groups = async { runCatching { dependencies.diaryManager.listTrainingGroups(clubId, entry.id) }.getOrElse { emptyList() } }
                    val parts = async { dependencies.diaryManager.listTrainingParticipants(entry.id) }
                    val freeform = async { runCatching { dependencies.diaryManager.listFreeformActivities(entry.id) }.getOrElse { emptyList() } }
                    val accList = async { runCatching { dependencies.diaryManager.listAccidents(clubId, entry.id) } }
                    val photos = async { runCatching { dependencies.diaryManager.listMemberGroupPhotos(clubId) }.getOrElse { emptyList() } }
                    planItems = activities.await()
                    val refreshedGroups = groups.await()
                    planGroups = refreshedGroups
                    participants = parts.await()
                    freeformActivities = freeform.await()
                    groupPhotos = photos.await()
                    accList.await().fold(
                        onSuccess = {
                            accidents = it
                            accidentSectionError = null
                        },
                        onFailure = { t ->
                            accidents = emptyList()
                            accidentSectionError = t.message
                        },
                    )
                }
            } catch (t: Throwable) {
                planActionError = t.message ?: "Plan konnte nicht aktualisiert werden"
            } finally {
                planMutating = false
            }
        }
    }

    LaunchedEffect(editingPlanItem?.id) {
        val item = editingPlanItem ?: return@LaunchedEffect
        editPlanName = item.predefinedActivity.displayLabel()
        editPlanDuration = item.duration?.toString().orEmpty()
        editPlanDurationText = item.durationText.orEmpty()
        editPlanGroupId = item.groupId
        editPlanPredefQuery = ""
        editPlanPredefResults = emptyList()
        editPlanPickedPredefinedId = null
    }

    LaunchedEffect(memberNotesSheetMember?.id, entry.id, clubId) {
        val m = memberNotesSheetMember ?: return@LaunchedEffect
        memberNotesBusy = true
        memberNotesError = null
        try {
            coroutineScope {
                val n = async { dependencies.diaryManager.listMemberNotes(clubId, entry.id, m.id) }
                val t = async { dependencies.diaryManager.listMemberTags(clubId, entry.id, m.id) }
                val a = async { dependencies.diaryManager.listAllDiaryTags() }
                memberNotesList = n.await()
                memberTagsList = t.await()
                memberNotesAvailableTags = a.await()
            }
        } catch (t: Throwable) {
            memberNotesError = t.message ?: "Fehler beim Laden"
        } finally {
            memberNotesBusy = false
        }
    }

    LaunchedEffect(newPlanPredefQuery, showAddPlanActivity) {
        if (!showAddPlanActivity) return@LaunchedEffect
        delay(350)
        val q = newPlanPredefQuery.trim()
        if (q.length < 2) {
            newPlanPredefResults = emptyList()
            return@LaunchedEffect
        }
        newPlanPredefLoading = true
        try {
            newPlanPredefResults = dependencies.diaryManager.searchPredefinedActivities(q)
        } catch (_: Throwable) {
            newPlanPredefResults = emptyList()
        } finally {
            newPlanPredefLoading = false
        }
    }

    LaunchedEffect(newPlanStandardPredefExpanded, showAddPlanActivity) {
        if (!showAddPlanActivity || !newPlanStandardPredefExpanded) return@LaunchedEffect
        if (newPlanStandardPredefList.isNotEmpty()) return@LaunchedEffect
        try {
            newPlanStandardPredefList = dependencies.diaryManager.listPredefinedActivities("standard")
        } catch (_: Throwable) {
            newPlanStandardPredefList = emptyList()
        }
    }

    LaunchedEffect(scopedPredefQuery, showAddPlanGroupActivity) {
        if (!showAddPlanGroupActivity) return@LaunchedEffect
        delay(350)
        val q = scopedPredefQuery.trim()
        if (q.length < 2) {
            scopedPredefResults = emptyList()
            return@LaunchedEffect
        }
        scopedPredefLoading = true
        try {
            scopedPredefResults = dependencies.diaryManager.searchPredefinedActivities(q)
        } catch (_: Throwable) {
            scopedPredefResults = emptyList()
        } finally {
            scopedPredefLoading = false
        }
    }

    LaunchedEffect(scopedStandardPredefExpanded, showAddPlanGroupActivity) {
        if (!showAddPlanGroupActivity || !scopedStandardPredefExpanded) return@LaunchedEffect
        if (scopedStandardPredefList.isNotEmpty()) return@LaunchedEffect
        try {
            scopedStandardPredefList = dependencies.diaryManager.listPredefinedActivities("standard")
        } catch (_: Throwable) {
            scopedStandardPredefList = emptyList()
        }
    }

    LaunchedEffect(editPlanPredefQuery, editingPlanItem?.id) {
        if (editingPlanItem == null) return@LaunchedEffect
        delay(350)
        val q = editPlanPredefQuery.trim()
        if (q.length < 2) {
            editPlanPredefResults = emptyList()
            return@LaunchedEffect
        }
        editPlanPredefLoading = true
        try {
            editPlanPredefResults = dependencies.diaryManager.searchPredefinedActivities(q)
        } catch (_: Throwable) {
            editPlanPredefResults = emptyList()
        } finally {
            editPlanPredefLoading = false
        }
    }

    val activeMembers = remember(membersState.members) {
        membersState.members.filter { it.active }.sortedWith(
            compareBy({ it.firstName.lowercase() }, { it.lastName.lowercase() }),
        )
    }

    val presentMembersForPlan = remember(activeMembers, participants) {
        activeMembers.filter { m ->
            participants.find { it.memberId == m.id }?.isPresentParticipant() == true
        }
    }
    val excusedActiveMembersCount = remember(activeMembers, participants) {
        activeMembers.count { member ->
            participants.find { it.memberId == member.id }?.attendanceStatus?.lowercase() == "excused"
        }
    }
    val availableActiveMembersCount = remember(activeMembers, excusedActiveMembersCount) {
        (activeMembers.size - excusedActiveMembersCount).coerceAtLeast(0)
    }
    val filteredMembersForToolbar = remember(activeMembers, participants, participantSearchQuery, participantFilter) {
        val q = participantSearchQuery.trim().lowercase()
        activeMembers.filter { m ->
            val p = participants.find { it.memberId == m.id }
            val status = p?.attendanceStatus?.lowercase()
            val isPresent = p?.isPresentParticipant() == true
            val matchesQuery = q.isBlank() || listOf(m.fullName(), m.phone.orEmpty(), m.email.orEmpty())
                .joinToString(" ")
                .lowercase()
                .contains(q)
            val matchesFilter = when (participantFilter) {
                ParticipantToolbarFilter.All -> true
                ParticipantToolbarFilter.Present -> isPresent
                ParticipantToolbarFilter.Absent -> !isPresent
                ParticipantToolbarFilter.Excused -> status == "excused"
                ParticipantToolbarFilter.Cancelled -> status == "cancelled"
                ParticipantToolbarFilter.Test -> m.testMembership == true
            }
            matchesQuery && matchesFilter
        }
    }

    val canDeleteCurrentDate = remember(planItems, participants, freeformActivities, accidents, entry.diaryNotes) {
        planItems.isEmpty() &&
            participants.isEmpty() &&
            freeformActivities.isEmpty() &&
            accidents.isEmpty() &&
            entry.diaryNotes.isEmpty()
    }

    val accidentSelectableMembers = remember(activeMembers, participants) {
        // Prefer currently listed participants; fallback to active members.
        val participantIds = participants.map { it.memberId }.toSet()
        val fromParticipants = activeMembers.filter { it.id in participantIds }
        if (fromParticipants.isNotEmpty()) fromParticipants else activeMembers
    }

    LaunchedEffect(accidentSelectableMembers, newAccidentMemberId) {
        if (accidentSelectableMembers.isEmpty()) {
            newAccidentMemberId = null
            return@LaunchedEffect
        }
        if (newAccidentMemberId == null || accidentSelectableMembers.none { it.id == newAccidentMemberId }) {
            newAccidentMemberId = accidentSelectableMembers.first().id
        }
    }

    val narrowPhone = LocalConfiguration.current.screenWidthDp < MAIN_NAV_RAIL_MIN_WIDTH_DP

    ordersForMemberId?.let { memberId ->
        val member = activeMembers.firstOrNull { it.id == memberId }
        StableAlertDialog(
            onDismissRequest = {
                ordersForMemberId = null
                ordersList = emptyList()
                ordersError = null
            },
            title = { Text(member?.fullName() ?: tr("orders", "Bestellungen")) },
            text = {
                Column(modifier = Modifier.fillMaxWidth().heightIn(max = 420.dp)) {
                    when {
                        ordersLoading -> {
                            CircularProgressIndicator(
                                modifier = Modifier.padding(24.dp).align(Alignment.CenterHorizontally),
                            )
                        }
                        ordersError != null -> Text(ordersError.orEmpty(), color = MaterialTheme.colors.error)
                        ordersList.isEmpty() -> Text(
                            tr("orders.noOrdersMember", "Für dieses Mitglied gibt es noch keine Bestellungen."),
                        )
                        else -> {
                            ordersList.forEach { order ->
                                Column(modifier = Modifier.fillMaxWidth().padding(6.dp)) {
                                    Text(order.item, fontWeight = FontWeight.SemiBold)
                                    Text(order.status, style = MaterialTheme.typography.caption)
                                    Divider(modifier = Modifier.padding(vertical = 6.dp))
                                }
                            }
                        }
                    }
                }
            },
            confirmButton = {
                TextButton(
                    onClick = {
                        ordersForMemberId = null
                        ordersList = emptyList()
                    },
                ) { Text(tr("common.close", "Schließen")) }
            },
        )
    }

    statsForMemberId?.let { memberId ->
        val member = activeMembers.firstOrNull { it.id == memberId }
        StableAlertDialog(
            onDismissRequest = {
                statsForMemberId = null
                statsList = emptyList()
                statsLastParticipations = emptyList()
                statsError = null
            },
            title = { Text(member?.fullName() ?: tr("trainingStats.title", "Übungsstatistik")) },
            text = {
                Column(modifier = Modifier.fillMaxWidth().heightIn(max = 420.dp)) {
                    when {
                        statsLoading -> {
                            CircularProgressIndicator(
                                modifier = Modifier.padding(24.dp).align(Alignment.CenterHorizontally),
                            )
                        }
                        statsError != null -> Text(statsError.orEmpty(), color = MaterialTheme.colors.error)
                        statsList.isEmpty() -> Text(tr("trainingStats.noData", "Keine Statistikdaten gefunden."))
                        else -> {
                            if (statsLastParticipations.isNotEmpty()) {
                                Text(tr("members.lastParticipations", "Letzte Teilnahmen"), fontWeight = FontWeight.SemiBold)
                                statsLastParticipations.take(3).forEach { participation ->
                                    Text(
                                        "${participation.date.orEmpty().take(10)}  ${participation.activityName ?: participation.activityFullName.orEmpty()}",
                                        style = MaterialTheme.typography.caption,
                                    )
                                }
                                Divider(modifier = Modifier.padding(vertical = 6.dp))
                            }
                            statsList.forEach { stat ->
                                Column(modifier = Modifier.fillMaxWidth().padding(6.dp)) {
                                    Text(stat.name ?: stat.code ?: "?", fontWeight = FontWeight.SemiBold)
                                    Text("${stat.count}x", style = MaterialTheme.typography.caption)
                                    Divider(modifier = Modifier.padding(vertical = 6.dp))
                                }
                            }
                        }
                    }
                }
            },
            confirmButton = {
                TextButton(
                    onClick = {
                        statsForMemberId = null
                        statsList = emptyList()
                        statsLastParticipations = emptyList()
                    },
                ) { Text(tr("common.close", "Schließen")) }
            },
        )
    }

    DiaryDeleteConfirmDialogs(
        showDateConfirm = showDeleteDateConfirm,
        groupCandidate = deleteGroupCandidate,
        canDeleteDate = canDeleteCurrentDate,
        clubId = clubId,
        diaryDateId = entry.id,
        dependencies = dependencies,
        onDismissDate = { showDeleteDateConfirm = false },
        onDismissGroup = { deleteGroupCandidate = null },
        onDateDeleted = onBack,
        onGroupBusy = { planMutating = it },
        onGroupUpdated = { items, groups ->
            planItems = items
            planGroups = groups
        },
        onGroupError = { planActionError = it },
    )

    Column(
        modifier = Modifier
            .fillMaxSize()
            .imePadding()
            .navigationBarsPadding()
            .padding(horizontal = ScreenHorizontalPadding, vertical = 12.dp),
    ) {
        ScreenTopBar(
            title = tr("mobile.entry", "Eintrag"),
            onBack = onBack,
            hint = if (narrowPhone) {
                tr("mobile.detailNavHint", "Zurück: Pfeil oben links oder die System-Taste „Zurück“.")
            } else {
                null
            },
        )
        Text(formatDate(entry.date), style = MaterialTheme.typography.subtitle1)
        Text(formatTimeRange(entry.trainingStart, entry.trainingEnd))

        if (activeDiaryTab == DiaryDetailTab.Participants) {
            Surface(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 8.dp, bottom = 4.dp),
                elevation = 2.dp,
            ) {
                Column(modifier = Modifier.padding(8.dp)) {
                    OutlinedTextField(
                        value = participantSearchQuery,
                        onValueChange = { participantSearchQuery = it },
                        label = { Text(tr("mobile.searchParticipants", "Teilnehmer suchen")) },
                        modifier = Modifier.fillMaxWidth(),
                        singleLine = true,
                    )
                    Row(
                        modifier = Modifier
                            .padding(top = 8.dp)
                            .horizontalScroll(rememberScrollState()),
                        horizontalArrangement = Arrangement.spacedBy(6.dp),
                    ) {
                        val chips = listOf(
                            ParticipantToolbarFilter.All to tr("common.all", "Alle"),
                            ParticipantToolbarFilter.Present to tr("mobile.present", "Anwesend"),
                            ParticipantToolbarFilter.Absent to tr("mobile.absent", "Abwesend"),
                            ParticipantToolbarFilter.Excused to tr("mobile.participantExcused", "Entschuldigt"),
                            ParticipantToolbarFilter.Cancelled to tr("mobile.participantCancelled", "Abgesagt"),
                            ParticipantToolbarFilter.Test to tr("members.testMember", "Test"),
                        )
                        chips.forEach { (filter, label) ->
                            OutlinedButton(
                                onClick = { participantFilter = filter },
                                modifier = Modifier.heightIn(min = 36.dp),
                            ) {
                                Text(
                                    if (participantFilter == filter) "✓ $label" else label,
                                    fontWeight = if (participantFilter == filter) FontWeight.Bold else FontWeight.Normal,
                                )
                            }
                        }
                    }
                }
            }
        }

        Column(
            modifier = Modifier
                .weight(1f)
                .fillMaxWidth()
                .verticalScroll(scroll),
        ) {
            ErrorText(diaryState.error)
            planError?.let { ErrorText(it) }
            participantsError?.let { ErrorText(it) }
            if (diaryState.isLoading || planLoading || participantsLoading) LoadingInline()
            if (clubState.currentPermissions != null && !canWriteDiary) {
                Text(
                    tr("diary.readOnlyHint", "Sie haben nur Lesezugriff auf das Tagebuch. Änderungen sind nicht möglich."),
                    style = MaterialTheme.typography.body2,
                    color = MaterialTheme.colors.onSurface.copy(alpha = 0.75f),
                    modifier = Modifier.padding(bottom = 8.dp),
                )
            }

            Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) {
                Button(
                    onClick = { showEdit = !showEdit },
                    enabled = canWriteDiary,
                    modifier = Modifier
                        .weight(1f)
                        .heightIn(min = TouchMinHeight),
                ) { Text(if (showEdit) tr("mobile.cancel", "Abbrechen") else tr("mobile.editTimes", "Zeiten bearbeiten")) }
                OutlinedButton(
                    onClick = { showDeleteDateConfirm = true },
                    enabled = canWriteDiary && canDeleteCurrentDate,
                    modifier = Modifier.heightIn(min = TouchMinHeight),
                ) {
                    Text(tr("common.delete", "Löschen"))
                }
                if (canReadMembers) {
                    var showMembersGalleryDialog by remember { mutableStateOf(false) }
                    OutlinedButton(
                        onClick = { showMembersGalleryDialog = true },
                        modifier = Modifier.heightIn(min = TouchMinHeight),
                    ) { Text(tr("members.gallery", "Mitglieder‑Galerie")) }

                    if (showMembersGalleryDialog) {
                        val dialogMembers = activeMembers.filter { m ->
                            val participantRow = participants.find { it.memberId == m.id }
                            val isExcused = participantRow?.attendanceStatus?.lowercase() == "excused"
                            !isExcused &&
                                ((m.hasImage == true) || (m.imageUrl != null) || (m.primaryImageId != null) || (m.images.isNotEmpty()))
                        }
                        StableAlertDialog(
                            onDismissRequest = { showMembersGalleryDialog = false },
                            text = {
                                Column(modifier = Modifier.fillMaxWidth()) {
                                    Text(tr("members.gallery", "Mitglieder‑Galerie"), style = MaterialTheme.typography.h6)
                                    Spacer(modifier = Modifier.height(8.dp))
                                    val gridState = rememberLazyGridState()
                                    Box(
                                        modifier = Modifier
                                            .fillMaxWidth()
                                            .heightIn(min = 240.dp, max = 420.dp)
                                            .clipToBounds(),
                                    ) {
                                        LazyVerticalGrid(
                                            columns = GridCells.Adaptive(minSize = 120.dp),
                                            state = gridState,
                                            modifier = Modifier
                                                .fillMaxSize()
                                                .padding(4.dp),
                                        ) {
                                            items(dialogMembers) { m ->
                                                val pRow = participants.find { it.memberId == m.id }
                                                val checked = pRow?.isPresentParticipant() == true
                                                Box(
                                                    modifier = Modifier
                                                        .padding(4.dp)
                                                        .aspectRatio(1f)
                                                        .clipToBounds()
                                                        .clickable {
                                                            if (!canWriteDiary) return@clickable
                                                            dependencies.applicationScope.launch {
                                                                try {
                                                                    if (!checked) dependencies.diaryManager.addTrainingParticipant(entry.id, m.id)
                                                                    else dependencies.diaryManager.removeTrainingParticipant(entry.id, m.id)
                                                                    participants = dependencies.diaryManager.listTrainingParticipants(entry.id)
                                                                } catch (_: Throwable) {
                                                                }
                                                            }
                                                        }
                                                ) {
                                                    AuthenticatedAsyncImage(
                                                        imageUrl = dependencies.apiConfig.toAbsoluteUrl(memberProfileImagePath(clubId, m.id)),
                                                        authHeaders = dependencies.diaryAuthHeaders(),
                                                        modifier = Modifier.fillMaxSize(),
                                                        contentDescription = m.fullName(),
                                                        contentScale = androidx.compose.ui.layout.ContentScale.Crop,
                                                    )
                                                    Box(
                                                        modifier = Modifier
                                                            .align(Alignment.BottomStart)
                                                            .fillMaxWidth()
                                                            .background(Color.Black.copy(alpha = 0.45f))
                                                            .padding(6.dp)
                                                    ) {
                                                        Text(
                                                            text = m.fullName(),
                                                            color = Color.White,
                                                            maxLines = 1,
                                                            overflow = TextOverflow.Ellipsis,
                                                            fontSize = 12.sp,
                                                        )
                                                    }
                                                    Checkbox(
                                                        checked = checked,
                                                        onCheckedChange = {
                                                            dependencies.applicationScope.launch {
                                                                try {
                                                                    if (it) dependencies.diaryManager.addTrainingParticipant(entry.id, m.id)
                                                                    else dependencies.diaryManager.removeTrainingParticipant(entry.id, m.id)
                                                                    participants = dependencies.diaryManager.listTrainingParticipants(entry.id)
                                                                } catch (_: Throwable) {
                                                                }
                                                            }
                                                        },
                                                        enabled = canWriteDiary,
                                                        modifier = Modifier.align(Alignment.TopEnd).padding(6.dp)
                                                    )
                                                }
                                            }
                                        }
                                    }
                                }
                            },
                            confirmButton = {
                                TextButton(onClick = { showMembersGalleryDialog = false }) { Text(tr("mobile.close", "Schließen")) }
                            },
                        )
                    }
                }
                if (canWriteMembers) {
                    OutlinedButton(
                        onClick = { onOpenMemberPortraitCrop() },
                        modifier = Modifier.heightIn(min = TouchMinHeight),
                    ) { Text(tr("members.groupPortraitCropTitle", "Portrait aus Gruppenfoto")) }
                }
            }
            if (showEdit) {
                DiaryEditForm(
                    initialDate = entry.date.take(10),
                    initialStart = entry.trainingStart.orEmpty(),
                    initialEnd = entry.trainingEnd.orEmpty(),
                    initialExcludeFromBilling = entry.excludeFromBilling,
                    excludeFromBillingLabel = tr("diary.excludeFromBilling", "Nicht abrechnen"),
                    submitLabel = tr("common.save", "Speichern"),
                    onExcludeFromBillingChange = { _, start, end, excludeFromBilling ->
                        dependencies.applicationScope.launch {
                            dependencies.diaryManager.updateTimes(clubId, entry.id, start, end, excludeFromBilling)
                        }
                    },
                    onSubmit = { _, start, end, excludeFromBilling ->
                        dependencies.applicationScope.launch {
                            dependencies.diaryManager.updateTimes(clubId, entry.id, start, end, excludeFromBilling)
                            showEdit = false
                        }
                    },
                )
            }

            // Tabs wie in der Webapp (Plan / Teilnehmer / Aktivitäten).
            TabRow(
                selectedTabIndex = activeDiaryTab.ordinal,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 8.dp, bottom = 4.dp),
            ) {
                Tab(
                    selected = activeDiaryTab == DiaryDetailTab.Plan,
                    onClick = { activeDiaryTab = DiaryDetailTab.Plan },
                    text = { Text("${tr("diary.trainingPlan", "Trainingsplan")} (${planItems.size})") },
                )
                Tab(
                    selected = activeDiaryTab == DiaryDetailTab.Participants,
                    onClick = { activeDiaryTab = DiaryDetailTab.Participants },
                    text = {
                        Text(
                            "${tr("diary.participants", "Teilnehmer")} (${participants.count { it.isPresentParticipant() }})",
                        )
                    },
                )
                Tab(
                    selected = activeDiaryTab == DiaryDetailTab.Activities,
                    onClick = { activeDiaryTab = DiaryDetailTab.Activities },
                    text = { Text("${tr("diary.activities", "Aktivitäten")} (${freeformActivities.size})") },
                )
            }

            if (activeDiaryTab == DiaryDetailTab.Participants) {
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .onGloballyPositioned { coords -> yParticipants = coords.positionInParent().y.toInt() }
                    .padding(top = 4.dp, bottom = 4.dp),
            ) {
                SectionTitle(tr("diary.participants", "Trainingsteilnehmer"))
            }
            Text(
                "${tr("diary.excusedParticipants", "Entschuldigt")}: $excusedActiveMembersCount | " +
                    "${tr("diary.availableParticipants", "Anwesend möglich")}: $availableActiveMembersCount | " +
                    "${tr("diary.activeMembers", "Aktive Mitglieder")}: ${activeMembers.size}",
                style = MaterialTheme.typography.caption,
                color = MaterialTheme.colors.onSurface.copy(alpha = 0.75f),
                modifier = Modifier.padding(bottom = 6.dp),
            )
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(bottom = 6.dp),
                horizontalArrangement = Arrangement.End,
            ) {
                OutlinedButton(
                    onClick = {
                        yAccidents?.let { target ->
                            scope.launch { scroll.scrollTo((target - 12).coerceAtLeast(0)) }
                        }
                    },
                    enabled = canWriteDiary,
                    modifier = Modifier.heightIn(min = 36.dp),
                ) {
                    Text(tr("diary.addAccidentQuick", "Unfall eintragen"), style = MaterialTheme.typography.caption)
                }
            }
            if (!participantsLoading && participantsError == null && filteredMembersForToolbar.isEmpty()) {
                EmptyText(tr("mobile.noMembers", "Keine Mitglieder gefunden"))
            }
            filteredMembersForToolbar.forEach { member ->
                val pRow = participants.find { it.memberId == member.id }
                val checked = pRow?.isPresentParticipant() == true
                val statusKey = pRow?.attendanceStatus?.lowercase()
                val statusLabel = when (statusKey) {
                    "excused" -> tr("mobile.participantExcused", "Entschuldigt")
                    "cancelled" -> tr("mobile.participantCancelled", "Abgesagt")
                    else -> null
                }
                val actionEnabled = !participantMutating && !diaryState.isLoading
                val toggleThisMember: () -> Unit = t@{
                    if (!canWriteDiary || !actionEnabled) return@t
                    val wantAdd = !checked
                    participantMutating = true
                    dependencies.applicationScope.launch {
                        try {
                            if (wantAdd) {
                                dependencies.diaryManager.addTrainingParticipant(entry.id, member.id)
                            } else {
                                dependencies.diaryManager.removeTrainingParticipant(entry.id, member.id)
                            }
                            participants = dependencies.diaryManager.listTrainingParticipants(entry.id)
                        } finally {
                            participantMutating = false
                        }
                    }
                }
                val setStatus: (String) -> Unit = status@{ targetStatus ->
                    if (!canWriteDiary || !actionEnabled) return@status
                    participantMutating = true
                    dependencies.applicationScope.launch {
                        try {
                            if (statusKey == targetStatus) {
                                dependencies.diaryManager.addTrainingParticipant(entry.id, member.id)
                            } else {
                                if (pRow == null) {
                                    dependencies.diaryManager.addTrainingParticipant(entry.id, member.id)
                                }
                                dependencies.diaryManager.setTrainingParticipantAttendanceStatus(
                                    entry.id,
                                    member.id,
                                    targetStatus,
                                )
                            }
                            participants = dependencies.diaryManager.listTrainingParticipants(entry.id)
                        } finally {
                            participantMutating = false
                        }
                    }
                }
                val openOrders: () -> Unit = {
                    ordersForMemberId = member.id
                    ordersLoading = true
                    ordersError = null
                    dependencies.applicationScope.launch {
                        try {
                            val env = dependencies.memberOrdersApi.listForMember(clubId, member.id)
                            ordersList = env.orders
                        } catch (t: Throwable) {
                            ordersError = t.message
                            ordersList = emptyList()
                        } finally {
                            ordersLoading = false
                        }
                    }
                }
                val openStats: () -> Unit = {
                    statsForMemberId = member.id
                    statsLoading = true
                    statsError = null
                    dependencies.applicationScope.launch {
                        try {
                            coroutineScope {
                                val allStats = async { dependencies.membersManager.memberActivityStats(clubId, member.id, "all") }
                                val recent = async { dependencies.membersManager.memberLastParticipations(clubId, member.id, 3) }
                                statsList = allStats.await()
                                statsLastParticipations = recent.await()
                            }
                        } catch (t: Throwable) {
                            statsError = t.message
                            statsList = emptyList()
                        } finally {
                            statsLoading = false
                        }
                    }
                }
                val openNotes: () -> Unit = {
                    memberNotesSheetMember = member
                    newMemberContextNote = ""
                    newMemberContextTagName = ""
                    memberTagPickMenu = false
                }
                val uploadActivityImage: () -> Unit = {
                    pendingMemberPortraitId = member.id
                    pickMemberPortrait.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
                }
                val actionButtons: @Composable () -> Unit = {
                    val primaryColor = MaterialTheme.colors.primary
                    Row(horizontalArrangement = Arrangement.spacedBy(2.dp)) {
                        IconButton(
                            onClick = { setStatus("excused") },
                            enabled = canWriteDiary && actionEnabled,
                            modifier = Modifier.size(40.dp),
                        ) {
                            Text(
                                "📴",
                                color = if (statusKey == "excused") primaryColor else Color.Unspecified,
                                fontSize = 18.sp,
                            )
                        }
                        IconButton(
                            onClick = uploadActivityImage,
                            enabled = canWriteDiary && actionEnabled,
                            modifier = Modifier.size(40.dp),
                        ) {
                            Text("🖼️", fontSize = 18.sp)
                        }
                        IconButton(
                            onClick = openOrders,
                            enabled = actionEnabled,
                            modifier = Modifier.size(40.dp),
                        ) {
                            Text("📦", fontSize = 18.sp)
                        }
                        IconButton(
                            onClick = openNotes,
                            enabled = canWriteDiary && actionEnabled,
                            modifier = Modifier.size(40.dp),
                        ) {
                            Text("ℹ️", fontSize = 18.sp)
                        }
                        if (member.testMembership == true && member.memberFormHandedOver != true) {
                            IconButton(
                                onClick = {
                                    dependencies.applicationScope.launch {
                                        runCatching { dependencies.membersManager.quickUpdateMemberFormHandedOver(clubId, member.id) }
                                        dependencies.membersManager.loadMembers(clubId)
                                    }
                                },
                                enabled = canWriteMembers && actionEnabled,
                                modifier = Modifier.size(40.dp),
                            ) { Text("F", fontWeight = FontWeight.Bold) }
                        }
                    }
                }
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(vertical = 4.dp),
                    verticalAlignment = Alignment.Top,
                ) {
                    if (canReadMembers) {
                        val mUrl = dependencies.apiConfig.toAbsoluteUrl(memberProfileImagePath(clubId, member.id))
                        val auth = dependencies.diaryAuthHeaders()
                        Box(
                            modifier = Modifier
                                .padding(end = 8.dp, top = 4.dp)
                                .size(40.dp),
                        ) {
                            AuthenticatedAsyncImage(
                                imageUrl = mUrl,
                                authHeaders = auth,
                                modifier = Modifier.fillMaxSize(),
                                contentDescription = member.fullName(),
                            )
                        }
                    }
                    Checkbox(
                        checked = checked,
                        enabled = canWriteDiary && actionEnabled,
                        onCheckedChange = { wantChecked ->
                            if (wantChecked == checked) return@Checkbox
                            toggleThisMember()
                        },
                    )
                    Column(
                        modifier = Modifier.weight(1f),
                    ) {
                        if (narrowPhone) {
                            Text(
                                member.fullName() + if (member.testMembership == true && (member.trainingParticipations ?: 0) >= 3) " !" else "",
                                fontWeight = FontWeight.SemiBold,
                            )
                            statusLabel?.let {
                                Text(
                                    it,
                                    style = MaterialTheme.typography.caption,
                                    color = MaterialTheme.colors.primary,
                                    modifier = Modifier.padding(top = 2.dp),
                                )
                            }
                            actionButtons()
                        } else {
                            Row(
                                modifier = Modifier.fillMaxWidth(),
                                horizontalArrangement = Arrangement.SpaceBetween,
                                verticalAlignment = Alignment.CenterVertically,
                            ) {
                                Column(modifier = Modifier.weight(1f)) {
                                    Text(
                                        member.fullName() + if (member.testMembership == true && (member.trainingParticipations ?: 0) >= 3) " !" else "",
                                        fontWeight = FontWeight.SemiBold,
                                    )
                                    statusLabel?.let {
                                        Text(
                                            it,
                                            style = MaterialTheme.typography.caption,
                                            color = MaterialTheme.colors.primary,
                                            modifier = Modifier.padding(top = 2.dp),
                                        )
                                    }
                                }
                                actionButtons()
                            }
                        }
                        if (checked) {
                            if (planGroups.isNotEmpty()) {
                                val curG = pRow?.groupId
                                val curLabel = curG?.let { gid ->
                                    planGroups.find { it.id == gid }?.name ?: "Gruppe $gid"
                                } ?: tr("diary.participantNoTrainingGroup", "Keine Gruppe")
                                Box(modifier = Modifier.fillMaxWidth().padding(top = 6.dp)) {
                                    OutlinedButton(
                                        onClick = { participantGroupMenuMemberId = member.id },
                                        enabled = canWriteDiary && !participantMutating && !diaryState.isLoading,
                                        modifier = Modifier.fillMaxWidth(),
                                    ) {
                                        Text("${tr("diary.participantTrainingGroup", "Trainingsgruppe")}: $curLabel")
                                    }
                                    DropdownMenu(
                                        expanded = participantGroupMenuMemberId == member.id,
                                        onDismissRequest = { participantGroupMenuMemberId = null },
                                    ) {
                                        DropdownMenuItem(
                                            onClick = {
                                                participantGroupMenuMemberId = null
                                                participantMutating = true
                                                dependencies.applicationScope.launch {
                                                    try {
                                                        dependencies.diaryManager.setTrainingParticipantGroup(entry.id, member.id, null)
                                                        participants = dependencies.diaryManager.listTrainingParticipants(entry.id)
                                                    } finally {
                                                        participantMutating = false
                                                    }
                                                }
                                            },
                                        ) { Text(tr("diary.participantClearGroup", "Keine Gruppe")) }
                                        planGroups.forEach { g ->
                                            DropdownMenuItem(
                                                onClick = {
                                                    participantGroupMenuMemberId = null
                                                    participantMutating = true
                                                    dependencies.applicationScope.launch {
                                                        try {
                                                            dependencies.diaryManager.setTrainingParticipantGroup(entry.id, member.id, g.id)
                                                            participants = dependencies.diaryManager.listTrainingParticipants(entry.id)
                                                        } finally {
                                                            participantMutating = false
                                                        }
                                                    }
                                                },
                                            ) { Text(g.name ?: "Gruppe ${g.id}") }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            if (canWriteMembers && canWriteDiary) {
                OutlinedButton(
                    onClick = { showQuickAddMember = true },
                    modifier = Modifier.fillMaxWidth().padding(top = 8.dp).heightIn(min = TouchMinHeight),
                ) { Text(tr("diary.quickAdd", "Testmitglied schnell hinzufuegen")) }
            }
            
                Box(modifier = Modifier.onGloballyPositioned { coords -> yAccidents = coords.positionInParent().y.toInt() }) {
                    SectionTitle(tr("diary.accidents", "Unfälle / Vorfälle"))
                }
                Text(
                    tr("diary.accidentsHint", "Meldungen zu Vorfällen am Trainingstag (wie in der Web-App)."),
                    style = MaterialTheme.typography.caption,
                    color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f),
                    modifier = Modifier.padding(bottom = 6.dp),
                )
            accidentSectionError?.let { ErrorText(it) }
            accidents.forEach { ev ->
                Card(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(vertical = 4.dp),
                    elevation = 1.dp,
                ) {
                    Column(modifier = Modifier.padding(12.dp)) {
                        Text(ev.memberLabel(), fontWeight = FontWeight.SemiBold)
                        Text(ev.accident, style = MaterialTheme.typography.body2)
                    }
                }
            }
            Box(modifier = Modifier.fillMaxWidth().padding(top = 4.dp)) {
                val amLabel = newAccidentMemberId?.let { mid ->
                    accidentSelectableMembers.find { it.id == mid }?.fullName()
                } ?: tr("diary.accidentPickMember", "Betroffenes Mitglied")
                OutlinedButton(
                    onClick = { accidentMemberMenu = true },
                    enabled = canWriteDiary && !accidentSubmitBusy && accidentSelectableMembers.isNotEmpty(),
                    modifier = Modifier.fillMaxWidth(),
                ) { Text(amLabel) }
                DropdownMenu(
                    expanded = accidentMemberMenu,
                    onDismissRequest = { accidentMemberMenu = false },
                ) {
                    accidentSelectableMembers.forEach { m ->
                        DropdownMenuItem(
                            onClick = {
                                newAccidentMemberId = m.id
                                accidentMemberMenu = false
                            },
                        ) { Text(m.fullName()) }
                    }
                }
            }
            if (accidentSelectableMembers.isEmpty()) {
                Text(
                    tr("diary.accidentNoMembers", "Keine verfügbaren Mitglieder für den Eintrag."),
                    style = MaterialTheme.typography.caption,
                    color = MaterialTheme.colors.error,
                    modifier = Modifier.padding(top = 6.dp),
                )
            }
            OutlinedTextField(
                value = newAccidentDescription,
                onValueChange = { newAccidentDescription = it },
                label = { Text(tr("diary.accidentDescription", "Schilderung")) },
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 8.dp),
                enabled = canWriteDiary && !accidentSubmitBusy,
                singleLine = false,
                maxLines = 5,
            )
            Button(
                onClick = {
                    val mid = newAccidentMemberId ?: return@Button
                    val desc = newAccidentDescription.trim()
                    if (desc.isEmpty()) return@Button
                    dependencies.applicationScope.launch {
                        accidentSubmitBusy = true
                        accidentSectionError = null
                        try {
                            dependencies.diaryManager.addAccident(clubId, entry.id, mid, desc)
                            newAccidentDescription = ""
                            accidents = dependencies.diaryManager.listAccidents(clubId, entry.id)
                        } catch (t: Throwable) {
                            accidentSectionError = t.message ?: "Eintrag konnte nicht gespeichert werden"
                        } finally {
                            accidentSubmitBusy = false
                        }
                    }
                },
                enabled = canWriteDiary && !accidentSubmitBusy && newAccidentMemberId != null && newAccidentDescription.isNotBlank(),
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 8.dp)
                    .heightIn(min = TouchMinHeight),
            ) { Text(tr("mobile.add", "Hinzufügen")) }

            if (showQuickAddMember) {
                DiaryQuickAddMemberDialog(
                    clubId = clubId,
                    diaryDateId = entry.id,
                    dependencies = dependencies,
                    onDismiss = { showQuickAddMember = false },
                    onParticipantsUpdated = { participants = it },
                )
            }

            memberNotesSheetMember?.let { sheetMember ->
                val notesScroll = rememberScrollState()
                StableAlertDialog(
                    onDismissRequest = {
                        memberNotesSheetMember = null
                        memberNotesError = null
                    },
                    title = { Text("${sheetMember.fullName()} – ${tr("diary.memberNotesTitle", "Notizen & Tags")}") },
                    text = {
                        Column(
                            modifier = Modifier
                                .heightIn(max = 480.dp)
                                .verticalScroll(notesScroll),
                            verticalArrangement = Arrangement.spacedBy(8.dp),
                        ) {
                            memberNotesError?.let { Text(it, color = MaterialTheme.colors.error) }
                            if (memberNotesBusy) {
                                Row(
                                    modifier = Modifier.fillMaxWidth(),
                                    horizontalArrangement = Arrangement.Center,
                                ) {
                                    CircularProgressIndicator(modifier = Modifier.size(28.dp))
                                }
                            }
                            Text(tr("diary.memberNotesSection", "Notizen"), style = MaterialTheme.typography.subtitle2)
                            memberNotesList.forEach { note ->
                                Row(
                                    verticalAlignment = Alignment.CenterVertically,
                                    modifier = Modifier.fillMaxWidth(),
                                ) {
                                    Text(note.content.orEmpty(), modifier = Modifier.weight(1f))
                                    TextButton(
                                        enabled = canWriteDiary && !memberNotesBusy,
                                        onClick = {
                                            dependencies.applicationScope.launch {
                                                memberNotesBusy = true
                                                try {
                                                    memberNotesList = dependencies.diaryManager.deleteMemberNote(
                                                        clubId,
                                                        entry.id,
                                                        sheetMember.id,
                                                        note.id,
                                                    )
                                                } catch (t: Throwable) {
                                                    memberNotesError = t.message
                                                } finally {
                                                    memberNotesBusy = false
                                                }
                                            }
                                        },
                                    ) { Text(tr("common.delete", "Löschen")) }
                                }
                            }
                            Row(
                                horizontalArrangement = Arrangement.spacedBy(8.dp),
                                modifier = Modifier.fillMaxWidth(),
                            ) {
                                OutlinedTextField(
                                    value = newMemberContextNote,
                                    onValueChange = { newMemberContextNote = it },
                                    label = { Text(tr("mobile.newNote", "Neue Notiz")) },
                                    modifier = Modifier.weight(1f),
                                    enabled = canWriteDiary && !memberNotesBusy,
                                    singleLine = false,
                                    maxLines = 3,
                                )
                                Button(
                                    onClick = {
                                        val text = newMemberContextNote.trim()
                                        if (text.isEmpty()) return@Button
                                        dependencies.applicationScope.launch {
                                            memberNotesBusy = true
                                            try {
                                                memberNotesList = dependencies.diaryManager.addMemberNote(
                                                    clubId,
                                                    entry.id,
                                                    sheetMember.id,
                                                    text,
                                                )
                                                newMemberContextNote = ""
                                            } catch (t: Throwable) {
                                                memberNotesError = t.message
                                            } finally {
                                                memberNotesBusy = false
                                            }
                                        }
                                    },
                                    enabled = canWriteDiary && !memberNotesBusy && newMemberContextNote.isNotBlank(),
                                    modifier = Modifier.heightIn(min = TouchMinHeight),
                                ) { Text(tr("mobile.add", "Hinzufügen")) }
                            }
                            Divider(modifier = Modifier.padding(vertical = 8.dp))
                            Text(tr("diary.memberTagsSection", "Tags für diesen Tag"), style = MaterialTheme.typography.subtitle2)
                            memberTagsList.forEach { link ->
                                Row(
                                    verticalAlignment = Alignment.CenterVertically,
                                    modifier = Modifier.fillMaxWidth(),
                                ) {
                                    Text(link.tagDisplayName(), modifier = Modifier.weight(1f))
                                    TextButton(
                                        enabled = canWriteDiary && !memberNotesBusy,
                                        onClick = {
                                            val tid = link.tagDefinitionId()
                                            if (tid <= 0) return@TextButton
                                            dependencies.applicationScope.launch {
                                                memberNotesBusy = true
                                                try {
                                                    memberTagsList = dependencies.diaryManager.removeMemberTag(
                                                        clubId,
                                                        entry.id,
                                                        sheetMember.id,
                                                        tid,
                                                    )
                                                } catch (t: Throwable) {
                                                    memberNotesError = t.message
                                                } finally {
                                                    memberNotesBusy = false
                                                }
                                            }
                                        },
                                    ) { Text(tr("mobile.remove", "Entfernen")) }
                                }
                            }
                            Box(modifier = Modifier.fillMaxWidth()) {
                                OutlinedButton(
                                    onClick = { memberTagPickMenu = true },
                                    enabled = canWriteDiary && !memberNotesBusy,
                                    modifier = Modifier.fillMaxWidth(),
                                ) { Text(tr("diary.addExistingMemberTag", "Bestehenden Tag hinzufügen")) }
                                DropdownMenu(
                                    expanded = memberTagPickMenu,
                                    onDismissRequest = { memberTagPickMenu = false },
                                ) {
                                    val linkedIds = memberTagsList.map { it.tagDefinitionId() }.toSet()
                                    val choices = memberNotesAvailableTags.filter { it.id !in linkedIds }
                                    if (choices.isEmpty()) {
                                        DropdownMenuItem(onClick = { memberTagPickMenu = false }) {
                                            Text(tr("diary.noTagsAvailable", "Keine weiteren Tags"))
                                        }
                                    } else {
                                        choices.forEach { tag ->
                                            DropdownMenuItem(
                                                onClick = {
                                                    memberTagPickMenu = false
                                                    dependencies.applicationScope.launch {
                                                        memberNotesBusy = true
                                                        try {
                                                            memberTagsList = dependencies.diaryManager.addMemberTag(
                                                                clubId,
                                                                entry.id,
                                                                sheetMember.id,
                                                                tag.id,
                                                            )
                                                        } catch (t: Throwable) {
                                                            memberNotesError = t.message
                                                        } finally {
                                                            memberNotesBusy = false
                                                        }
                                                    }
                                                },
                                            ) { Text(tag.name) }
                                        }
                                    }
                                }
                            }
                            Row(
                                horizontalArrangement = Arrangement.spacedBy(8.dp),
                                modifier = Modifier.fillMaxWidth(),
                            ) {
                                OutlinedTextField(
                                    value = newMemberContextTagName,
                                    onValueChange = { newMemberContextTagName = it },
                                    label = { Text(tr("diary.newMemberTagName", "Neuer Tag-Name")) },
                                    modifier = Modifier.weight(1f),
                                    enabled = canWriteDiary && !memberNotesBusy,
                                    singleLine = true,
                                )
                                Button(
                                    onClick = {
                                        val name = newMemberContextTagName.trim()
                                        if (name.isEmpty()) return@Button
                                        dependencies.applicationScope.launch {
                                            memberNotesBusy = true
                                            try {
                                                memberTagsList = dependencies.diaryManager.createMemberTagAndLink(
                                                    clubId,
                                                    entry.id,
                                                    sheetMember.id,
                                                    name,
                                                )
                                                newMemberContextTagName = ""
                                                memberNotesAvailableTags = dependencies.diaryManager.listAllDiaryTags()
                                            } catch (t: Throwable) {
                                                memberNotesError = t.message
                                            } finally {
                                                memberNotesBusy = false
                                            }
                                        }
                                    },
                                    enabled = canWriteDiary && !memberNotesBusy && newMemberContextTagName.isNotBlank(),
                                    modifier = Modifier.heightIn(min = TouchMinHeight),
                                ) { Text(tr("mobile.add", "Hinzufügen")) }
                            }
                        }
                    },
                    confirmButton = {
                        TextButton(
                            onClick = {
                                memberNotesSheetMember = null
                                memberNotesError = null
                            },
                        ) { Text(tr("common.close", "Schließen")) }
                    },
                )
            }

            }

            if (activeDiaryTab == DiaryDetailTab.Activities) {
            OutlinedButton(
                onClick = { gallerySectionExpanded = !gallerySectionExpanded },
                modifier = Modifier
                    .fillMaxWidth()
                    .onGloballyPositioned { coords -> yGallery = coords.positionInParent().y.toInt() }
                    .padding(top = 8.dp)
                    .heightIn(min = TouchMinHeight),
            ) {
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.SpaceBetween,
                    verticalAlignment = Alignment.CenterVertically,
                ) {
                    Column(modifier = Modifier.weight(1f)) {
                        Text(tr("diary.gallerySection", "Gruppenfotos & Galerie"), style = MaterialTheme.typography.subtitle1)
                        if (!gallerySectionExpanded) {
                            Text(
                                tr("diary.galleryCollapsedHint", "Vereins-Galerie aufklappen."),
                                style = MaterialTheme.typography.caption,
                                color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f),
                            )
                        }
                    }
                    Icon(
                        imageVector = if (gallerySectionExpanded) Icons.Filled.ExpandLess else Icons.Filled.ExpandMore,
                        contentDescription = null,
                    )
                }
            }
            if (gallerySectionExpanded && canReadMembers) {
                if (canWriteMembers) {
                    Row(
                        horizontalArrangement = Arrangement.spacedBy(8.dp),
                        modifier = Modifier.fillMaxWidth().padding(top = 8.dp),
                    ) {
                        OutlinedTextField(
                            value = newGroupPhotoTitle,
                            onValueChange = { newGroupPhotoTitle = it },
                            label = { Text(tr("diary.groupPhotoTitle", "Titel")) },
                            modifier = Modifier.weight(1f),
                            enabled = !groupPhotoBusy,
                            singleLine = true,
                        )
                        OutlinedButton(
                            onClick = {
                                pickGroupPhoto.launch(
                                    PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly),
                                )
                            },
                            enabled = !groupPhotoBusy,
                            modifier = Modifier.heightIn(min = TouchMinHeight),
                        ) { Text(tr("diary.uploadGroupPhoto", "Foto wählen")) }
                    }
                    OutlinedTextField(
                        value = newGroupPhotoDescription,
                        onValueChange = { newGroupPhotoDescription = it },
                        label = { Text(tr("diary.groupPhotoDescription", "Beschreibung (optional)")) },
                        modifier = Modifier.fillMaxWidth(),
                        enabled = !groupPhotoBusy,
                        singleLine = false,
                        maxLines = 3,
                    )
                }
                if (groupPhotoBusy) LoadingInline()
                if (groupPhotos.isEmpty()) {
                    Text(tr("diary.noGroupPhotos", "Keine Gruppenfotos."), modifier = Modifier.padding(vertical = 8.dp))
                } else {
                    val auth = dependencies.diaryAuthHeaders()
                    groupPhotos.forEach { ph ->
                        val url = ph.imageUrl?.let { dependencies.apiConfig.toAbsoluteUrl(it) }
                        Card(
                            modifier = Modifier
                                .fillMaxWidth()
                                .padding(vertical = 6.dp),
                            elevation = 1.dp,
                        ) {
                            Column(modifier = Modifier.padding(8.dp)) {
                                Text(ph.title.orEmpty(), fontWeight = FontWeight.SemiBold)
                                if (!ph.description.isNullOrBlank()) {
                                    Text(ph.description.orEmpty(), style = MaterialTheme.typography.caption)
                                }
                                if (url != null) {
                                    AuthenticatedAsyncImage(
                                        imageUrl = url,
                                        authHeaders = auth,
                                        modifier = Modifier
                                            .fillMaxWidth()
                                            .height(180.dp)
                                            .padding(top = 6.dp),
                                        contentDescription = ph.title,
                                    )
                                }
                                if (canWriteMembers) {
                                    TextButton(
                                        enabled = !groupPhotoBusy,
                                        onClick = {
                                            dependencies.applicationScope.launch {
                                                groupPhotoBusy = true
                                                try {
                                                    dependencies.diaryManager.deleteMemberGroupPhoto(clubId, ph.id)
                                                    groupPhotos = dependencies.diaryManager.listMemberGroupPhotos(clubId)
                                                } finally {
                                                    groupPhotoBusy = false
                                                }
                                            }
                                        },
                                    ) { Text(tr("common.delete", "Löschen")) }
                                }
                            }
                        }
                    }
                }
            }

            Box(modifier = Modifier.onGloballyPositioned { coords -> yDayActivities = coords.positionInParent().y.toInt() }) {
                SectionTitle(tr("diary.dayActivitiesSection", "Weitere Tages-Aktivitäten"))
            }
            Text(
                tr("diary.dayActivitiesHint", "Kurze Einträge zum Trainingstag (API /activities – unabhängig vom Trainingsplan)."),
                style = MaterialTheme.typography.body2,
                color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f),
                modifier = Modifier.padding(bottom = 8.dp),
            )
            freeformActivities.forEach { fa ->
                Card(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(vertical = 4.dp),
                    elevation = 1.dp,
                ) {
                    Text(fa.description, modifier = Modifier.padding(12.dp), style = MaterialTheme.typography.body2)
                }
            }
            Row(
                horizontalArrangement = Arrangement.spacedBy(8.dp),
                modifier = Modifier.fillMaxWidth(),
            ) {
                OutlinedTextField(
                    value = newFreeformDescription,
                    onValueChange = { newFreeformDescription = it },
                    label = { Text(tr("diary.newDayActivity", "Neuer Eintrag")) },
                    modifier = Modifier.weight(1f),
                    enabled = canWriteDiary && !freeformBusy && !planLoading,
                    singleLine = false,
                    maxLines = 3,
                )
                Button(
                    onClick = {
                        val text = newFreeformDescription.trim()
                        if (text.isEmpty()) return@Button
                        dependencies.applicationScope.launch {
                            freeformBusy = true
                            try {
                                dependencies.diaryManager.addFreeformActivity(entry.id, text)
                                newFreeformDescription = ""
                                freeformActivities = dependencies.diaryManager.listFreeformActivities(entry.id)
                            } finally {
                                freeformBusy = false
                            }
                        }
                    },
                    enabled = canWriteDiary && !freeformBusy && newFreeformDescription.isNotBlank(),
                    modifier = Modifier.heightIn(min = TouchMinHeight),
                ) { Text(tr("mobile.add", "Hinzufügen")) }
            }

            }

            if (activeDiaryTab == DiaryDetailTab.Plan) {
            Box(modifier = Modifier.onGloballyPositioned { coords -> yTrainingPlan = coords.positionInParent().y.toInt() }) {
                SectionTitle(tr("diary.trainingPlan", "Trainingsplan"))
            }
            ErrorText(planActionError)
            if (canReadDiary) {
                DiaryPlanToolbar(
                    clubId = clubId,
                    entry = entry,
                    dependencies = dependencies,
                    clubName = clubState.clubs.firstOrNull { it.id == clubId }?.name ?: "Verein",
                    planItems = planItems,
                    planGroups = planGroups,
                    activeMembers = activeMembers,
                    participants = participants,
                    freeformActivities = freeformActivities,
                    selectedGroupId = planGroupFilter,
                    canWriteDiary = canWriteDiary,
                    planLoading = planLoading,
                    planMutating = planMutating,
                    onSelectGroup = { planGroupFilter = it },
                    onRefresh = refreshPlanAfterMutation,
                    onToggleAddActivity = { showAddPlanActivity = !showAddPlanActivity },
                    onCreateDrawing = { creatingDrawingActivity = true; drawingPlanItem = null },
                    onToggleGroupActivity = { showAddPlanGroupActivity = !showAddPlanGroupActivity },
                    onToggleTrainingGroup = { showAddTrainingGroup = !showAddTrainingGroup },
                    onError = { planActionError = it },
                )
            }
            if (planGroups.isNotEmpty()) {
                DiaryTrainingGroupsEditor(
                    groups = planGroups,
                    editingGroup = editingTrainingGroup,
                    name = editTrainingGroupName,
                    lead = editTrainingGroupLead,
                    canWriteDiary = canWriteDiary,
                    planMutating = planMutating,
                    clubId = clubId,
                    diaryDateId = entry.id,
                    dependencies = dependencies,
                    onEdit = { group ->
                        editingTrainingGroup = group
                        editTrainingGroupName = group.name.orEmpty()
                        editTrainingGroupLead = group.lead.orEmpty()
                    },
                    onNameChange = { editTrainingGroupName = it },
                    onLeadChange = { editTrainingGroupLead = it },
                    onCancel = { editingTrainingGroup = null },
                    onDelete = { deleteGroupCandidate = it },
                    onBusy = { planMutating = it },
                    onGroupsUpdated = {
                        planGroups = it
                        editingTrainingGroup = null
                    },
                    onError = { planActionError = it },
                )
            }
            if (showAddPlanActivity) {
                var newActGroupMenu by remember { mutableStateOf(false) }
                Text(
                    tr("diary.addPlanActivityHint", "Name wie in der Web-App (wird ggf. als neue Übung angelegt). Optional: Dauer und Zuordnung zu einer Trainingsgruppe."),
                    style = MaterialTheme.typography.caption,
                    color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f),
                    modifier = Modifier.padding(top = 12.dp, bottom = 8.dp),
                )
                Row(verticalAlignment = Alignment.CenterVertically) {
                    Checkbox(
                        checked = newPlanIsTimeblock,
                        onCheckedChange = { newPlanIsTimeblock = it },
                        enabled = canWriteDiary && !planMutating,
                    )
                    Text(tr("diary.planTimeblock", "Zeitblock"), modifier = Modifier.clickable(enabled = canWriteDiary && !planMutating) { newPlanIsTimeblock = !newPlanIsTimeblock })
                }
                if (!newPlanIsTimeblock) {
                    Text(
                        tr(
                            "diary.predefinedSearchHint",
                            "Vordefinierte Übung: nach Kürzel suchen (mind. 2 Zeichen) oder Standardliste öffnen.",
                        ),
                        style = MaterialTheme.typography.caption,
                        color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f),
                        modifier = Modifier.padding(top = 8.dp, bottom = 4.dp),
                    )
                    OutlinedTextField(
                        value = newPlanPredefQuery,
                        onValueChange = { newPlanPredefQuery = it },
                        label = { Text(tr("diary.predefinedSearchLabel", "Kürzel-Suche")) },
                        modifier = Modifier.fillMaxWidth(),
                        enabled = canWriteDiary && !planMutating,
                        singleLine = true,
                    )
                    if (newPlanPredefLoading) {
                        Text(tr("mobile.searching", "Suche …"), style = MaterialTheme.typography.caption)
                    }
                    newPlanPredefResults.take(12).forEach { dto ->
                        TextButton(
                            enabled = canWriteDiary && !planMutating,
                            onClick = {
                                newPlanPickedPredefined = dto
                                newPlanActivityName = dto.predefinedDtoDisplayLabel()
                                dto.duration?.let { newPlanDuration = it.toString() }
                                newPlanDurationText = dto.durationText.orEmpty()
                            },
                            modifier = Modifier.fillMaxWidth(),
                        ) {
                            val code = dto.code?.takeIf { it.isNotBlank() }?.let { " ($it)" }.orEmpty()
                            Text(dto.predefinedDtoDisplayLabel() + code)
                        }
                    }
                    Row(
                        horizontalArrangement = Arrangement.spacedBy(8.dp),
                        modifier = Modifier.fillMaxWidth(),
                    ) {
                        TextButton(
                            enabled = canWriteDiary && !planMutating,
                            onClick = { newPlanStandardPredefExpanded = !newPlanStandardPredefExpanded },
                        ) {
                            Text(
                                if (newPlanStandardPredefExpanded) {
                                    tr("diary.predefinedHideStandard", "Standardliste ausblenden")
                                } else {
                                    tr("diary.predefinedShowStandard", "Standard-Übungen")
                                },
                            )
                        }
                        TextButton(
                            enabled = canWriteDiary && !planMutating && newPlanPickedPredefined != null,
                            onClick = { newPlanPickedPredefined = null },
                        ) { Text(tr("diary.predefinedClearPick", "Auswahl zurücksetzen")) }
                    }
                    if (newPlanStandardPredefExpanded) {
                        newPlanStandardPredefList.take(24).forEach { dto ->
                            TextButton(
                                enabled = canWriteDiary && !planMutating,
                                onClick = {
                                    newPlanPickedPredefined = dto
                                    newPlanActivityName = dto.predefinedDtoDisplayLabel()
                                    dto.duration?.let { newPlanDuration = it.toString() }
                                    newPlanDurationText = dto.durationText.orEmpty()
                                },
                                modifier = Modifier.fillMaxWidth(),
                            ) {
                                val code = dto.code?.takeIf { it.isNotBlank() }?.let { " ($it)" }.orEmpty()
                                Text(dto.predefinedDtoDisplayLabel() + code)
                            }
                        }
                    }
                    newPlanPickedPredefined?.let { picked ->
                        Text(
                            tr("diary.predefinedPicked", "Ausgewählt") + ": ${picked.predefinedDtoDisplayLabel()}",
                            style = MaterialTheme.typography.caption,
                            color = MaterialTheme.colors.primary,
                        )
                    }
                }
                OutlinedTextField(
                    value = newPlanActivityName,
                    onValueChange = {
                        newPlanActivityName = it
                        newPlanPickedPredefined = null
                    },
                    label = { Text(tr("diary.planActivityName", "Aktivität / Bezeichnung")) },
                    modifier = Modifier.fillMaxWidth(),
                    enabled = canWriteDiary && !planMutating && !newPlanIsTimeblock,
                    singleLine = true,
                )
                Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) {
                    OutlinedTextField(
                        value = newPlanDuration,
                        onValueChange = { newPlanDuration = it },
                        label = { Text(tr("diary.planDurationMin", "Minuten")) },
                        modifier = Modifier.weight(1f),
                        enabled = canWriteDiary && !planMutating,
                        singleLine = true,
                    )
                    OutlinedTextField(
                        value = newPlanDurationText,
                        onValueChange = { newPlanDurationText = it },
                        label = { Text(tr("diary.planDurationText", "Dauer (Freitext)")) },
                        modifier = Modifier.weight(1f),
                        enabled = canWriteDiary && !planMutating,
                        singleLine = true,
                    )
                }
                if (planGroups.isNotEmpty()) {
                    Box(modifier = Modifier.fillMaxWidth().padding(top = 8.dp)) {
                        val assignLabel = newPlanTargetGroupId?.let { id ->
                            planGroups.find { it.id == id }?.name ?: "Gruppe $id"
                        } ?: tr("diary.planGroupGlobal", "Alle / keine Gruppe")
                        OutlinedButton(
                            onClick = { newActGroupMenu = true },
                            enabled = canWriteDiary && !planMutating,
                            modifier = Modifier.fillMaxWidth(),
                        ) { Text("${tr("diary.planAssignGroup", "Zuordnung")}: $assignLabel") }
                        DropdownMenu(expanded = newActGroupMenu, onDismissRequest = { newActGroupMenu = false }) {
                            DropdownMenuItem(onClick = {
                                newPlanTargetGroupId = null
                                newActGroupMenu = false
                            }) { Text(tr("diary.planGroupGlobal", "Alle / keine Gruppe")) }
                            planGroups.forEach { g ->
                                DropdownMenuItem(onClick = {
                                    newPlanTargetGroupId = g.id
                                    newActGroupMenu = false
                                }) { Text(g.name ?: "Gruppe ${g.id}") }
                            }
                        }
                    }
                }
                Button(
                    onClick = {
                        if (!newPlanIsTimeblock && newPlanActivityName.isBlank()) return@Button
                        dependencies.applicationScope.launch {
                            planMutating = true
                            planActionError = null
                            try {
                                dependencies.diaryManager.createPlanActivity(
                                    clubId,
                                    CreateDiaryPlanActivityRequest(
                                        diaryDateId = entry.id,
                                        activity = if (newPlanIsTimeblock) "" else newPlanActivityName.trim(),
                                        predefinedActivityId = newPlanPickedPredefined?.id,
                                        duration = newPlanDuration.toIntOrNull(),
                                        durationText = newPlanDurationText.trim().takeIf { it.isNotEmpty() },
                                        isTimeblock = newPlanIsTimeblock,
                                        groupId = newPlanTargetGroupId,
                                    ),
                                )
                                newPlanActivityName = ""
                                newPlanDuration = ""
                                newPlanDurationText = ""
                                newPlanIsTimeblock = false
                                newPlanTargetGroupId = null
                                newPlanPredefQuery = ""
                                newPlanPredefResults = emptyList()
                                newPlanPickedPredefined = null
                                showAddPlanActivity = false
                                planItems = dependencies.diaryManager.fetchDateActivities(clubId, entry.id)
                                planGroups = dependencies.diaryManager.listTrainingGroups(clubId, entry.id)
                            } catch (t: Throwable) {
                                planActionError = t.message
                            } finally {
                                planMutating = false
                            }
                        }
                    },
                    enabled = canWriteDiary && !planMutating,
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(top = 8.dp)
                        .heightIn(min = TouchMinHeight),
                ) { Text(tr("mobile.add", "Hinzufügen")) }
            }
            if (showAddPlanGroupActivity) {
                var scopedGroupMenu by remember { mutableStateOf(false) }
                Text(
                    tr("diary.addGroupActivityHint", "Legt eine Aktivität in einer bestehenden Trainingsgruppe an (wie Web „Gruppen-Aktivität“)."),
                    style = MaterialTheme.typography.caption,
                    color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f),
                    modifier = Modifier.padding(top = 12.dp, bottom = 8.dp),
                )
                if (planGroups.isEmpty()) {
                    Text(tr("diary.needTrainingGroupFirst", "Lege zuerst eine Trainingsgruppe an."), modifier = Modifier.padding(vertical = 8.dp))
                } else {
                    Box(modifier = Modifier.fillMaxWidth()) {
                        val gl = newPlanGroupActivityGroupId?.let { id ->
                            planGroups.find { it.id == id }?.name ?: "Gruppe $id"
                        } ?: tr("diary.pickTrainingGroup", "Trainingsgruppe wählen")
                        OutlinedButton(
                            onClick = { scopedGroupMenu = true },
                            enabled = canWriteDiary && !planMutating,
                            modifier = Modifier.fillMaxWidth(),
                        ) { Text(gl) }
                        DropdownMenu(expanded = scopedGroupMenu, onDismissRequest = { scopedGroupMenu = false }) {
                            planGroups.forEach { g ->
                                DropdownMenuItem(onClick = {
                                    newPlanGroupActivityGroupId = g.id
                                    scopedGroupMenu = false
                                }) { Text(g.name ?: "Gruppe ${g.id}") }
                            }
                        }
                    }
                    Text(
                        tr(
                            "diary.predefinedSearchHint",
                            "Vordefinierte Übung: nach Kürzel suchen (mind. 2 Zeichen) oder Standardliste öffnen.",
                        ),
                        style = MaterialTheme.typography.caption,
                        color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f),
                        modifier = Modifier.padding(top = 8.dp, bottom = 4.dp),
                    )
                    OutlinedTextField(
                        value = scopedPredefQuery,
                        onValueChange = { scopedPredefQuery = it },
                        label = { Text(tr("diary.predefinedSearchLabel", "Kürzel-Suche")) },
                        modifier = Modifier.fillMaxWidth(),
                        enabled = canWriteDiary && !planMutating,
                        singleLine = true,
                    )
                    if (scopedPredefLoading) {
                        Text(tr("mobile.searching", "Suche …"), style = MaterialTheme.typography.caption)
                    }
                    scopedPredefResults.take(12).forEach { dto ->
                        TextButton(
                            enabled = canWriteDiary && !planMutating,
                            onClick = {
                                scopedPickedPredefined = dto
                                newScopedActivityName = dto.predefinedDtoDisplayLabel()
                                dto.duration?.let { newScopedDuration = it.toString() }
                                newScopedDurationText = dto.durationText.orEmpty()
                            },
                            modifier = Modifier.fillMaxWidth(),
                        ) {
                            val code = dto.code?.takeIf { it.isNotBlank() }?.let { " ($it)" }.orEmpty()
                            Text(dto.predefinedDtoDisplayLabel() + code)
                        }
                    }
                    Row(
                        horizontalArrangement = Arrangement.spacedBy(8.dp),
                        modifier = Modifier.fillMaxWidth(),
                    ) {
                        TextButton(
                            enabled = canWriteDiary && !planMutating,
                            onClick = { scopedStandardPredefExpanded = !scopedStandardPredefExpanded },
                        ) {
                            Text(
                                if (scopedStandardPredefExpanded) {
                                    tr("diary.predefinedHideStandard", "Standardliste ausblenden")
                                } else {
                                    tr("diary.predefinedShowStandard", "Standard-Übungen")
                                },
                            )
                        }
                        TextButton(
                            enabled = canWriteDiary && !planMutating && scopedPickedPredefined != null,
                            onClick = { scopedPickedPredefined = null },
                        ) { Text(tr("diary.predefinedClearPick", "Auswahl zurücksetzen")) }
                    }
                    if (scopedStandardPredefExpanded) {
                        scopedStandardPredefList.take(24).forEach { dto ->
                            TextButton(
                                enabled = canWriteDiary && !planMutating,
                                onClick = {
                                    scopedPickedPredefined = dto
                                    newScopedActivityName = dto.predefinedDtoDisplayLabel()
                                    dto.duration?.let { newScopedDuration = it.toString() }
                                    newScopedDurationText = dto.durationText.orEmpty()
                                },
                                modifier = Modifier.fillMaxWidth(),
                            ) {
                                val code = dto.code?.takeIf { it.isNotBlank() }?.let { " ($it)" }.orEmpty()
                                Text(dto.predefinedDtoDisplayLabel() + code)
                            }
                        }
                    }
                    scopedPickedPredefined?.let { picked ->
                        Text(
                            tr("diary.predefinedPicked", "Ausgewählt") + ": ${picked.predefinedDtoDisplayLabel()}",
                            style = MaterialTheme.typography.caption,
                            color = MaterialTheme.colors.primary,
                        )
                    }
                    OutlinedTextField(
                        value = newScopedActivityName,
                        onValueChange = {
                            newScopedActivityName = it
                            scopedPickedPredefined = null
                        },
                        label = { Text(tr("diary.planActivityName", "Aktivität / Bezeichnung")) },
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(top = 8.dp),
                        enabled = canWriteDiary && !planMutating,
                        singleLine = true,
                    )
                    Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) {
                        OutlinedTextField(
                            value = newScopedDuration,
                            onValueChange = { newScopedDuration = it },
                            label = { Text(tr("diary.planDurationMin", "Minuten")) },
                            modifier = Modifier.weight(1f),
                            enabled = canWriteDiary && !planMutating,
                            singleLine = true,
                        )
                        OutlinedTextField(
                            value = newScopedDurationText,
                            onValueChange = { newScopedDurationText = it },
                            label = { Text(tr("diary.planDurationText", "Dauer (Freitext)")) },
                            modifier = Modifier.weight(1f),
                            enabled = canWriteDiary && !planMutating,
                            singleLine = true,
                        )
                    }
                    Button(
                        onClick = {
                            val gid = newPlanGroupActivityGroupId
                            if (gid == null || newScopedActivityName.isBlank()) return@Button
                            dependencies.applicationScope.launch {
                                planMutating = true
                                planActionError = null
                                try {
                                    dependencies.diaryManager.addPlanGroupActivity(
                                        AddDiaryPlanGroupActivityRequest(
                                            clubId = clubId,
                                            diaryDateId = entry.id,
                                            groupId = gid,
                                            activity = newScopedActivityName.trim(),
                                            predefinedActivityId = scopedPickedPredefined?.id,
                                            timeblockId = null,
                                            duration = newScopedDuration.toIntOrNull(),
                                            durationText = newScopedDurationText.trim().takeIf { it.isNotEmpty() },
                                        ),
                                    )
                                    newScopedActivityName = ""
                                    newScopedDuration = ""
                                    newScopedDurationText = ""
                                    scopedPredefQuery = ""
                                    scopedPredefResults = emptyList()
                                    scopedPickedPredefined = null
                                    newPlanGroupActivityGroupId = null
                                    showAddPlanGroupActivity = false
                                    planItems = dependencies.diaryManager.fetchDateActivities(clubId, entry.id)
                                    val updatedGroups = dependencies.diaryManager.listTrainingGroups(clubId, entry.id)
                                    planGroups = updatedGroups
                                } catch (t: Throwable) {
                                    planActionError = t.message
                                } finally {
                                    planMutating = false
                                }
                            }
                        },
                        enabled = canWriteDiary && !planMutating && planGroups.isNotEmpty(),
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(top = 8.dp)
                            .heightIn(min = TouchMinHeight),
                    ) { Text(tr("mobile.add", "Hinzufügen")) }
                }
            }
            if (showAddTrainingGroup) {
                OutlinedTextField(
                    value = newTrainingGroupCount,
                    onValueChange = { value -> newTrainingGroupCount = value.filter(Char::isDigit).take(2) },
                    label = { Text(tr("diary.numberOfGroups", "Anzahl Gruppen")) },
                    modifier = Modifier.fillMaxWidth().padding(top = 8.dp),
                    enabled = canWriteDiary && !planMutating,
                    singleLine = true,
                )
                OutlinedTextField(
                    value = newTrainingGroupName,
                    onValueChange = { newTrainingGroupName = it },
                    label = { Text(tr("diary.trainingGroupName", "Name der Gruppe")) },
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(top = 8.dp),
                    enabled = canWriteDiary && !planMutating,
                    singleLine = true,
                )
                OutlinedTextField(
                    value = newTrainingGroupLead,
                    onValueChange = { newTrainingGroupLead = it },
                    label = { Text(tr("diary.trainingGroupLead", "Übungsleitung (optional)")) },
                    modifier = Modifier.fillMaxWidth(),
                    enabled = canWriteDiary && !planMutating,
                    singleLine = true,
                )
                Button(
                    onClick = {
                        val n = newTrainingGroupName.trim()
                        val count = newTrainingGroupCount.toIntOrNull()?.coerceIn(1, 10) ?: 1
                        if (n.isEmpty()) return@Button
                        dependencies.applicationScope.launch {
                            planMutating = true
                            planActionError = null
                            try {
                                repeat(count) { offset ->
                                    val groupName = if (count == 1) n else "$n ${offset + 1}"
                                    dependencies.diaryManager.createTrainingGroup(
                                        clubId,
                                        entry.id,
                                        groupName,
                                        newTrainingGroupLead.trim().takeIf { it.isNotEmpty() },
                                    )
                                }
                                newTrainingGroupName = ""
                                newTrainingGroupLead = ""
                                newTrainingGroupCount = "1"
                                showAddTrainingGroup = false
                                planGroups = dependencies.diaryManager.listTrainingGroups(clubId, entry.id)
                            } catch (t: Throwable) {
                                planActionError = t.message
                            } finally {
                                planMutating = false
                            }
                        }
                    },
                    enabled = canWriteDiary && !planMutating,
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(top = 8.dp)
                        .heightIn(min = TouchMinHeight),
                ) { Text(tr("mobile.add", "Anlegen")) }
            }
            if (!planLoading && planError == null && planItems.isEmpty()) {
                EmptyText(tr("diary.planEmpty", "Keine geplanten Aktivitäten"))
            }
            val sortedPlan = remember(planItems, planGroupFilter) {
                planItems
                    .filter { item -> planGroupFilter == null || item.groupId == null || item.groupId == planGroupFilter }
                    .sortedWith(::diaryPlanItemComparator)
            }
            val planStartTimes = remember(sortedPlan, entry.trainingStart) {
                calculatePlanStartLabels(sortedPlan, entry.trainingStart)
            }
            LaunchedEffect(entry.id, entry.trainingStart, entry.trainingEnd, sortedPlan, planStartTimes) {
                val playedMarks = mutableSetOf<String>()
                while (true) {
                    val currentHHMM = currentMinuteLabel()
                    val trainingStart = entry.trainingStart?.take(5)
                    val trainingEnd = entry.trainingEnd?.take(5)

                    listOfNotNull(trainingStart, trainingEnd).forEach { bellTime ->
                        val key = "bell:$bellTime"
                        if (currentHHMM == bellTime && playedMarks.add(key)) {
                            playRawSound(androidContext, R.raw.training_bell)
                        }
                    }

                    if (trainingStart != null) {
                        sortedPlan
                            .mapNotNull { item -> planStartTimes[item.id]?.take(5) }
                            .filter { it != trainingStart }
                            .distinct()
                            .forEach { startTime ->
                                val key = "item:$startTime"
                                if (currentHHMM == startTime && playedMarks.add(key)) {
                                    playRawSound(androidContext, R.raw.plan_item_start)
                                }
                            }
                    }

                    delay(1_000)
                }
            }
            if (expandedPlanActionsItemId != null && sortedPlan.none { it.id == expandedPlanActionsItemId }) {
                expandedPlanActionsItemId = null
            }
            val planTableScroll = rememberScrollState()
            val planTableWidth = max(LocalConfiguration.current.screenWidthDp, 380)
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .horizontalScroll(planTableScroll)
                    .clickable { expandedPlanActionsItemId = null },
            ) {
                Column(modifier = Modifier.width(planTableWidth.dp)) {
                    Row(
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(top = 8.dp, bottom = 2.dp),
                        verticalAlignment = Alignment.CenterVertically,
                    ) {
                        Text(
                            "STARTZEIT",
                            style = MaterialTheme.typography.caption,
                            fontWeight = FontWeight.Bold,
                            modifier = Modifier.width(DiaryPlanColStart),
                        )
                        Text(
                            "AKTIVITÄT / ZEITBLOCK",
                            style = MaterialTheme.typography.caption,
                            fontWeight = FontWeight.Bold,
                            modifier = Modifier.weight(1f),
                        )
                        Text(
                            "GRUPPE",
                            style = MaterialTheme.typography.caption,
                            fontWeight = FontWeight.Bold,
                            modifier = Modifier.width(DiaryPlanColGroup),
                        )
                        Text(
                            "DAUER",
                            style = MaterialTheme.typography.caption,
                            fontWeight = FontWeight.Bold,
                            modifier = Modifier.width(DiaryPlanColDuration),
                        )
                        Spacer(modifier = Modifier.width(26.dp))
                    }
                    Divider()
                    val showGroupedPlanTable = planGroupFilter == null && planGroups.isNotEmpty()
                    if (showGroupedPlanTable) {
                        DiaryGroupedPlanTable(
                            sortedPlan = sortedPlan,
                            planGroups = planGroups,
                            planStartTimes = planStartTimes,
                        )
                    }
                    if (!showGroupedPlanTable) {
                        sortedPlan.forEach { item ->
                            val cfg = dependencies.apiConfig
                            val mainImg = item.mainActivityImagePath()?.let { cfg.toAbsoluteUrl(it) }
                            val nestedImageUrls = item.groupActivities.mapNotNull { ga ->
                                val id = ga.id ?: return@mapNotNull null
                                ga.nestedActivityImagePath()?.let { id to cfg.toAbsoluteUrl(it) }
                            }.toMap()
                            DiaryPlanEditableCard(
                                item = item,
                                allPlanItems = planItems,
                                scheduledStart = planStartTimes[item.id],
                                planGroups = planGroups,
                                planMutating = planMutating,
                                canWriteDiary = canWriteDiary,
                                mainImageUrl = mainImg,
                                nestedImageUrls = nestedImageUrls,
                                canReadImages = canReadDiary,
                                visibleGroupId = planGroupFilter,
                                isExpanded = expandedPlanActionsItemId == item.id,
                                onToggleExpand = {
                                    expandedPlanActionsItemId = if (expandedPlanActionsItemId == item.id) null else item.id
                                },
                                onAssignParticipants = {
                                    assigningParticipantsItem = item
                                    assignParticipantsBusy = true
                                    assignParticipantsError = null
                                    dependencies.applicationScope.launch {
                                        try {
                                            val links = dependencies.diaryManager.listMemberActivityLinks(clubId, item.id)
                                            assigningParticipantIds = links.map { it.participantId }.toSet()
                                        } catch (t: Throwable) {
                                            assignParticipantsError = t.message
                                            assigningParticipantIds = emptySet()
                                        } finally {
                                            assignParticipantsBusy = false
                                        }
                                    }
                                },
                                onAssignGroup = {
                                    assignPlanGroupId = item.groupId
                                    assigningPlanItem = item
                                },
                                onOpenImage = { url -> planImageViewerUrl = url },
                                onViewDrawing = { raw, title -> drawingViewer = raw to title },
                                onOpenDrawing = { drawingPlanItem = item },
                                onOpenNestedDrawing = { drawingNestedPlanItem = it },
                                onViewNestedDrawing = { raw, title -> drawingViewer = raw to title },
                                onEdit = { editingPlanItem = item },
                                onDelete = {
                                    dependencies.applicationScope.launch {
                                        planMutating = true
                                        planActionError = null
                                        try {
                                            dependencies.diaryManager.deletePlanActivity(clubId, item.id)
                                            planItems = dependencies.diaryManager.fetchDateActivities(clubId, entry.id)
                                            val updatedGroups = dependencies.diaryManager.listTrainingGroups(clubId, entry.id)
                                            planGroups = updatedGroups
                                        } catch (t: Throwable) {
                                            planActionError = t.message
                                        } finally {
                                            planMutating = false
                                        }
                                    }
                                },
                                onMoveUp = {
                                    val scope = sameTrainingPlanScope(planItems, item)
                                    val idx = scope.indexOfFirst { it.id == item.id }
                                    if (idx > 0) {
                                        val targetOrder = scope[idx - 1].orderId
                                        dependencies.applicationScope.launch {
                                            planMutating = true
                                            planActionError = null
                                            try {
                                                dependencies.diaryManager.updatePlanActivityOrder(clubId, item.id, targetOrder)
                                                planItems = dependencies.diaryManager.fetchDateActivities(clubId, entry.id)
                                            } catch (t: Throwable) {
                                                planActionError = t.message
                                            } finally {
                                                planMutating = false
                                            }
                                        }
                                    }
                                },
                                onMoveDown = {
                                    val scope = sameTrainingPlanScope(planItems, item)
                                    val idx = scope.indexOfFirst { it.id == item.id }
                                    if (idx >= 0 && idx < scope.lastIndex) {
                                        val targetOrder = scope[idx + 1].orderId
                                        dependencies.applicationScope.launch {
                                            planMutating = true
                                            planActionError = null
                                            try {
                                                dependencies.diaryManager.updatePlanActivityOrder(clubId, item.id, targetOrder)
                                                planItems = dependencies.diaryManager.fetchDateActivities(clubId, entry.id)
                                            } catch (t: Throwable) {
                                                planActionError = t.message
                                            } finally {
                                                planMutating = false
                                            }
                                        }
                                    }
                                },
                                onDeleteNested = { nestedId ->
                                    dependencies.applicationScope.launch {
                                        planMutating = true
                                        planActionError = null
                                        try {
                                            dependencies.diaryManager.deletePlanNestedGroupActivity(clubId, nestedId)
                                            planItems = dependencies.diaryManager.fetchDateActivities(clubId, entry.id)
                                        } catch (t: Throwable) {
                                            planActionError = t.message
                                        } finally {
                                            planMutating = false
                                        }
                                    }
                                },
                                onEditNested = { nested ->
                                    editingNestedPlanItem = nested
                                },
                                onAssignNested = { nested ->
                                    val nestedId = nested.id
                                    if (nestedId != null) {
                                        assigningParticipantsItem = DiaryDateActivityItem(
                                            id = nestedId,
                                            predefinedActivity = nested.groupPredefinedActivity,
                                        )
                                        assignParticipantsBusy = true
                                        dependencies.applicationScope.launch {
                                            try {
                                                assigningParticipantIds = dependencies.diaryManager
                                                    .listMemberActivityLinks(clubId, nestedId)
                                                    .map { it.participantId }
                                                    .toSet()
                                            } finally {
                                                assignParticipantsBusy = false
                                            }
                                        }
                                    }
                                },
                            )
                        }
                    }
                }
            }

            assigningParticipantsItem?.let { assignItem ->
                val presentParticipantRows = participants
                    .filter { it.isPresentParticipant() }
                    .sortedBy { p -> activeMembers.find { it.id == p.memberId }?.fullName()?.lowercase() ?: "" }
                StableAlertDialog(
                    onDismissRequest = { if (!assignParticipantsBusy) assigningParticipantsItem = null },
                    title = { Text(tr("diary.assignParticipants", "Teilnehmer zuordnen")) },
                    text = {
                        Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                            Text(
                                assignItem.displayTitle(tr("diary.timeblock", "Zeitblock")),
                                style = MaterialTheme.typography.body2,
                                maxLines = 2,
                                overflow = TextOverflow.Ellipsis,
                            )
                            assignParticipantsError?.let {
                                Text(it, style = MaterialTheme.typography.caption, color = MaterialTheme.colors.error)
                            }
                            Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
                                OutlinedButton(
                                    onClick = {
                                        assigningParticipantIds = presentParticipantRows.map { it.id }.toSet()
                                    },
                                    enabled = !assignParticipantsBusy,
                                ) { Text(tr("common.all", "Alle")) }
                                OutlinedButton(
                                    onClick = { assigningParticipantIds = emptySet() },
                                    enabled = !assignParticipantsBusy,
                                ) { Text(tr("common.none", "Keine")) }
                            }
                            Column(
                                modifier = Modifier.heightIn(max = 280.dp).verticalScroll(rememberScrollState()),
                                verticalArrangement = Arrangement.spacedBy(4.dp),
                            ) {
                                presentParticipantRows.forEach { p ->
                                    val name = activeMembers.find { it.id == p.memberId }?.fullName()
                                        ?: "${tr("mobile.member", "Mitglied")} ${p.memberId}"
                                    Row(
                                        modifier = Modifier.fillMaxWidth(),
                                        verticalAlignment = Alignment.CenterVertically,
                                    ) {
                                        Checkbox(
                                            checked = p.id in assigningParticipantIds,
                                            enabled = !assignParticipantsBusy,
                                            onCheckedChange = { checked ->
                                                assigningParticipantIds = if (checked) {
                                                    assigningParticipantIds + p.id
                                                } else {
                                                    assigningParticipantIds - p.id
                                                }
                                            },
                                        )
                                        Text(name, style = MaterialTheme.typography.body2)
                                    }
                                }
                            }
                        }
                    },
                    confirmButton = {
                        TextButton(
                            enabled = canWriteDiary && !assignParticipantsBusy,
                            onClick = {
                                dependencies.applicationScope.launch {
                                    assignParticipantsBusy = true
                                    assignParticipantsError = null
                                    try {
                                        val existing = dependencies.diaryManager
                                            .listMemberActivityLinks(clubId, assignItem.id)
                                            .map { it.participantId }
                                            .toSet()
                                        val target = assigningParticipantIds
                                        val toAdd = target - existing
                                        val toRemove = existing - target
                                        if (toAdd.isNotEmpty()) {
                                            dependencies.diaryManager.addParticipantsToMemberActivity(
                                                clubId,
                                                assignItem.id,
                                                toAdd.toList(),
                                            )
                                        }
                                        toRemove.forEach { pid ->
                                            dependencies.diaryManager.removeParticipantFromMemberActivity(
                                                clubId,
                                                assignItem.id,
                                                pid,
                                            )
                                        }
                                        assigningParticipantsItem = null
                                    } catch (t: Throwable) {
                                        assignParticipantsError = t.message
                                    } finally {
                                        assignParticipantsBusy = false
                                    }
                                }
                            },
                        ) { Text(tr("common.save", "Speichern")) }
                    },
                    dismissButton = {
                        TextButton(
                            enabled = !assignParticipantsBusy,
                            onClick = { assigningParticipantsItem = null },
                        ) { Text(tr("mobile.cancel", "Abbrechen")) }
                    },
                )
            }

            assigningPlanItem?.let { assignItem ->
                var assignGroupMenu by remember { mutableStateOf(false) }
                StableAlertDialog(
                    onDismissRequest = { if (!planMutating) assigningPlanItem = null },
                    title = { Text(tr("diary.planAssignGroup", "Zuordnen")) },
                    text = {
                        Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                            Text(
                                assignItem.displayTitle(tr("diary.timeblock", "Zeitblock")),
                                style = MaterialTheme.typography.body2,
                                maxLines = 2,
                                overflow = TextOverflow.Ellipsis,
                            )
                            Box(modifier = Modifier.fillMaxWidth()) {
                                val selectedLabel = assignPlanGroupId?.let { id ->
                                    planGroups.find { it.id == id }?.name ?: "Gruppe $id"
                                } ?: tr("diary.planGroupGlobal", "Alle / keine Gruppe")
                                OutlinedButton(
                                    onClick = { assignGroupMenu = true },
                                    enabled = canWriteDiary && !planMutating,
                                    modifier = Modifier.fillMaxWidth(),
                                ) { Text("${tr("diary.planAssignGroup", "Zuordnen")}: $selectedLabel") }
                                DropdownMenu(expanded = assignGroupMenu, onDismissRequest = { assignGroupMenu = false }) {
                                    DropdownMenuItem(
                                        onClick = {
                                            assignPlanGroupId = null
                                            assignGroupMenu = false
                                        },
                                    ) { Text(tr("diary.planGroupGlobal", "Alle / keine Gruppe")) }
                                    planGroups.forEach { g ->
                                        DropdownMenuItem(
                                            onClick = {
                                                assignPlanGroupId = g.id
                                                assignGroupMenu = false
                                            },
                                        ) { Text(g.name ?: "Gruppe ${g.id}") }
                                    }
                                }
                            }
                        }
                    },
                    confirmButton = {
                        TextButton(
                            enabled = canWriteDiary && !planMutating,
                            onClick = {
                                dependencies.applicationScope.launch {
                                    planMutating = true
                                    planActionError = null
                                    try {
                                        dependencies.diaryManager.updatePlanActivity(
                                            clubId,
                                            assignItem.id,
                                            UpdateDiaryPlanActivityRequest(groupId = assignPlanGroupId),
                                        )
                                        assigningPlanItem = null
                                        planItems = dependencies.diaryManager.fetchDateActivities(clubId, entry.id)
                                        planGroups = dependencies.diaryManager.listTrainingGroups(clubId, entry.id)
                                    } catch (t: Throwable) {
                                        planActionError = t.message
                                    } finally {
                                        planMutating = false
                                    }
                                }
                            },
                        ) { Text(tr("common.save", "Speichern")) }
                    },
                    dismissButton = {
                        TextButton(
                            enabled = canWriteDiary && !planMutating,
                            onClick = { assigningPlanItem = null },
                        ) { Text(tr("mobile.cancel", "Abbrechen")) }
                    },
                )
            }

            editingPlanItem?.let { editItem ->
                var editGroupMenu by remember { mutableStateOf(false) }
                StableAlertDialog(
                    onDismissRequest = { if (!planMutating) editingPlanItem = null },
                    title = { Text(tr("diary.editPlanItem", "Eintrag bearbeiten")) },
                    text = {
                        Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                            if (!editItem.isTimeblock) {
                                Text(
                                    tr(
                                        "diary.predefinedSearchHint",
                                        "Vordefinierte Übung: nach Kürzel suchen (mind. 2 Zeichen) oder Standardliste öffnen.",
                                    ),
                                    style = MaterialTheme.typography.caption,
                                    color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f),
                                )
                                OutlinedTextField(
                                    value = editPlanPredefQuery,
                                    onValueChange = { editPlanPredefQuery = it },
                                    label = { Text(tr("diary.predefinedSearchLabel", "Kürzel-Suche")) },
                                    modifier = Modifier.fillMaxWidth(),
                                    enabled = canWriteDiary && !planMutating,
                                    singleLine = true,
                                )
                                if (editPlanPredefLoading) {
                                    Text(tr("mobile.searching", "Suche …"), style = MaterialTheme.typography.caption)
                                }
                                editPlanPredefResults.take(10).forEach { dto ->
                                    TextButton(
                                        enabled = canWriteDiary && !planMutating,
                                        onClick = {
                                            editPlanPickedPredefinedId = dto.id
                                            editPlanName = dto.predefinedDtoDisplayLabel()
                                            dto.duration?.let { editPlanDuration = it.toString() }
                                            editPlanDurationText = dto.durationText.orEmpty()
                                        },
                                        modifier = Modifier.fillMaxWidth(),
                                    ) {
                                        val code = dto.code?.takeIf { it.isNotBlank() }?.let { " ($it)" }.orEmpty()
                                        Text(dto.predefinedDtoDisplayLabel() + code)
                                    }
                                }
                                TextButton(
                                    enabled = canWriteDiary && !planMutating && editPlanPickedPredefinedId != null,
                                    onClick = { editPlanPickedPredefinedId = null },
                                ) { Text(tr("diary.predefinedClearPick", "Auswahl zurücksetzen")) }
                                OutlinedTextField(
                                    value = editPlanName,
                                    onValueChange = {
                                        editPlanName = it
                                        editPlanPickedPredefinedId = null
                                    },
                                    label = { Text(tr("diary.planActivityName", "Aktivität / Bezeichnung")) },
                                    modifier = Modifier.fillMaxWidth(),
                                    enabled = canWriteDiary && !planMutating,
                                    singleLine = true,
                                )
                            } else {
                                Text(tr("diary.planTimeblockEditHint", "Zeitblock – nur Dauer anpassbar."), style = MaterialTheme.typography.caption)
                            }
                            Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) {
                                OutlinedTextField(
                                    value = editPlanDuration,
                                    onValueChange = { editPlanDuration = it },
                                    label = { Text(tr("diary.planDurationMin", "Minuten")) },
                                    modifier = Modifier.weight(1f),
                                    enabled = canWriteDiary && !planMutating,
                                    singleLine = true,
                                )
                                OutlinedTextField(
                                    value = editPlanDurationText,
                                    onValueChange = { editPlanDurationText = it },
                                    label = { Text(tr("diary.planDurationText", "Dauer (Freitext)")) },
                                    modifier = Modifier.weight(1f),
                                    enabled = canWriteDiary && !planMutating,
                                    singleLine = true,
                                )
                            }
                            if (planGroups.isNotEmpty()) {
                                Box(modifier = Modifier.fillMaxWidth()) {
                                    val el = editPlanGroupId?.let { id ->
                                        planGroups.find { it.id == id }?.name ?: "Gruppe $id"
                                    } ?: tr("diary.planGroupGlobal", "Alle / keine Gruppe")
                                    OutlinedButton(
                                        onClick = { editGroupMenu = true },
                                        enabled = canWriteDiary && !planMutating,
                                        modifier = Modifier.fillMaxWidth(),
                                    ) { Text("${tr("diary.planAssignGroup", "Zuordnung")}: $el") }
                                    DropdownMenu(expanded = editGroupMenu, onDismissRequest = { editGroupMenu = false }) {
                                        DropdownMenuItem(onClick = {
                                            editPlanGroupId = null
                                            editGroupMenu = false
                                        }) { Text(tr("diary.planGroupGlobal", "Alle / keine Gruppe")) }
                                        planGroups.forEach { g ->
                                            DropdownMenuItem(onClick = {
                                                editPlanGroupId = g.id
                                                editGroupMenu = false
                                            }) { Text(g.name ?: "Gruppe ${g.id}") }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    confirmButton = {
                        TextButton(
                            enabled = canWriteDiary && !planMutating,
                            onClick = {
                                dependencies.applicationScope.launch {
                                    planMutating = true
                                    planActionError = null
                                    try {
                                        val pickedPredef = editPlanPickedPredefinedId
                                        val updateBody = when {
                                            editItem.isTimeblock -> UpdateDiaryPlanActivityRequest(
                                                duration = editPlanDuration.toIntOrNull(),
                                                durationText = editPlanDurationText.trim().takeIf { it.isNotEmpty() },
                                                groupId = editPlanGroupId,
                                            )
                                            pickedPredef != null -> UpdateDiaryPlanActivityRequest(
                                                predefinedActivityId = pickedPredef,
                                                duration = editPlanDuration.toIntOrNull(),
                                                durationText = editPlanDurationText.trim().takeIf { it.isNotEmpty() },
                                                groupId = editPlanGroupId,
                                            )
                                            editPlanName.isNotBlank() -> UpdateDiaryPlanActivityRequest(
                                                customActivityName = editPlanName.trim(),
                                                duration = editPlanDuration.toIntOrNull(),
                                                durationText = editPlanDurationText.trim().takeIf { it.isNotEmpty() },
                                                groupId = editPlanGroupId,
                                            )
                                            else -> UpdateDiaryPlanActivityRequest(
                                                duration = editPlanDuration.toIntOrNull(),
                                                durationText = editPlanDurationText.trim().takeIf { it.isNotEmpty() },
                                                groupId = editPlanGroupId,
                                            )
                                        }
                                        dependencies.diaryManager.updatePlanActivity(
                                            clubId,
                                            editItem.id,
                                            updateBody,
                                        )
                                        editingPlanItem = null
                                        planItems = dependencies.diaryManager.fetchDateActivities(clubId, entry.id)
                                        planGroups = dependencies.diaryManager.listTrainingGroups(clubId, entry.id)
                                    } catch (t: Throwable) {
                                        planActionError = t.message
                                    } finally {
                                        planMutating = false
                                    }
                                }
                            },
                        ) { Text(tr("common.save", "Speichern")) }
                    },
                    dismissButton = {
                        TextButton(
                            enabled = canWriteDiary && !planMutating,
                            onClick = { editingPlanItem = null },
                        ) { Text(tr("mobile.cancel", "Abbrechen")) }
                    },
                )
            }

            DiaryPlanSupplementDialogs(
                clubId = clubId,
                diaryDateId = entry.id,
                dependencies = dependencies,
                creatingDrawing = creatingDrawingActivity,
                drawingItem = drawingPlanItem,
                drawingNestedItem = drawingNestedPlanItem,
                editingNestedItem = editingNestedPlanItem,
                selectedGroupId = planGroupFilter,
                canWriteDiary = canWriteDiary,
                planMutating = planMutating,
                onDismissDrawing = {
                    creatingDrawingActivity = false
                    drawingPlanItem = null
                    drawingNestedPlanItem = null
                },
                onDismissEditNested = { editingNestedPlanItem = null },
                onBusy = { planMutating = it },
                onError = { planActionError = it },
                onPlanUpdated = {
                    planItems = it
                    creatingDrawingActivity = false
                    drawingPlanItem = null
                    drawingNestedPlanItem = null
                    editingNestedPlanItem = null
                },
            )
            drawingViewer?.let { (raw, title) ->
                DiaryCourtDrawingViewerDialog(
                    title = title,
                    data = DiaryCourtDrawingData.fromJson(raw),
                    onDismiss = { drawingViewer = null },
                )
            }

            SectionTitle("Tags")
            FlowRowText(items = entry.diaryTags.map { it.name })
            Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) {
                OutlinedTextField(
                    value = tagName,
                    onValueChange = { tagName = it },
                    label = { Text(tr("mobile.newTag", "Neuer Tag")) },
                    modifier = Modifier.weight(1f),
                    singleLine = true,
                )
                Button(onClick = {
                    val value = tagName.trim()
                    if (value.isNotEmpty()) {
                        dependencies.applicationScope.launch {
                            dependencies.diaryManager.createAndLinkTag(clubId, entry.id, value)
                            tagName = ""
                        }
                    }
                }, enabled = canWriteDiary) { Text(tr("mobile.add", "Add")) }
            }
            entry.diaryTags.forEach { tag ->
                TextButton(onClick = { dependencies.applicationScope.launch { dependencies.diaryManager.removeTag(clubId, tag.id) } }, enabled = canWriteDiary) {
                    Text("${tr("mobile.deleteTag", "Tag entfernen")}: ${tag.name}")
                }
            }

            SectionTitle("Notizen")
            entry.diaryNotes.forEach { noteEntry ->
                Card(modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp), elevation = 1.dp) {
                    Row(modifier = Modifier.padding(8.dp), verticalAlignment = Alignment.CenterVertically) {
                        Text(noteEntry.content.orEmpty(), modifier = Modifier.weight(1f))
                        TextButton(onClick = { dependencies.applicationScope.launch { dependencies.diaryManager.deleteNote(clubId, noteEntry.id) } }, enabled = canWriteDiary) {
                            Text(tr("common.delete", "Löschen"))
                        }
                    }
                }
            }
            Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) {
                OutlinedTextField(
                    value = note,
                    onValueChange = { note = it },
                    label = { Text(tr("mobile.newNote", "Neue Notiz")) },
                    modifier = Modifier.weight(1f),
                )
                Button(onClick = {
                    val value = note.trim()
                    if (value.isNotEmpty()) {
                        dependencies.applicationScope.launch {
                            dependencies.diaryManager.addNote(clubId, entry.id, value)
                            note = ""
                        }
                    }
                }, enabled = canWriteDiary) { Text(tr("mobile.add", "Add")) }
            }
            planImageViewerUrl?.let { u ->
                val auth = dependencies.diaryAuthHeaders()
                StableAlertDialog(
                    onDismissRequest = { planImageViewerUrl = null },
                    confirmButton = {
                        TextButton(onClick = { planImageViewerUrl = null }) {
                            Text(tr("common.close", "Schließen"))
                        }
                    },
                    title = { Text(tr("diary.planImageTitle", "Übungsbild")) },
                    text = {
                        Box(Modifier.fillMaxWidth().heightIn(min = 200.dp)) {
                            AuthenticatedAsyncImage(
                                imageUrl = u,
                                authHeaders = auth,
                                modifier = Modifier.fillMaxWidth(),
                                contentDescription = null,
                            )
                        }
                    },
                )
            }
            }
        }
    }
}

private sealed class MembersStackRoute {
    object Browse : MembersStackRoute()
    data class Detail(val memberId: Int) : MembersStackRoute()
    /** `null` = neues Mitglied anlegen */
    data class Edit(val memberId: Int?) : MembersStackRoute()
    object GroupPhoto : MembersStackRoute()
    /** `returnToGroupPhotoManage` = true: Zurück zur Gruppenfoto-Verwaltung (von dort geöffnet). */
    data class GroupPhotoPortraitCrop(val returnToGroupPhotoManage: Boolean = false) : MembersStackRoute()
    object TransferRun : MembersStackRoute()
}

private fun trainingWeekdayLabel(weekday: Int): String = when (weekday) {
    0 -> "So"
    1 -> "Mo"
    2 -> "Di"
    3 -> "Mi"
    4 -> "Do"
    5 -> "Fr"
    6 -> "Sa"
    else -> "?$weekday"
}

private fun displayActivityDate(raw: String?): String {
    if (raw.isNullOrBlank()) return "-"
    val s = raw.trim()
    return s.take(10).ifBlank { s }
}

@Composable
private fun MembersScreen(
    dependencies: AppDependencies,
    onNestedOpenChange: (Boolean) -> Unit,
    openMemberPortraitCropRequested: Boolean,
    onConsumeOpenMemberPortraitCrop: () -> Unit,
    openMemberGalleryRequested: Boolean,
    onConsumeOpenMemberGallery: () -> Unit,
) {
    val clubState by dependencies.clubManager.state.collectAsState()
    val membersState by dependencies.membersManager.state.collectAsState()
    val clubId = clubState.currentClubId ?: return
    val canReadMembers = clubState.currentPermissions?.canReadMembers() == true
    val canWriteMembers = clubState.currentPermissions?.canWriteMembers() == true
    var stack by remember { mutableStateOf<MembersStackRoute>(MembersStackRoute.Browse) }
    LaunchedEffect(openMemberPortraitCropRequested) {
        if (openMemberPortraitCropRequested) {
            stack = MembersStackRoute.GroupPhotoPortraitCrop(returnToGroupPhotoManage = false)
            onConsumeOpenMemberPortraitCrop()
        }
    }
    LaunchedEffect(openMemberGalleryRequested) {
        if (openMemberGalleryRequested) {
            stack = MembersStackRoute.GroupPhoto
            onConsumeOpenMemberGallery()
        }
    }
    var query by rememberSaveable { mutableStateOf("") }

    LaunchedEffect(stack, clubId) {
        onNestedOpenChange(stack !is MembersStackRoute.Browse)
    }

    DisposableEffect(Unit) {
        onDispose { onNestedOpenChange(false) }
    }

    LaunchedEffect(clubId) {
        stack = MembersStackRoute.Browse
        dependencies.membersManager.loadMembers(clubId)
    }

    if (!canReadMembers) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(horizontal = ScreenHorizontalPadding, vertical = 16.dp),
        ) {
            Header(tr("members.title", "Mitglieder"))
            Text(tr("members.noReadPermission", "Keine Berechtigung zur Mitgliederliste."))
        }
        return
    }

    when (val route = stack) {
        is MembersStackRoute.Detail -> {
            val member = membersState.members.firstOrNull { it.id == route.memberId }
            if (member == null) {
                LaunchedEffect(route.memberId, membersState.members) {
                    if (!membersState.isLoading && membersState.members.isNotEmpty()) {
                        stack = MembersStackRoute.Browse
                    }
                }
                Column(Modifier.fillMaxSize().padding(ScreenHorizontalPadding)) {
                    if (membersState.isLoading) LoadingInline()
                    else Text(tr("members.memberNotFound", "Mitglied nicht gefunden."), modifier = Modifier.padding(16.dp))
                    TextButton(onClick = { stack = MembersStackRoute.Browse }) { Text(tr("mobile.back", "Zurück")) }
                }
            } else {
                MemberDetailRoute(
                    clubId = clubId,
                    member = member,
                    dependencies = dependencies,
                    canWriteMembers = canWriteMembers,
                    onBack = { stack = MembersStackRoute.Browse },
                    onEdit = { stack = MembersStackRoute.Edit(member.id) },
                )
            }
        }
        is MembersStackRoute.Edit -> {
            val seed = route.memberId?.let { id -> membersState.members.firstOrNull { it.id == id } }
            MemberEditRoute(
                clubId = clubId,
                seedMember = seed,
                dependencies = dependencies,
                canWriteMembers = canWriteMembers,
                onBack = {
                    stack = if (route.memberId != null) MembersStackRoute.Detail(route.memberId) else MembersStackRoute.Browse
                },
                onSaved = { newId ->
                    dependencies.applicationScope.launch { dependencies.membersManager.loadMembers(clubId) }
                    stack = if (newId != null) MembersStackRoute.Detail(newId) else MembersStackRoute.Browse
                },
            )
        }
        MembersStackRoute.GroupPhoto -> {
            MemberGroupPhotoManageRoute(
                clubId = clubId,
                dependencies = dependencies,
                canReadMembers = canReadMembers,
                canWriteMembers = canWriteMembers,
                onBack = { stack = MembersStackRoute.Browse },
                onOpenPortraitCrop = {
                    stack = MembersStackRoute.GroupPhotoPortraitCrop(returnToGroupPhotoManage = true)
                },
            )
        }
        is MembersStackRoute.GroupPhotoPortraitCrop -> {
            MemberGroupPortraitCropRoute(
                clubId = clubId,
                members = membersState.members,
                dependencies = dependencies,
                canWriteMembers = canWriteMembers,
                onBack = {
                    stack = if (route.returnToGroupPhotoManage) {
                        MembersStackRoute.GroupPhoto
                    } else {
                        MembersStackRoute.Browse
                    }
                },
            )
        }
        MembersStackRoute.TransferRun -> {
            MemberTransferRunRoute(
                clubId = clubId,
                dependencies = dependencies,
                canWriteMembers = canWriteMembers,
                onBack = { stack = MembersStackRoute.Browse },
            )
        }
        MembersStackRoute.Browse -> {
            val languageCode = LocalLanguageCode.current
            fun trStr(key: String, fallback: String): String = MobileStrings.get(languageCode, key, fallback)
            val permissions = clubState.currentPermissions
            val canWriteMyTt = permissions?.canWriteMyTischtennis() == true
            var memberScope by rememberSaveable { mutableStateOf("active") }
            var showInactiveMembers by rememberSaveable { mutableStateOf(false) }
            var selectedTrainingGroupId by rememberSaveable { mutableStateOf<String?>(null) }
            var lastTrainingFilter by rememberSaveable { mutableStateOf("all") }
            var selectedSort by rememberSaveable { mutableStateOf("name") }
            var sortAscending by rememberSaveable { mutableStateOf(true) }
            var showTrainingParticipationsColumn by rememberSaveable { mutableStateOf(false) }
            var memberActionsPanelExpanded by rememberSaveable { mutableStateOf(false) }
            var memberFiltersPanelExpanded by rememberSaveable { mutableStateOf(false) }
            var groupFilterMenuOpen by remember { mutableStateOf(false) }
            var ratingsBusy by remember { mutableStateOf(false) }
            var membersActionNote by remember { mutableStateOf<String?>(null) }
            var pendingBulkForms by remember { mutableStateOf(false) }
            var pendingBulkTestOff by remember { mutableStateOf(false) }
            var pendingDeactivateMember by remember { mutableStateOf<Member?>(null) }
            var clickTtPendingMemberIds by remember { mutableStateOf<Set<Int>>(emptySet()) }
            var ordersForMember by remember { mutableStateOf<Member?>(null) }
            var ordersList by remember { mutableStateOf<List<de.tsschulz.tt_tagebuch.shared.api.models.MemberOrderDto>>(emptyList()) }
            var ordersLoading by remember { mutableStateOf(false) }
            var ordersError by remember { mutableStateOf<String?>(null) }
            var statsForMember by remember { mutableStateOf<Member?>(null) }
            var statsList by remember { mutableStateOf<List<MemberActivityStatDto>>(emptyList()) }
            var statsLastParticipations by remember { mutableStateOf<List<MemberLastParticipationDto>>(emptyList()) }
            var statsLoading by remember { mutableStateOf(false) }
            var statsError by remember { mutableStateOf<String?>(null) }
            var notesForMember by remember { mutableStateOf<Member?>(null) }
            var notesList by remember { mutableStateOf<List<DiaryMemberNoteDto>>(emptyList()) }
            var notesDraft by rememberSaveable { mutableStateOf("") }
            var notesLoading by remember { mutableStateOf(false) }
            var notesError by remember { mutableStateOf<String?>(null) }
            var seasonStartYear by rememberSaveable { mutableStateOf(getSeasonStartYearFromDateToday()) }
            var seasonMenuOpen by remember { mutableStateOf(false) }
            var selectedAgeGroup by rememberSaveable { mutableStateOf("") }
            var ageGroupMenuOpen by remember { mutableStateOf(false) }
            var selectedAgeFrom by rememberSaveable { mutableStateOf("") }
            var selectedAgeTo by rememberSaveable { mutableStateOf("") }
            var selectedGender by rememberSaveable { mutableStateOf("") }
            var genderMenuOpen by remember { mutableStateOf(false) }
            val androidCtx = LocalContext.current
            val copyToClipboard: (String) -> Unit = { value ->
                val cm = androidCtx.getSystemService(android.content.Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
                cm.setPrimaryClip(ClipData.newPlainText("tt_tagebuch", value))
            }
            val qualityReq = remember(clubState.clubs, clubId) {
                clubState.clubs.firstOrNull { it.id == clubId }?.memberDataQualityRequirements ?: MemberDataQualityRequirements()
            }
            val clubName = remember(clubState.clubs, clubId) {
                clubState.clubs.firstOrNull { it.id == clubId }?.name ?: "Verein"
            }
            val trainingGroupFilterOptions = remember(membersState.members) {
                val map = linkedMapOf<String, String>()
                membersState.members.forEach { m ->
                    m.trainingGroups.forEach { g ->
                        if (g.id != 0) {
                            map.putIfAbsent(g.id.toString(), g.name.ifBlank { "#${g.id}" })
                        }
                    }
                }
                map.entries.sortedBy { it.value.lowercase() }
            }
            val filteredMembers = remember(
                membersState.members,
                query,
                memberScope,
                showInactiveMembers,
                selectedTrainingGroupId,
                lastTrainingFilter,
                qualityReq,
                seasonStartYear,
                selectedAgeGroup,
                selectedAgeFrom,
                selectedAgeTo,
                selectedGender,
                selectedSort,
                sortAscending,
            ) {
                val normalizedQuery = query.trim().lowercase()
                val scopeIncludesInactive =
                    memberScope == "test" || memberScope == "inactive" || memberScope == "dataIncomplete"
                membersState.members
                    .filter { member ->
                        if (!member.active && !showInactiveMembers && !scopeIncludesInactive) {
                            return@filter false
                        }
                        when (memberScope) {
                            "all" -> Unit
                            "active" -> if (!member.active || member.testMembership == true) return@filter false
                            "test" -> if (member.testMembership != true) return@filter false
                            "activeTest" -> if (!member.active || member.testMembership != true) return@filter false
                            "inactive" -> if (member.active) return@filter false
                            "notTraining" -> if (member.notInTraining != true) return@filter false
                            "needsForm" -> if (member.memberFormHandedOver == true) return@filter false
                            "dataIncomplete" -> if (memberDataQualityIssues(member, qualityReq).isEmpty()) return@filter false
                            "activeDataIncomplete" -> {
                                if (!member.active || member.testMembership == true) return@filter false
                                if (memberDataQualityIssues(member, qualityReq).isEmpty()) return@filter false
                            }
                            else -> Unit
                        }
                        val gid = selectedTrainingGroupId
                        if (gid != null) {
                            val ids = member.trainingGroups.map { it.id.toString() }
                            if (!ids.contains(gid)) return@filter false
                        }
                        when (lastTrainingFilter) {
                            "hasDate" -> if (!memberHasValidLastTrainingDate(member)) return@filter false
                            "noDate" -> if (memberHasValidLastTrainingDate(member)) return@filter false
                            "notInTraining" -> if (member.notInTraining != true) return@filter false
                        }
                        val ag = selectedAgeGroup.trim()
                        if (ag.isNotEmpty() && ag != "range") {
                            if (!memberMatchesTtAgeClass(member, ag, seasonStartYear)) return@filter false
                        }
                        if (ag == "range") {
                            val age = memberAgeYears(member) ?: return@filter false
                            val minA = selectedAgeFrom.trim().toIntOrNull()
                            val maxA = selectedAgeTo.trim().toIntOrNull()
                            if (minA != null && age < minA) return@filter false
                            if (maxA != null && age > maxA) return@filter false
                        }
                        val gFilter = selectedGender.trim()
                        if (gFilter.isNotEmpty()) {
                            val mg = member.gender?.trim()?.lowercase().orEmpty()
                            if (mg != gFilter.lowercase()) return@filter false
                        }
                        if (normalizedQuery.isNotEmpty()) {
                            val haystack = listOf(
                                member.fullName(),
                                member.firstName,
                                member.lastName,
                                member.birthDate.orEmpty(),
                                member.gender.orEmpty(),
                                member.city.orEmpty(),
                                member.street.orEmpty(),
                                member.postalCode.orEmpty(),
                                member.email.orEmpty(),
                                member.phone.orEmpty(),
                                formatMemberPhonesLine(member),
                                formatMemberEmailsLine(member),
                            ).joinToString(" ").lowercase()
                            if (!haystack.contains(normalizedQuery)) return@filter false
                        }
                        true
                    }
                    .sortedWith(
                        Comparator { a, b ->
                            val base = when (selectedSort) {
                                "ttr" -> (a.ttr ?: Int.MIN_VALUE).compareTo(b.ttr ?: Int.MIN_VALUE)
                                "qttr" -> (a.qttr ?: Int.MIN_VALUE).compareTo(b.qttr ?: Int.MIN_VALUE)
                                "lastTraining" -> a.lastTraining.orEmpty().compareTo(b.lastTraining.orEmpty())
                                "birthDate" -> a.birthDate.orEmpty().compareTo(b.birthDate.orEmpty())
                                "firstName" -> a.firstName.lowercase().compareTo(b.firstName.lowercase())
                                "age" -> (memberAgeYears(a) ?: Int.MIN_VALUE).compareTo(memberAgeYears(b) ?: Int.MIN_VALUE)
                                "openTasks" -> memberOpenTasksCount(a, qualityReq).compareTo(memberOpenTasksCount(b, qualityReq))
                                else -> {
                                    val byLast = a.lastName.lowercase().compareTo(b.lastName.lowercase())
                                    if (byLast != 0) byLast else a.firstName.lowercase().compareTo(b.firstName.lowercase())
                                }
                            }
                            if (sortAscending) base else -base
                        },
                    )
            }
            val activeRegularCount = remember(membersState.members) {
                membersState.members.count { it.active && it.testMembership != true }
            }
            val inactiveCount = remember(membersState.members) { membersState.members.count { !it.active } }
            val testActiveCount = remember(membersState.members) {
                membersState.members.count { it.testMembership == true && it.active }
            }
            fun scopeOptionCount(key: String): Int = when (key) {
                "all" -> membersState.members.size
                "active" -> membersState.members.count { it.active && it.testMembership != true }
                "test" -> membersState.members.count { it.testMembership == true }
                "activeTest" -> membersState.members.count { it.active && it.testMembership == true }
                "notTraining" -> membersState.members.count { it.notInTraining == true }
                "needsForm" -> membersState.members.count { it.memberFormHandedOver != true }
                "activeDataIncomplete" -> membersState.members.count { m ->
                    m.active && m.testMembership != true && memberDataQualityIssues(m, qualityReq).isNotEmpty()
                }
                "dataIncomplete" -> membersState.members.count { memberDataQualityIssues(it, qualityReq).isNotEmpty() }
                "inactive" -> membersState.members.count { !it.active }
                else -> 0
            }
            fun formatBirthAndAge(member: Member): String {
                val raw = member.birthDate?.take(10).orEmpty()
                if (raw.length < 10) return if (raw.isBlank()) "-" else raw
                return try {
                    val date = java.time.LocalDate.parse(raw)
                    val age = java.time.Period.between(date, java.time.LocalDate.now()).years
                    "$raw ($age)"
                } catch (_: Throwable) {
                    raw
                }
            }
            val filteredEmailsForBcc = remember(filteredMembers) {
                buildList {
                    val seen = mutableSetOf<String>()
                    for (m in filteredMembers) {
                        for (e in extractEmailAddressesFromMember(m)) {
                            if (seen.add(e)) add(e)
                        }
                    }
                }
            }
            val filteredPhonesPlain = remember(filteredMembers) {
                filteredMembers.flatMap { m ->
                    val line = formatMemberPhonesLine(m)
                    if (line == MEMBER_CONTACT_EMPTY) {
                        emptyList()
                    } else {
                        line.split(",").map { it.trim().substringBefore("(").trim() }.filter { it.isNotBlank() }
                    }
                }.distinct()
            }
            val filteredEmailsPlain = remember(filteredMembers) {
                filteredMembers.flatMap { extractEmailAddressesFromMember(it) }.distinct()
            }
            fun loadMemberOrders(member: Member) {
                ordersForMember = member
                ordersLoading = true
                ordersError = null
                ordersList = emptyList()
                dependencies.applicationScope.launch {
                    try {
                        ordersList = dependencies.memberOrdersApi.listForMember(clubId, member.id).orders
                    } catch (t: Throwable) {
                        ordersError = t.message
                    } finally {
                        ordersLoading = false
                    }
                }
            }
            fun loadMemberStats(member: Member) {
                statsForMember = member
                statsLoading = true
                statsError = null
                statsList = emptyList()
                statsLastParticipations = emptyList()
                dependencies.applicationScope.launch {
                    try {
                        coroutineScope {
                            val allStats = async { dependencies.membersManager.memberActivityStats(clubId, member.id, "all") }
                            val recent = async { dependencies.membersManager.memberLastParticipations(clubId, member.id, 8) }
                            statsList = allStats.await()
                            statsLastParticipations = recent.await()
                        }
                    } catch (t: Throwable) {
                        statsError = t.message
                    } finally {
                        statsLoading = false
                    }
                }
            }
            fun loadMemberNotes(member: Member) {
                notesForMember = member
                notesDraft = ""
                notesLoading = true
                notesError = null
                notesList = emptyList()
                dependencies.applicationScope.launch {
                    try {
                        notesList = dependencies.membersManager.listMemberNotes(clubId, member.id)
                    } catch (t: Throwable) {
                        notesError = t.message
                    } finally {
                        notesLoading = false
                    }
                }
            }
            fun quickMarkForm(member: Member) {
                if (!canWriteMembers) return
                dependencies.applicationScope.launch {
                    val r = runCatching { dependencies.membersManager.quickUpdateMemberFormHandedOver(clubId, member.id) }
                    r.onSuccess { membersActionNote = it.message ?: trStr("members.formMarkedAsHandedOver", "Formularstatus aktualisiert.") }
                        .onFailure { membersActionNote = it.message ?: trStr("members.errorMarkingForm", "Formularstatus konnte nicht geändert werden.") }
                    dependencies.membersManager.loadMembers(clubId)
                }
            }
            fun requestClickTt(member: Member) {
                if (!canWriteMembers || clickTtPendingMemberIds.contains(member.id)) return
                clickTtPendingMemberIds = clickTtPendingMemberIds + member.id
                dependencies.applicationScope.launch {
                    try {
                        val r = dependencies.membersManager.requestClickTtRegistration(clubId, member.id)
                        membersActionNote = r.message ?: r.error ?: trStr("members.clickTtRequestSuccess", "Click-TT-Antrag angestoßen.")
                        dependencies.membersManager.loadMembers(clubId)
                    } catch (t: Throwable) {
                        membersActionNote = t.message ?: trStr("members.clickTtRequestError", "Click-TT-Antrag konnte nicht gestartet werden.")
                    } finally {
                        clickTtPendingMemberIds = clickTtPendingMemberIds - member.id
                    }
                }
            }

            if (pendingBulkForms && canWriteMembers) {
                val targets = filteredMembers.filter { it.memberFormHandedOver != true }
                StableAlertDialog(
                    onDismissRequest = { pendingBulkForms = false },
                    title = { Text(tr("members.bulkFormsTitle", "Formulare übergeben")) },
                    text = {
                        Text(
                            if (targets.isEmpty()) {
                                tr("members.bulkFormsEmpty", "Keine passenden Mitglieder in der aktuellen Filterliste.")
                            } else {
                                tr(
                                    "members.bulkFormsConfirm",
                                    "Für %d Mitglieder (gefilterte Liste) das Formular als ausgehändigt markieren?",
                                ).format(targets.size)
                            },
                        )
                    },
                    confirmButton = {
                        TextButton(
                            onClick = {
                                pendingBulkForms = false
                                if (targets.isEmpty()) return@TextButton
                                dependencies.applicationScope.launch {
                                    var failed = 0
                                    for (m in targets) {
                                        val r = dependencies.membersManager.quickUpdateMemberFormHandedOver(clubId, m.id)
                                        if (r.success != true) failed++
                                    }
                                    dependencies.membersManager.loadMembers(clubId)
                                    membersActionNote = if (failed == 0) {
                                        trStr("members.bulkFormsDone", "Formularstatus aktualisiert.")
                                    } else {
                                        trStr("members.bulkFormsPartial", "Teilweise fehlgeschlagen (%d).").replace("%d", failed.toString())
                                    }
                                }
                            },
                        ) { Text(tr("mobile.ok", "OK")) }
                    },
                    dismissButton = {
                        TextButton(onClick = { pendingBulkForms = false }) {
                            Text(tr("mobile.cancel", "Abbrechen"))
                        }
                    },
                )
            }
            if (pendingBulkTestOff && canWriteMembers) {
                val targets = filteredMembers.filter { it.testMembership == true }
                StableAlertDialog(
                    onDismissRequest = { pendingBulkTestOff = false },
                    title = { Text(tr("members.bulkTestOffTitle", "Testmitgliedschaften beenden")) },
                    text = {
                        Text(
                            if (targets.isEmpty()) {
                                tr("members.bulkTestOffEmpty", "Keine Testmitglieder in der aktuellen Filterliste.")
                            } else {
                                tr(
                                    "members.bulkTestOffConfirm",
                                    "Bei %d Mitgliedern die Testmitgliedschaft entfernen?",
                                ).format(targets.size)
                            },
                        )
                    },
                    confirmButton = {
                        TextButton(
                            onClick = {
                                pendingBulkTestOff = false
                                if (targets.isEmpty()) return@TextButton
                                dependencies.applicationScope.launch {
                                    var failed = 0
                                    for (m in targets) {
                                        val r = dependencies.membersManager.quickUpdateTestMembership(clubId, m.id)
                                        if (r.success != true) failed++
                                    }
                                    dependencies.membersManager.loadMembers(clubId)
                                    membersActionNote = if (failed == 0) {
                                        trStr("members.bulkTestOffDone", "Testmitgliedschaften aktualisiert.")
                                    } else {
                                        trStr("members.bulkTestOffPartial", "Teilweise fehlgeschlagen (%d).").replace("%d", failed.toString())
                                    }
                                }
                            },
                        ) { Text(tr("mobile.ok", "OK")) }
                    },
                    dismissButton = {
                        TextButton(onClick = { pendingBulkTestOff = false }) {
                            Text(tr("mobile.cancel", "Abbrechen"))
                        }
                    },
                )
            }

            pendingDeactivateMember?.let { member ->
                StableAlertDialog(
                    onDismissRequest = { pendingDeactivateMember = null },
                    title = { Text(tr("members.deactivateMember", "Mitglied deaktivieren")) },
                    text = { Text(trStr("members.deactivateMemberConfirm", "%s deaktivieren?").replace("%s", member.fullName()).replace("{name}", member.fullName())) },
                    confirmButton = {
                        TextButton(
                            onClick = {
                                pendingDeactivateMember = null
                                dependencies.applicationScope.launch {
                                    val r = runCatching { dependencies.membersManager.quickDeactivateMember(clubId, member.id) }
                                    r.onSuccess { membersActionNote = it.message ?: trStr("members.memberDeactivated", "Mitglied deaktiviert.") }
                                        .onFailure { membersActionNote = it.message ?: trStr("members.errorDeactivatingMember", "Mitglied konnte nicht deaktiviert werden.") }
                                    dependencies.membersManager.loadMembers(clubId)
                                }
                            },
                        ) { Text(tr("mobile.ok", "OK")) }
                    },
                    dismissButton = { TextButton(onClick = { pendingDeactivateMember = null }) { Text(tr("mobile.cancel", "Abbrechen")) } },
                )
            }
            ordersForMember?.let { member ->
                StableAlertDialog(
                    onDismissRequest = { ordersForMember = null; ordersList = emptyList(); ordersError = null },
                    title = { Text(trStr("orders.memberTitle", "Bestellungen: {name}").replace("{name}", member.fullName())) },
                    text = {
                        Column(modifier = Modifier.fillMaxWidth().heightIn(max = 420.dp).verticalScroll(rememberScrollState())) {
                            when {
                                ordersLoading -> CircularProgressIndicator(modifier = Modifier.padding(24.dp).align(Alignment.CenterHorizontally))
                                ordersError != null -> Text(ordersError.orEmpty(), color = MaterialTheme.colors.error)
                                ordersList.isEmpty() -> Text(tr("orders.noOrdersMember", "Für dieses Mitglied gibt es noch keine Bestellungen."))
                                else -> ordersList.forEach { order ->
                                    Column(modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp)) {
                                        Text(order.item, fontWeight = FontWeight.SemiBold)
                                        Text(order.status, style = MaterialTheme.typography.caption)
                                        order.cost?.let { Text("${trStr("orders.cost", "Kosten")}: $it", style = MaterialTheme.typography.caption) }
                                        Divider(modifier = Modifier.padding(top = 6.dp))
                                    }
                                }
                            }
                        }
                    },
                    confirmButton = { TextButton(onClick = { ordersForMember = null; ordersList = emptyList() }) { Text(tr("common.close", "Schließen")) } },
                )
            }
            statsForMember?.let { member ->
                StableAlertDialog(
                    onDismissRequest = { statsForMember = null; statsList = emptyList(); statsLastParticipations = emptyList(); statsError = null },
                    title = { Text(tr("members.exercises", "Übungen") + ": " + member.fullName()) },
                    text = {
                        Column(modifier = Modifier.fillMaxWidth().heightIn(max = 460.dp).verticalScroll(rememberScrollState())) {
                            when {
                                statsLoading -> CircularProgressIndicator(modifier = Modifier.padding(24.dp).align(Alignment.CenterHorizontally))
                                statsError != null -> Text(statsError.orEmpty(), color = MaterialTheme.colors.error)
                                statsList.isEmpty() && statsLastParticipations.isEmpty() -> Text(trStr("memberActivities.noActivities", "Keine Übungen im gewählten Zeitraum gefunden."))
                                else -> {
                                    if (statsList.isNotEmpty()) {
                                        Text(tr("members.activityStats", "Aktivität (Statistik)"), fontWeight = FontWeight.SemiBold)
                                        statsList.take(20).forEach { row ->
                                            Text("${row.name ?: row.code ?: "?"} - ${row.count}x", style = MaterialTheme.typography.body2, modifier = Modifier.padding(top = 3.dp))
                                        }
                                    }
                                    if (statsLastParticipations.isNotEmpty()) {
                                        Spacer(modifier = Modifier.height(10.dp))
                                        Text(tr("members.lastParticipations", "Letzte Teilnahmen"), fontWeight = FontWeight.SemiBold)
                                        statsLastParticipations.forEach { part ->
                                            Text("${displayActivityDate(part.date)}  ${part.activityName ?: part.activityFullName ?: ""}", style = MaterialTheme.typography.body2, modifier = Modifier.padding(top = 3.dp))
                                        }
                                    }
                                }
                            }
                        }
                    },
                    confirmButton = { TextButton(onClick = { statsForMember = null; statsList = emptyList(); statsLastParticipations = emptyList() }) { Text(tr("common.close", "Schließen")) } },
                )
            }
            notesForMember?.let { member ->
                StableAlertDialog(
                    onDismissRequest = { notesForMember = null; notesList = emptyList(); notesDraft = ""; notesError = null },
                    title = { Text(tr("members.notes", "Notizen") + ": " + member.fullName()) },
                    text = {
                        Column(modifier = Modifier.fillMaxWidth().heightIn(max = 460.dp).verticalScroll(rememberScrollState())) {
                            notesError?.let { Text(it, color = MaterialTheme.colors.error, modifier = Modifier.padding(bottom = 6.dp)) }
                            if (notesLoading) {
                                CircularProgressIndicator(modifier = Modifier.padding(24.dp).align(Alignment.CenterHorizontally))
                            } else if (notesList.isEmpty()) {
                                Text(trStr("memberNotes.noNotes", "Keine Notizen vorhanden."), style = MaterialTheme.typography.caption)
                            } else {
                                notesList.forEach { note ->
                                    Row(modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp), verticalAlignment = Alignment.Top) {
                                        Text(note.content.orEmpty(), modifier = Modifier.weight(1f), style = MaterialTheme.typography.body2)
                                        if (canWriteMembers) {
                                            TextButton(
                                                onClick = {
                                                    notesLoading = true
                                                    dependencies.applicationScope.launch {
                                                        try {
                                                            notesList = dependencies.membersManager.deleteMemberNote(clubId, note.id)
                                                        } catch (t: Throwable) {
                                                            notesError = t.message
                                                        } finally {
                                                            notesLoading = false
                                                        }
                                                    }
                                                },
                                            ) { Text(tr("common.delete", "Löschen"), style = MaterialTheme.typography.caption) }
                                        }
                                    }
                                    Divider()
                                }
                            }
                            if (canWriteMembers) {
                                Spacer(modifier = Modifier.height(8.dp))
                                OutlinedTextField(
                                    value = notesDraft,
                                    onValueChange = { notesDraft = it },
                                    label = { Text(tr("mobile.newNote", "Neue Notiz")) },
                                    modifier = Modifier.fillMaxWidth(),
                                    enabled = !notesLoading,
                                )
                                TextButton(
                                    onClick = {
                                        val text = notesDraft.trim()
                                        if (text.isEmpty()) return@TextButton
                                        notesLoading = true
                                        dependencies.applicationScope.launch {
                                            try {
                                                notesList = dependencies.membersManager.addMemberNote(clubId, member.id, text)
                                                notesDraft = ""
                                            } catch (t: Throwable) {
                                                notesError = t.message
                                            } finally {
                                                notesLoading = false
                                            }
                                        }
                                    },
                                    enabled = notesDraft.isNotBlank() && !notesLoading,
                                ) { Text(tr("memberNotes.add", "Hinzufügen")) }
                            }
                        }
                    },
                    confirmButton = { TextButton(onClick = { notesForMember = null; notesList = emptyList(); notesDraft = "" }) { Text(tr("common.close", "Schließen")) } },
                )
            }

            val membersBrowseScroll = rememberScrollState()
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .verticalScroll(membersBrowseScroll)
                    .imePadding()
                    .navigationBarsPadding()
                    .padding(horizontal = ScreenHorizontalPadding, vertical = 16.dp),
            ) {
                Header(tr("members.title", "Mitglieder"))
                if (membersState.fromOfflineCache) {
                    Text(
                        trStr("mobile.offlineCacheHint", "Keine Verbindung – zuletzt geladene Daten."),
                        style = MaterialTheme.typography.caption,
                        color = MaterialTheme.colors.onSurface.copy(alpha = 0.65f),
                        modifier = Modifier.padding(bottom = 8.dp),
                    )
                }
                Row(
                    modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp),
                    horizontalArrangement = Arrangement.spacedBy(8.dp),
                ) {
                    Card(modifier = Modifier.weight(1f), elevation = 0.dp, backgroundColor = MaterialTheme.colors.onSurface.copy(alpha = 0.03f)) {
                        Column(Modifier.padding(8.dp)) {
                            Text(tr("members.scopeActive", "Aktive"), style = MaterialTheme.typography.caption)
                            Text(activeRegularCount.toString(), fontWeight = FontWeight.Bold)
                        }
                    }
                    Card(modifier = Modifier.weight(1f), elevation = 0.dp, backgroundColor = MaterialTheme.colors.onSurface.copy(alpha = 0.03f)) {
                        Column(Modifier.padding(8.dp)) {
                            Text(tr("mobile.inactive", "Inaktiv"), style = MaterialTheme.typography.caption)
                            Text(inactiveCount.toString(), fontWeight = FontWeight.Bold)
                        }
                    }
                    Card(modifier = Modifier.weight(1f), elevation = 0.dp, backgroundColor = MaterialTheme.colors.onSurface.copy(alpha = 0.03f)) {
                        Column(Modifier.padding(8.dp)) {
                            Text(tr("members.testMember", "Test (aktiv)"), style = MaterialTheme.typography.caption)
                            Text(testActiveCount.toString(), fontWeight = FontWeight.Bold)
                        }
                    }
                }
                MembersTogglePanel(
                    title = tr("members.actions", "Aktionen"),
                    expanded = memberActionsPanelExpanded,
                    onToggle = { memberActionsPanelExpanded = !memberActionsPanelExpanded },
                ) {
                    Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) {
                    OutlinedButton(
                        onClick = { dependencies.applicationScope.launch { dependencies.membersManager.loadMembers(clubId) } },
                        modifier = Modifier
                            .weight(1f)
                            .heightIn(min = TouchMinHeight),
                    ) { Text(tr("mobile.refresh", "Aktualisieren")) }
                    if (canWriteMembers) {
                        Button(
                            onClick = { stack = MembersStackRoute.Edit(null) },
                            modifier = Modifier
                                .weight(1f)
                                .heightIn(min = TouchMinHeight),
                        ) { Text(tr("members.newMember", "Neu")) }
                    }
                }
                if (canWriteMyTt) {
                    Row(modifier = Modifier.fillMaxWidth().padding(top = 6.dp), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                        OutlinedButton(
                            onClick = {
                                dependencies.applicationScope.launch {
                                    ratingsBusy = true
                                    membersActionNote = null
                                    runCatching {
                                        dependencies.membersManager.updateRatingsFromMyTischtennis(clubId)
                                        dependencies.membersManager.loadMembers(clubId)
                                    }.onSuccess {
                                        membersActionNote = trStr("members.ratingsUpdated", "Ratings aktualisiert.")
                                    }.onFailure {
                                        membersActionNote = it.message ?: trStr("members.ratingsUpdateFailed", "Ratings-Update fehlgeschlagen.")
                                    }
                                    ratingsBusy = false
                                }
                            },
                            enabled = !ratingsBusy,
                            modifier = Modifier
                                .weight(1f)
                                .heightIn(min = TouchMinHeight),
                        ) {
                            Text(
                                if (ratingsBusy) tr("mobile.loading", "Laden…") else tr("members.updateRatings", "Ratings aktualisieren"),
                            )
                        }
                    }
                }
                Row(
                    modifier = Modifier.fillMaxWidth().horizontalScroll(rememberScrollState()).padding(top = 4.dp, bottom = 4.dp),
                    horizontalArrangement = Arrangement.spacedBy(6.dp),
                ) {
                    OutlinedButton(
                        onClick = {
                            val list = membersState.members.filter { it.active && it.testMembership != true }
                            if (list.isEmpty()) {
                                membersActionNote = trStr("members.phoneListEmpty", "Keine aktiven regulären Mitglieder für die Telefonliste.")
                            } else {
                                dependencies.applicationScope.launch {
                                    runCatching {
                                        val f = File(androidCtx.cacheDir, "telefonliste-alle.pdf")
                                        writeMembersPhoneListPdf(
                                            f,
                                            "$clubName – ${trStr("pdfGenerator.phoneList", "Telefonliste")}",
                                            list,
                                        )
                                        sharePdfFile(androidCtx, f, trStr("members.sharePhoneList", "Telefonliste teilen"))
                                    }.onFailure {
                                        membersActionNote = it.message
                                            ?: trStr("members.phoneListError", "PDF konnte nicht erstellt werden.")
                                    }
                                }
                            }
                        },
                        modifier = Modifier.heightIn(min = 34.dp),
                    ) { Text(tr("members.generatePhoneList", "Telefonliste PDF"), style = MaterialTheme.typography.caption) }
                    OutlinedButton(
                        onClick = {
                            val list = filteredMembers.filter { it.active && it.testMembership != true }
                            if (list.isEmpty()) {
                                membersActionNote = trStr("members.phoneListEmpty", "Keine aktiven regulären Mitglieder für die Telefonliste.")
                            } else {
                                dependencies.applicationScope.launch {
                                    runCatching {
                                        val f = File(androidCtx.cacheDir, "telefonliste-filter.pdf")
                                        writeMembersPhoneListPdf(
                                            f,
                                            "$clubName – ${trStr("members.phoneListSelectionFile", "Telefonliste (Auswahl)")}",
                                            list,
                                        )
                                        sharePdfFile(androidCtx, f, trStr("members.sharePhoneList", "Telefonliste teilen"))
                                    }.onFailure {
                                        membersActionNote = it.message
                                            ?: trStr("members.phoneListError", "PDF konnte nicht erstellt werden.")
                                    }
                                }
                            }
                        },
                        modifier = Modifier.heightIn(min = 34.dp),
                    ) { Text(tr("members.phoneListFiltered", "Telefonliste (Filter)"), style = MaterialTheme.typography.caption) }
                    OutlinedButton(
                        onClick = { stack = MembersStackRoute.GroupPhoto },
                        modifier = Modifier.heightIn(min = 34.dp),
                    ) { Text(tr("members.groupPhotoCrop", "Gruppenfoto"), style = MaterialTheme.typography.caption) }
                    if (canWriteMembers) {
                        OutlinedButton(
                            onClick = { stack = MembersStackRoute.GroupPhotoPortraitCrop() },
                            modifier = Modifier.heightIn(min = 34.dp),
                        ) { Text(tr("members.portraitCropShort", "Portrait-Zuschnitt"), style = MaterialTheme.typography.caption) }
                        OutlinedButton(
                            onClick = { stack = MembersStackRoute.TransferRun },
                            modifier = Modifier.heightIn(min = 34.dp),
                        ) { Text(tr("members.transferMembers", "Transfer"), style = MaterialTheme.typography.caption) }
                    }
                }
                }
                MembersTogglePanel(
                    title = tr("members.filterSearchSort", "Filter, Suche & Sortierung"),
                    expanded = memberFiltersPanelExpanded,
                    onToggle = { memberFiltersPanelExpanded = !memberFiltersPanelExpanded },
                ) {
                    OutlinedTextField(
                    value = query,
                    onValueChange = { query = it },
                    label = { Text(tr("mobile.search", "Suche")) },
                    modifier = Modifier.fillMaxWidth().padding(top = 10.dp, bottom = 6.dp),
                    singleLine = true,
                )
                Row(
                    modifier = Modifier.fillMaxWidth().horizontalScroll(rememberScrollState()).padding(bottom = 6.dp),
                    verticalAlignment = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.spacedBy(8.dp),
                ) {
                    Text(tr("members.showInactive", "Inaktive anzeigen"), style = MaterialTheme.typography.caption)
                    Switch(checked = showInactiveMembers, onCheckedChange = { showInactiveMembers = it })
                }
                Text(tr("members.scopeLabel", "Ansicht"), style = MaterialTheme.typography.caption, modifier = Modifier.padding(bottom = 4.dp))
                Row(
                    modifier = Modifier.fillMaxWidth().horizontalScroll(rememberScrollState()).padding(bottom = 8.dp),
                    horizontalArrangement = Arrangement.spacedBy(6.dp),
                ) {
                    listOf(
                        "all" to tr("members.scopeAll", "Alle"),
                        "active" to tr("members.scopeActive", "Aktive o. Test"),
                        "test" to tr("members.scopeTest", "Test"),
                        "activeTest" to tr("members.scopeActiveTest", "Aktive Test"),
                        "notTraining" to tr("members.scopeNotTraining", "Nicht im Training"),
                        "needsForm" to tr("members.scopeNeedsForm", "Formular fehlt"),
                        "activeDataIncomplete" to tr("members.scopeActiveDataIncomplete", "Aktive Daten lückenhaft"),
                        "dataIncomplete" to tr("members.scopeDataIncomplete", "Daten lückenhaft"),
                        "inactive" to tr("members.scopeInactive", "Inaktive"),
                    ).forEach { (key, label) ->
                        val c = scopeOptionCount(key)
                        OutlinedButton(onClick = { memberScope = key }, modifier = Modifier.heightIn(min = 34.dp)) {
                            Text(
                                (if (memberScope == key) "✓ " else "") + "$label ($c)",
                                style = MaterialTheme.typography.caption,
                            )
                        }
                    }
                }
                Row(
                    modifier = Modifier.fillMaxWidth().padding(bottom = 6.dp),
                    horizontalArrangement = Arrangement.spacedBy(8.dp),
                    verticalAlignment = Alignment.CenterVertically,
                ) {
                    Box {
                        OutlinedButton(onClick = { groupFilterMenuOpen = true }, modifier = Modifier.heightIn(min = 36.dp)) {
                            Text(
                                selectedTrainingGroupId?.let { id -> trainingGroupFilterOptions.find { it.key == id }?.value }
                                    ?: tr("members.allTrainingGroups", "Alle Trainingsgruppen"),
                                style = MaterialTheme.typography.caption,
                                maxLines = 1,
                                overflow = TextOverflow.Ellipsis,
                            )
                        }
                        DropdownMenu(expanded = groupFilterMenuOpen, onDismissRequest = { groupFilterMenuOpen = false }) {
                            DropdownMenuItem(onClick = {
                                selectedTrainingGroupId = null
                                groupFilterMenuOpen = false
                            }) {
                                Text(tr("common.all", "Alle"))
                            }
                            trainingGroupFilterOptions.forEach { (id, name) ->
                                DropdownMenuItem(onClick = {
                                    selectedTrainingGroupId = id
                                    groupFilterMenuOpen = false
                                }) {
                                    Text(name)
                                }
                            }
                        }
                    }
                }
                Row(
                    modifier = Modifier.fillMaxWidth().horizontalScroll(rememberScrollState()).padding(bottom = 8.dp),
                    horizontalArrangement = Arrangement.spacedBy(6.dp),
                ) {
                    Text(tr("members.lastTrainingFilter", "Letztes Training"), style = MaterialTheme.typography.caption, modifier = Modifier.padding(end = 4.dp))
                    listOf(
                        "all" to tr("common.all", "Alle"),
                        "hasDate" to tr("members.lastTrainingHasDate", "Mit Datum"),
                        "noDate" to tr("members.lastTrainingNoDate", "Ohne Datum"),
                        "notInTraining" to tr("members.scopeNotTraining", "Nicht im Training"),
                    ).forEach { (key, label) ->
                        OutlinedButton(onClick = { lastTrainingFilter = key }, modifier = Modifier.heightIn(min = 34.dp)) {
                            Text(
                                if (lastTrainingFilter == key) "✓ $label" else label,
                                style = MaterialTheme.typography.caption,
                            )
                        }
                    }
                }
                Text(tr("members.ttStichtagHint", "Altersklassen nach Stichtag 01.01., Saison Aug–Jul."), style = MaterialTheme.typography.caption, color = MaterialTheme.colors.onSurface.copy(alpha = 0.65f), modifier = Modifier.padding(bottom = 4.dp))
                Row(
                    modifier = Modifier.fillMaxWidth().horizontalScroll(rememberScrollState()).padding(bottom = 6.dp),
                    horizontalArrangement = Arrangement.spacedBy(8.dp),
                    verticalAlignment = Alignment.CenterVertically,
                ) {
                    Box {
                        OutlinedButton(onClick = { seasonMenuOpen = true }, modifier = Modifier.heightIn(min = 36.dp)) {
                            Text(
                                "${tr("members.ttSeasonFilter", "Saison")}: ${formatSeasonSlash(seasonStartYear)}",
                                style = MaterialTheme.typography.caption,
                                maxLines = 1,
                                overflow = TextOverflow.Ellipsis,
                            )
                        }
                        DropdownMenu(expanded = seasonMenuOpen, onDismissRequest = { seasonMenuOpen = false }) {
                            val y0 = getSeasonStartYearFromDateToday()
                            listOf(y0, y0 + 1).forEach { y ->
                                DropdownMenuItem(onClick = {
                                    seasonStartYear = y
                                    seasonMenuOpen = false
                                }) {
                                    Text(formatSeasonSlash(y))
                                }
                            }
                        }
                    }
                    Box {
                        OutlinedButton(onClick = { ageGroupMenuOpen = true }, modifier = Modifier.heightIn(min = 36.dp)) {
                            Text(
                                when (selectedAgeGroup.trim()) {
                                    "" -> tr("members.ageGroup", "Altersklasse") + ": " + tr("common.all", "Alle")
                                    "adult" -> tr("members.ttAdult", "Erwachsene")
                                    "range" -> tr("members.ageRange", "Altersbereich")
                                    else -> selectedAgeGroup.trim()
                                },
                                style = MaterialTheme.typography.caption,
                                maxLines = 1,
                                overflow = TextOverflow.Ellipsis,
                            )
                        }
                        DropdownMenu(expanded = ageGroupMenuOpen, onDismissRequest = { ageGroupMenuOpen = false }) {
                            DropdownMenuItem(onClick = { selectedAgeGroup = ""; ageGroupMenuOpen = false }) {
                                Text(tr("common.all", "Alle"))
                            }
                            DropdownMenuItem(onClick = { selectedAgeGroup = "adult"; ageGroupMenuOpen = false }) {
                                Text(tr("members.ttAdult", "Erwachsene"))
                            }
                            listOf("J19", "J15", "J13", "J11", "J9").forEach { k ->
                                DropdownMenuItem(onClick = { selectedAgeGroup = k; ageGroupMenuOpen = false }) {
                                    Text(k)
                                }
                            }
                            listOf("M19", "M15", "M13", "M11", "M9").forEach { k ->
                                DropdownMenuItem(onClick = { selectedAgeGroup = k; ageGroupMenuOpen = false }) {
                                    Text(k)
                                }
                            }
                            DropdownMenuItem(onClick = { selectedAgeGroup = "range"; ageGroupMenuOpen = false }) {
                                Text(tr("members.ageRange", "Altersbereich"))
                            }
                        }
                    }
                    Box {
                        OutlinedButton(onClick = { genderMenuOpen = true }, modifier = Modifier.heightIn(min = 36.dp)) {
                            Text(
                                when (selectedGender.trim()) {
                                    "" -> tr("members.gender", "Geschlecht") + ": " + tr("common.all", "Alle")
                                    "female" -> tr("members.genderFemale", "weiblich")
                                    "male" -> tr("members.genderMale", "männlich")
                                    "diverse" -> tr("members.genderDiverse", "divers")
                                    "unknown" -> tr("members.genderUnknown", "unbekannt")
                                    else -> selectedGender
                                },
                                style = MaterialTheme.typography.caption,
                                maxLines = 1,
                                overflow = TextOverflow.Ellipsis,
                            )
                        }
                        DropdownMenu(expanded = genderMenuOpen, onDismissRequest = { genderMenuOpen = false }) {
                            DropdownMenuItem(onClick = { selectedGender = ""; genderMenuOpen = false }) {
                                Text(tr("common.all", "Alle"))
                            }
                            DropdownMenuItem(onClick = { selectedGender = "female"; genderMenuOpen = false }) {
                                Text(tr("members.genderFemale", "weiblich"))
                            }
                            DropdownMenuItem(onClick = { selectedGender = "male"; genderMenuOpen = false }) {
                                Text(tr("members.genderMale", "männlich"))
                            }
                            DropdownMenuItem(onClick = { selectedGender = "diverse"; genderMenuOpen = false }) {
                                Text(tr("members.genderDiverse", "divers"))
                            }
                            DropdownMenuItem(onClick = { selectedGender = "unknown"; genderMenuOpen = false }) {
                                Text(tr("members.genderUnknown", "unbekannt"))
                            }
                        }
                    }
                }
                if (selectedAgeGroup.trim() == "range") {
                    Row(
                        modifier = Modifier.fillMaxWidth().horizontalScroll(rememberScrollState()).padding(bottom = 8.dp),
                        horizontalArrangement = Arrangement.spacedBy(8.dp),
                        verticalAlignment = Alignment.CenterVertically,
                    ) {
                        OutlinedTextField(
                            value = selectedAgeFrom,
                            onValueChange = { selectedAgeFrom = it.filter { ch -> ch.isDigit() }.take(3) },
                            label = { Text(tr("members.ageFromPlaceholder", "Alter von")) },
                            modifier = Modifier.width(120.dp),
                            singleLine = true,
                        )
                        Text("–", style = MaterialTheme.typography.body2)
                        OutlinedTextField(
                            value = selectedAgeTo,
                            onValueChange = { selectedAgeTo = it.filter { ch -> ch.isDigit() }.take(3) },
                            label = { Text(tr("members.ageToPlaceholder", "Alter bis")) },
                            modifier = Modifier.width(120.dp),
                            singleLine = true,
                        )
                    }
                }
                Row(
                    modifier = Modifier.fillMaxWidth().horizontalScroll(rememberScrollState()).padding(bottom = 8.dp),
                    horizontalArrangement = Arrangement.spacedBy(6.dp),
                ) {
                    listOf(
                        "name" to tr("members.name", "Name"),
                        "firstName" to tr("members.sortFirstName", "Vorname"),
                        "ttr" to "TTR",
                        "qttr" to tr("members.sortQttr", "QTTR"),
                        "birthDate" to tr("members.birthDate", "Geburtsdatum"),
                        "age" to tr("members.sortAge", "Alter"),
                        "lastTraining" to tr("mobile.lastTraining", "Letztes Training"),
                        "openTasks" to tr("members.sortOpenTasks", "Aufgaben"),
                    ).forEach { (key, label) ->
                        OutlinedButton(onClick = { selectedSort = key }, modifier = Modifier.heightIn(min = 34.dp)) {
                            Text(if (selectedSort == key) "✓ $label" else label, style = MaterialTheme.typography.caption)
                        }
                    }
                    OutlinedButton(onClick = { sortAscending = !sortAscending }, modifier = Modifier.heightIn(min = 34.dp)) {
                        Text(
                            if (sortAscending) tr("members.sortAsc", "↑ Aufsteigend") else tr("members.sortDesc", "↓ Absteigend"),
                            style = MaterialTheme.typography.caption,
                        )
                    }
                    OutlinedButton(onClick = { showTrainingParticipationsColumn = !showTrainingParticipationsColumn }, modifier = Modifier.heightIn(min = 34.dp)) {
                        Text(
                            if (showTrainingParticipationsColumn) "✓ ${tr("members.trainingParticipations", "Trainings-Teilnahmen")}"
                            else tr("members.trainingParticipations", "Trainings-Teilnahmen"),
                            style = MaterialTheme.typography.caption,
                        )
                    }
                    OutlinedButton(
                        onClick = {
                            query = ""
                            memberScope = "active"
                            showInactiveMembers = false
                            selectedTrainingGroupId = null
                            lastTrainingFilter = "all"
                            seasonStartYear = getSeasonStartYearFromDateToday()
                            selectedAgeGroup = ""
                            selectedAgeFrom = ""
                            selectedAgeTo = ""
                            selectedGender = ""
                            selectedSort = "name"
                            sortAscending = true
                            showTrainingParticipationsColumn = false
                            membersActionNote = null
                        },
                        modifier = Modifier.heightIn(min = 34.dp),
                    ) {
                        Text(tr("members.resetFilters", "Filter zurücksetzen"), style = MaterialTheme.typography.caption)
                    }
                }
                Row(
                    modifier = Modifier.fillMaxWidth().horizontalScroll(rememberScrollState()).padding(bottom = 8.dp),
                    horizontalArrangement = Arrangement.spacedBy(6.dp),
                ) {
                    OutlinedButton(
                        onClick = { copyToClipboard(filteredPhonesPlain.joinToString("; ")) },
                        enabled = filteredPhonesPlain.isNotEmpty(),
                        modifier = Modifier.heightIn(min = 34.dp),
                    ) { Text(tr("members.copyFilteredPhones", "Telefone kopieren"), style = MaterialTheme.typography.caption) }
                    OutlinedButton(
                        onClick = {
                            val block = filteredMembers.mapNotNull { m ->
                                val p = formatMemberPhonesLine(m)
                                if (p == MEMBER_CONTACT_EMPTY) null else "${m.fullName()}: $p"
                            }.joinToString("\n")
                            copyToClipboard(block)
                        },
                        enabled = filteredMembers.any { formatMemberPhonesLine(it) != MEMBER_CONTACT_EMPTY },
                        modifier = Modifier.heightIn(min = 34.dp),
                    ) { Text(tr("members.copyPhonesWithNames", "Telefone mit Namen"), style = MaterialTheme.typography.caption) }
                    OutlinedButton(
                        onClick = { copyToClipboard(filteredEmailsPlain.joinToString("; ")) },
                        enabled = filteredEmailsPlain.isNotEmpty(),
                        modifier = Modifier.heightIn(min = 34.dp),
                    ) { Text(tr("members.copyFilteredEmails", "E-Mails kopieren"), style = MaterialTheme.typography.caption) }
                    OutlinedButton(
                        onClick = {
                            val block = filteredMembers.mapNotNull { m ->
                                val emails = extractEmailAddressesFromMember(m)
                                if (emails.isEmpty()) null else "${m.fullName()}: ${emails.joinToString(", ")}"
                            }.joinToString("\n")
                            copyToClipboard(block)
                        },
                        enabled = filteredMembers.any { extractEmailAddressesFromMember(it).isNotEmpty() },
                        modifier = Modifier.heightIn(min = 34.dp),
                    ) { Text(tr("members.copyEmailsWithNames", "E-Mails mit Namen"), style = MaterialTheme.typography.caption) }
                    OutlinedButton(
                        onClick = {
                            copyToClipboard(buildMembersCsvExport(filteredMembers))
                            membersActionNote = trStr("members.csvCopied", "CSV in die Zwischenablage kopiert.")
                        },
                        enabled = filteredMembers.isNotEmpty(),
                        modifier = Modifier.heightIn(min = 34.dp),
                    ) { Text(tr("members.copyCsv", "CSV kopieren"), style = MaterialTheme.typography.caption) }
                    OutlinedButton(
                        onClick = {
                            if (filteredEmailsForBcc.isEmpty()) return@OutlinedButton
                            runCatching {
                                val bcc = filteredEmailsForBcc.joinToString(",")
                                val uri = Uri.parse("mailto:?bcc=${Uri.encode(bcc)}")
                                androidCtx.startActivity(Intent(Intent.ACTION_VIEW, uri))
                            }
                        },
                        enabled = filteredEmailsForBcc.isNotEmpty(),
                        modifier = Modifier.heightIn(min = 34.dp),
                    ) { Text(tr("members.openMailtoBcc", "E-Mail (Bcc)"), style = MaterialTheme.typography.caption) }
                }
                if (canWriteMembers) {
                    Row(
                        modifier = Modifier.fillMaxWidth().horizontalScroll(rememberScrollState()).padding(bottom = 8.dp),
                        horizontalArrangement = Arrangement.spacedBy(6.dp),
                    ) {
                        OutlinedButton(
                            onClick = { pendingBulkForms = true },
                            modifier = Modifier.heightIn(min = 34.dp),
                        ) { Text(tr("members.bulkHandOverForms", "Formulare übergeben"), style = MaterialTheme.typography.caption) }
                        OutlinedButton(
                            onClick = { pendingBulkTestOff = true },
                            modifier = Modifier.heightIn(min = 34.dp),
                        ) { Text(tr("members.bulkEndTest", "Test beenden (Liste)"), style = MaterialTheme.typography.caption) }
                    }
                }
                }
                membersActionNote?.let { note ->
                    Text(note, style = MaterialTheme.typography.caption, color = MaterialTheme.colors.primary, modifier = Modifier.padding(bottom = 6.dp))
                }
                if (membersState.isLoading) LoadingInline()
                ErrorText(membersState.error)
                if (!membersState.isLoading && filteredMembers.isEmpty()) {
                    EmptyText(if (query.isBlank()) tr("mobile.noMembers", "Keine Mitglieder gefunden") else tr("mobile.noResults", "Keine Treffer"))
                }
                val tableScroll = rememberScrollState()
                val tableWidth = if (showTrainingParticipationsColumn) 1180.dp else 1080.dp
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .horizontalScroll(tableScroll),
                ) {
                    Column(modifier = Modifier.width(tableWidth)) {
                        Row(
                            modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp),
                            verticalAlignment = Alignment.CenterVertically,
                        ) {
                            Text(tr("members.name", "Name"), modifier = Modifier.width(220.dp), fontWeight = FontWeight.Bold, style = MaterialTheme.typography.caption)
                            Text(tr("members.status", "Status"), modifier = Modifier.width(92.dp), fontWeight = FontWeight.Bold, style = MaterialTheme.typography.caption)
                            Text("TTR/QTTR", modifier = Modifier.width(110.dp), fontWeight = FontWeight.Bold, style = MaterialTheme.typography.caption)
                            Text(tr("members.contact", "Kontakt"), modifier = Modifier.width(220.dp), fontWeight = FontWeight.Bold, style = MaterialTheme.typography.caption)
                            Text(tr("members.birthDate", "Geburtsdatum"), modifier = Modifier.width(160.dp), fontWeight = FontWeight.Bold, style = MaterialTheme.typography.caption)
                            if (showTrainingParticipationsColumn) {
                                Text(tr("members.trainingParticipations", "Trainings-Teilnahmen"), modifier = Modifier.width(100.dp), fontWeight = FontWeight.Bold, style = MaterialTheme.typography.caption)
                            }
                            Text(tr("members.actions", "Aktionen"), modifier = Modifier.width(270.dp), fontWeight = FontWeight.Bold, style = MaterialTheme.typography.caption)
                        }
                        Divider()
                        Column(modifier = Modifier.fillMaxWidth()) {
                            for (member in filteredMembers) {
                                key(member.id) {
                                    Row(
                                        modifier = Modifier
                                            .fillMaxWidth()
                                            .clickable { stack = MembersStackRoute.Detail(member.id) }
                                            .padding(vertical = 8.dp),
                                        verticalAlignment = Alignment.CenterVertically,
                                    ) {
                                        Row(modifier = Modifier.width(220.dp), verticalAlignment = Alignment.CenterVertically) {
                                            val avatarUrl = memberProfileImagePath(clubId, member.id)
                                                ?.let { dependencies.apiConfig.toAbsoluteUrl(it) }
                                            if (canReadMembers && avatarUrl != null) {
                                                AuthenticatedAsyncImage(
                                                    imageUrl = avatarUrl,
                                                    authHeaders = dependencies.diaryAuthHeaders(),
                                                    modifier = Modifier.size(28.dp),
                                                    contentDescription = member.fullName(),
                                                )
                                                Spacer(modifier = Modifier.width(8.dp))
                                            }
                                            Text(member.fullName(), maxLines = 1, overflow = TextOverflow.Ellipsis, fontWeight = FontWeight.SemiBold)
                                        }
                                        val status = when {
                                            !member.active -> tr("mobile.inactive", "Inaktiv")
                                            member.testMembership == true -> tr("members.testMember", "Test")
                                            else -> tr("mobile.active", "Aktiv")
                                        }
                                        Text(status, modifier = Modifier.width(92.dp), style = MaterialTheme.typography.caption)
                                        Text(
                                            listOfNotNull(member.ttr?.let { "TTR $it" }, member.qttr?.let { "QTTR $it" }).joinToString(" · ").ifBlank { "-" },
                                            modifier = Modifier.width(110.dp),
                                            style = MaterialTheme.typography.caption,
                                        )
                                        val phoneLine = formatMemberPhonesLine(member)
                                        val emailLine = formatMemberEmailsLine(member)
                                        val contactLine = listOf(phoneLine, emailLine)
                                            .filter { it != MEMBER_CONTACT_EMPTY }
                                            .joinToString(" · ")
                                            .ifBlank { "-" }
                                        Text(contactLine, modifier = Modifier.width(220.dp), maxLines = 2, overflow = TextOverflow.Ellipsis, style = MaterialTheme.typography.caption)
                                        Text(formatBirthAndAge(member), modifier = Modifier.width(160.dp), style = MaterialTheme.typography.caption)
                                        if (showTrainingParticipationsColumn) {
                                            Text((member.trainingParticipations ?: 0).toString(), modifier = Modifier.width(100.dp), style = MaterialTheme.typography.caption)
                                        }
                                        Row(modifier = Modifier.width(270.dp), horizontalArrangement = Arrangement.spacedBy(2.dp), verticalAlignment = Alignment.CenterVertically) {
                                            if (member.memberFormHandedOver != true) {
                                                IconButton(
                                                    onClick = { quickMarkForm(member) },
                                                    enabled = canWriteMembers,
                                                    modifier = Modifier.size(36.dp).semantics { contentDescription = trStr("members.markFormReceived", "Formular erhalten") },
                                                ) { Text("✓", color = Color(0xFF2E7D32), fontWeight = FontWeight.Bold) }
                                            }
                                            if (member.active && member.ttr == null && member.qttr == null && member.clickTtApplicationSubmitted != true) {
                                                IconButton(
                                                    onClick = { requestClickTt(member) },
                                                    enabled = canWriteMembers && !clickTtPendingMemberIds.contains(member.id),
                                                    modifier = Modifier.size(36.dp).semantics { contentDescription = trStr("members.clickTtRequestAction", "Click-TT-Antrag") },
                                                ) { Text(if (clickTtPendingMemberIds.contains(member.id)) "…" else "🏓", fontSize = 16.sp) }
                                            }
                                            IconButton(
                                                onClick = { loadMemberOrders(member) },
                                                modifier = Modifier.size(36.dp).semantics { contentDescription = trStr("orders.title", "Bestellungen") },
                                            ) { Text("📦", fontSize = 16.sp) }
                                            IconButton(
                                                onClick = { stack = MembersStackRoute.Edit(member.id) },
                                                enabled = canWriteMembers,
                                                modifier = Modifier.size(36.dp).semantics { contentDescription = trStr("members.editMember", "Bearbeiten") },
                                            ) { Text("✎", fontSize = 16.sp) }
                                            IconButton(
                                                onClick = { loadMemberNotes(member) },
                                                modifier = Modifier.size(36.dp).semantics { contentDescription = trStr("members.notes", "Notizen") },
                                            ) { Text("📝", fontSize = 16.sp) }
                                            IconButton(
                                                onClick = { loadMemberStats(member) },
                                                modifier = Modifier.size(36.dp).semantics { contentDescription = trStr("members.exercises", "Übungen") },
                                            ) { Text("🏃", fontSize = 16.sp) }
                                            IconButton(
                                                onClick = { stack = MembersStackRoute.Detail(member.id) },
                                                modifier = Modifier.size(36.dp).semantics { contentDescription = trStr("mobile.detail", "Details") },
                                            ) { Icon(Icons.Filled.Visibility, contentDescription = null, modifier = Modifier.size(18.dp)) }
                                            if (member.active) {
                                                IconButton(
                                                    onClick = { pendingDeactivateMember = member },
                                                    enabled = canWriteMembers,
                                                    modifier = Modifier.size(36.dp).semantics { contentDescription = trStr("members.deactivateMember", "Mitglied deaktivieren") },
                                                ) { Text("⊖", color = MaterialTheme.colors.error, fontWeight = FontWeight.Bold) }
                                            }
                                        }
                                    }
                                    Divider()
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

@Composable
private fun MemberDetailRoute(
    clubId: Int,
    member: Member,
    dependencies: AppDependencies,
    canWriteMembers: Boolean,
    onBack: () -> Unit,
    onEdit: () -> Unit,
) {
    BackHandler(onBack = onBack)
    val scroll = rememberScrollState()
    val narrowPhone = LocalConfiguration.current.screenWidthDp < MAIN_NAV_RAIL_MIN_WIDTH_DP
    val androidContext = LocalContext.current
    var activityPeriod by rememberSaveable { mutableStateOf("year") }
    var activityStats by remember { mutableStateOf<List<MemberActivityStatDto>>(emptyList()) }
    var lastParts by remember { mutableStateOf<List<MemberLastParticipationDto>>(emptyList()) }
    var memberGroups by remember { mutableStateOf<List<TrainingGroupDto>>(emptyList()) }
    var allGroups by remember { mutableStateOf<List<TrainingGroupDto>>(emptyList()) }
    var schedule by remember { mutableStateOf<List<TrainingGroupDto>>(emptyList()) }
    var detailBusy by remember { mutableStateOf(false) }
    var detailError by remember { mutableStateOf<String?>(null) }
    var groupMenu by remember { mutableStateOf(false) }

    LaunchedEffect(member.id, clubId, activityPeriod) {
        detailBusy = true
        detailError = null
        try {
            coroutineScope {
                val a = async { runCatching { dependencies.membersManager.memberActivityStats(clubId, member.id, activityPeriod) }.getOrDefault(emptyList()) }
                val b = async { runCatching { dependencies.membersManager.memberLastParticipations(clubId, member.id) }.getOrDefault(emptyList()) }
                val c = async { runCatching { dependencies.membersManager.listMemberTrainingGroups(clubId, member.id) }.getOrDefault(emptyList()) }
                val d = async { runCatching { dependencies.membersManager.listTrainingGroups(clubId) }.getOrDefault(emptyList()) }
                val e = async { runCatching { dependencies.membersManager.trainingScheduleGroups(clubId) }.getOrDefault(emptyList()) }
                activityStats = a.await()
                lastParts = b.await()
                memberGroups = c.await()
                allGroups = d.await()
                schedule = e.await()
            }
        } catch (t: Throwable) {
            detailError = t.message
        } finally {
            detailBusy = false
        }
    }

    val cropPortrait = rememberMemberPortraitCropLauncher(
        onCropped = { bytes ->
            dependencies.applicationScope.launch {
                runCatching {
                    dependencies.membersManager.uploadMemberPortrait(clubId, member.id, bytes)
                    dependencies.membersManager.loadMembers(clubId)
                }
            }
        },
    )

    val pickPortrait = rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
        if (uri == null || !canWriteMembers) return@rememberLauncherForActivityResult
        runCatching {
            val sourceForCrop = copyPickedImageToCacheForCrop(androidContext, uri, member.id)
            val (_, destUri) = createCropOutputUri(androidContext, member.id)
            cropPortrait.launch(buildMemberPortraitCropIntent(androidContext, sourceForCrop, destUri))
        }
    }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .imePadding()
            .navigationBarsPadding()
            .padding(horizontal = ScreenHorizontalPadding, vertical = 12.dp)
            .verticalScroll(scroll),
    ) {
        ScreenTopBar(
            title = member.fullName(),
            onBack = onBack,
            hint = if (narrowPhone) {
                tr("mobile.detailNavHint", "Zurück: Pfeil oben links oder die System-Taste „Zurück“.")
            } else {
                null
            },
        )
        Spacer(modifier = Modifier.height(8.dp))
        if (canWriteMembers) {
            Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) {
                Button(onClick = onEdit, modifier = Modifier.weight(1f).heightIn(min = TouchMinHeight)) {
                    Text(tr("members.editMember", "Bearbeiten"))
                }
                OutlinedButton(
                    onClick = {
                        pickPortrait.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
                    },
                    modifier = Modifier.weight(1f).heightIn(min = TouchMinHeight),
                ) { Text(tr("members.cropAndUploadPhoto", "Foto wählen & zuschneiden")) }
            }
            Spacer(modifier = Modifier.height(8.dp))
        }
        val memberImageUrls = remember(member.id, member.images, member.primaryImageUrl, member.imageUrl) {
            val urls = mutableListOf<String>()
            member.primaryImageUrl
                ?.takeIf { it.isNotBlank() }
                ?.let { urls += dependencies.apiConfig.toAbsoluteUrl(it) }
            member.imageUrl
                ?.takeIf { it.isNotBlank() }
                ?.let { urls += dependencies.apiConfig.toAbsoluteUrl(it) }
            member.images.forEach { image ->
                image.url
                    ?.takeIf { it.isNotBlank() }
                    ?.let { urls += dependencies.apiConfig.toAbsoluteUrl(it) }
            }
            if (urls.isEmpty()) {
                memberProfileImagePath(clubId, member.id)
                    ?.let { urls += dependencies.apiConfig.toAbsoluteUrl(it) }
            }
            urls.distinct()
        }
        val activeMemberImageUrl = remember(member.id, member.images, member.primaryImageUrl, member.imageUrl, memberImageUrls) {
            val primary = member.primaryImageUrl
                ?.takeIf { it.isNotBlank() }
                ?.let { dependencies.apiConfig.toAbsoluteUrl(it) }
            val legacy = member.imageUrl
                ?.takeIf { it.isNotBlank() }
                ?.let { dependencies.apiConfig.toAbsoluteUrl(it) }
            primary ?: legacy ?: memberImageUrls.firstOrNull()
        }
        if (memberImageUrls.isNotEmpty()) {
            if (memberImageUrls.size == 1) {
                Box(modifier = Modifier.fillMaxWidth()) {
                    AuthenticatedAsyncImage(
                        imageUrl = memberImageUrls.first(),
                        authHeaders = dependencies.diaryAuthHeaders(),
                        modifier = Modifier
                            .fillMaxWidth()
                            .heightIn(max = 220.dp),
                        contentDescription = member.fullName(),
                    )
                    if (activeMemberImageUrl != null) {
                        Box(
                            modifier = Modifier
                                .align(Alignment.TopEnd)
                                .padding(8.dp)
                                .background(Color(0xCC2E7D32))
                                .padding(horizontal = 8.dp, vertical = 4.dp),
                        ) {
                            Text(
                                tr("members.activeImage", "Aktiv"),
                                color = Color.White,
                                style = MaterialTheme.typography.caption,
                                fontWeight = FontWeight.Bold,
                            )
                        }
                    }
                }
            } else {
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .horizontalScroll(rememberScrollState()),
                    horizontalArrangement = Arrangement.spacedBy(8.dp),
                ) {
                    memberImageUrls.forEachIndexed { index, url ->
                        Column(modifier = Modifier.width(220.dp)) {
                            Box(modifier = Modifier.fillMaxWidth().height(220.dp)) {
                                AuthenticatedAsyncImage(
                                    imageUrl = url,
                                    authHeaders = dependencies.diaryAuthHeaders(),
                                    modifier = Modifier.fillMaxSize(),
                                    contentDescription = "${member.fullName()} (${index + 1})",
                                )
                                if (url == activeMemberImageUrl) {
                                    Box(
                                        modifier = Modifier
                                            .align(Alignment.TopEnd)
                                            .padding(6.dp)
                                            .background(Color(0xCC2E7D32))
                                            .padding(horizontal = 8.dp, vertical = 3.dp),
                                    ) {
                                        Text(
                                            tr("members.activeImage", "Aktiv"),
                                            color = Color.White,
                                            style = MaterialTheme.typography.caption,
                                            fontWeight = FontWeight.Bold,
                                        )
                                    }
                                }
                            }
                        }
                    }
                }
            }
            Spacer(modifier = Modifier.height(12.dp))
        }
        DetailLine(tr("mobile.status", "Status"), if (member.active) tr("mobile.active", "Aktiv") else tr("mobile.inactive", "Inaktiv"))
        member.testMembership?.takeIf { it }?.let {
            DetailLine(tr("members.testMember", "Testmitglied"), tr("mobile.yes", "Ja"))
        }
        DetailLine(tr("members.street", "Straße"), member.street ?: "-")
        DetailLine(tr("members.city", "Ort"), member.city ?: "-")
        DetailLine(tr("members.postalCode", "PLZ"), member.postalCode ?: "-")
        DetailLine(tr("members.birthDate", "Geburtsdatum"), formatIsoDateForDisplay(member.birthDate))
        DetailLine(tr("members.gender", "Geschlecht"), member.gender ?: "-")
        DetailLine("TTR", member.ttr?.toString() ?: "-")
        DetailLine("QTTR", member.qttr?.toString() ?: "-")
        DetailLine(tr("mobile.lastTraining", "Letztes Training"), formatIsoDateForDisplay(member.lastTraining))
        DetailLine(tr("members.trainingParticipations", "Trainings-Teilnahmen"), member.trainingParticipations?.toString() ?: "-")

        SectionTitle(tr("members.contact", "Kontakt"))
        val phones = member.contacts.filter { it.type == "phone" }
        val emails = member.contacts.filter { it.type == "email" }
        if (phones.isEmpty() && emails.isEmpty()) {
            DetailLine(tr("members.phone", "Telefon"), member.phone?.ifBlank { "-" } ?: "-")
            DetailLine(tr("members.email", "E-Mail"), member.email?.ifBlank { "-" } ?: "-")
        } else {
            phones.forEach { DetailLine(tr("members.phone", "Telefon"), it.value) }
            emails.forEach { DetailLine(tr("members.email", "E-Mail"), it.value) }
        }

        SectionTitle(tr("members.trainingGroups", "Trainingsgruppen"))
        if (detailBusy) LoadingInline()
        ErrorText(detailError)
        memberGroups.forEach { g ->
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceBetween,
                verticalAlignment = Alignment.CenterVertically,
            ) {
                Text("· ${g.name}", modifier = Modifier.weight(1f))
                if (canWriteMembers) {
                    TextButton(onClick = {
                        dependencies.applicationScope.launch {
                            runCatching {
                                dependencies.membersManager.removeMemberFromTrainingGroup(clubId, g.id, member.id)
                                memberGroups = dependencies.membersManager.listMemberTrainingGroups(clubId, member.id)
                            }
                        }
                    }) { Text(tr("members.removeFromGroup", "Entfernen")) }
                }
            }
        }
        if (memberGroups.isEmpty() && !detailBusy) {
            Text(tr("members.noGroupsAssigned", "Keine Gruppe zugewiesen."), style = MaterialTheme.typography.caption)
        }
        if (canWriteMembers) {
            Box {
                OutlinedButton(onClick = { groupMenu = true }, modifier = Modifier.fillMaxWidth()) {
                    Text(tr("members.addToGroup", "Gruppe zuweisen"))
                }
                DropdownMenu(expanded = groupMenu, onDismissRequest = { groupMenu = false }) {
                    val assigned = memberGroups.map { it.id }.toSet()
                    val choices = allGroups.filter { it.id !in assigned }
                    if (choices.isEmpty()) {
                        DropdownMenuItem(onClick = { groupMenu = false }) {
                            Text(tr("members.noMoreGroups", "Keine weiteren Gruppen"))
                        }
                    } else {
                        choices.forEach { g ->
                            DropdownMenuItem(onClick = {
                                groupMenu = false
                                dependencies.applicationScope.launch {
                                    runCatching {
                                        dependencies.membersManager.addMemberToTrainingGroup(clubId, g.id, member.id)
                                        memberGroups = dependencies.membersManager.listMemberTrainingGroups(clubId, member.id)
                                    }
                                }
                            }) { Text(g.name) }
                        }
                    }
                }
            }
        }

        SectionTitle(tr("members.trainingTimesClub", "Trainingszeiten (Verein)"))
        schedule.forEach { g ->
            Text(g.name, fontWeight = FontWeight.SemiBold, modifier = Modifier.padding(top = 6.dp))
            if (g.trainingTimes.isEmpty()) {
                Text("–", style = MaterialTheme.typography.caption, modifier = Modifier.padding(start = 8.dp))
            } else {
                g.trainingTimes.sortedWith(compareBy<TrainingTimeDto> { it.weekday }.thenBy { it.sortOrder }).forEach { tt ->
                    Text(
                        "· ${trainingWeekdayLabel(tt.weekday)}  ${tt.startTime}–${tt.endTime}",
                        style = MaterialTheme.typography.caption,
                        modifier = Modifier.padding(start = 8.dp, top = 2.dp),
                    )
                }
            }
        }

        SectionTitle(tr("members.activityStats", "Aktivität (Statistik)"))
        Row(horizontalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier.fillMaxWidth()) {
            listOf("month" to "1M", "3months" to "3M", "year" to "1J", "all" to "Alle").forEach { (key, label) ->
                TextButton(
                    onClick = { activityPeriod = key },
                    enabled = activityPeriod != key,
                ) { Text(label) }
            }
        }
        if (activityStats.isEmpty() && !detailBusy) {
            Text("–", style = MaterialTheme.typography.caption)
        } else {
            activityStats.take(25).forEach { row ->
                Text(
                    "${row.name ?: row.code ?: "?"} — ${row.count}×",
                    style = MaterialTheme.typography.body2,
                    modifier = Modifier.padding(vertical = 2.dp),
                )
            }
        }

        SectionTitle(tr("members.lastParticipations", "Letzte Teilnahmen"))
        if (lastParts.isEmpty() && !detailBusy) {
            Text("–", style = MaterialTheme.typography.caption)
        } else {
            lastParts.forEach { p ->
                val line = "${displayActivityDate(p.date)}  ${p.activityName ?: p.activityFullName ?: ""}"
                Text(line, style = MaterialTheme.typography.body2, modifier = Modifier.padding(vertical = 2.dp))
            }
        }
    }
}

private data class MemberContactEditRow(
    val value: String,
    val isParent: Boolean = false,
    val parentName: String = "",
    val isPrimary: Boolean = false,
)

private fun memberContactsToSave(
    phones: List<MemberContactEditRow>,
    emails: List<MemberContactEditRow>,
): List<MemberContactSetBody> = buildList {
    phones.forEach { r ->
        if (r.value.isNotBlank()) {
            add(
                MemberContactSetBody(
                    type = "phone",
                    value = r.value.trim(),
                    isParent = r.isParent,
                    parentName = r.parentName.trim().ifBlank { null },
                    isPrimary = r.isPrimary,
                ),
            )
        }
    }
    emails.forEach { r ->
        if (r.value.isNotBlank()) {
            add(
                MemberContactSetBody(
                    type = "email",
                    value = r.value.trim(),
                    isParent = r.isParent,
                    parentName = r.parentName.trim().ifBlank { null },
                    isPrimary = r.isPrimary,
                ),
            )
        }
    }
}

private fun primaryPhoneForLegacy(phones: List<MemberContactEditRow>): String? =
    phones.firstOrNull { it.isPrimary && it.value.isNotBlank() }?.value?.trim()
        ?: phones.firstOrNull { it.value.isNotBlank() }?.value?.trim()

private fun primaryEmailForLegacy(emails: List<MemberContactEditRow>): String? =
    emails.firstOrNull { it.isPrimary && it.value.isNotBlank() }?.value?.trim()
        ?: emails.firstOrNull { it.value.isNotBlank() }?.value?.trim()

@Composable
private fun MemberEditRoute(
    clubId: Int,
    seedMember: Member?,
    dependencies: AppDependencies,
    canWriteMembers: Boolean,
    onBack: () -> Unit,
    onSaved: (Int?) -> Unit,
) {
    BackHandler(onBack = onBack)
    if (!canWriteMembers) {
        Column(Modifier.fillMaxSize().padding(ScreenHorizontalPadding)) {
            Text(tr("members.noWritePermission", "Keine Berechtigung zum Bearbeiten."))
            TextButton(onClick = onBack) { Text(tr("mobile.back", "Zurück")) }
        }
        return
    }
    val scroll = rememberScrollState()
    val mid = seedMember?.id ?: 0
    var firstName by remember(mid) { mutableStateOf(seedMember?.firstName.orEmpty()) }
    var lastName by remember(mid) { mutableStateOf(seedMember?.lastName.orEmpty()) }
    var street by remember(mid) { mutableStateOf(seedMember?.street.orEmpty()) }
    var city by remember(mid) { mutableStateOf(seedMember?.city.orEmpty()) }
    var postalCode by remember(mid) { mutableStateOf(seedMember?.postalCode.orEmpty()) }
    var birthDate by remember(mid) { mutableStateOf(seedMember?.birthDate.orEmpty()) }
    val phoneRows = remember(mid) {
        mutableStateListOf<MemberContactEditRow>().apply {
            if (seedMember == null) {
                add(MemberContactEditRow(""))
            } else {
                val fromContacts = seedMember.contacts.filter { it.type == "phone" }
                if (fromContacts.isNotEmpty()) {
                    fromContacts.forEach { c ->
                        add(MemberContactEditRow(c.value, c.isParent, c.parentName.orEmpty(), c.isPrimary))
                    }
                } else {
                    val p = seedMember.phone?.takeIf { it.isNotBlank() }
                    add(MemberContactEditRow(p ?: "", isPrimary = p != null))
                }
            }
        }
    }
    val emailRows = remember(mid) {
        mutableStateListOf<MemberContactEditRow>().apply {
            if (seedMember == null) {
                add(MemberContactEditRow(""))
            } else {
                val fromContacts = seedMember.contacts.filter { it.type == "email" }
                if (fromContacts.isNotEmpty()) {
                    fromContacts.forEach { c ->
                        add(MemberContactEditRow(c.value, c.isParent, c.parentName.orEmpty(), c.isPrimary))
                    }
                } else {
                    val e = seedMember.email?.takeIf { it.isNotBlank() }
                    add(MemberContactEditRow(e ?: "", isPrimary = e != null))
                }
            }
        }
    }
    var gender by remember(mid) { mutableStateOf(seedMember?.gender ?: "unknown") }
    var active by remember(mid) { mutableStateOf(seedMember?.active ?: true) }
    var testMembership by remember(mid) { mutableStateOf(seedMember?.testMembership == true) }
    var picsOk by remember(mid) { mutableStateOf(seedMember?.picsInInternetAllowed == true) }
    var formHanded by remember(mid) { mutableStateOf(seedMember?.memberFormHandedOver == true) }
    var adultRelease by remember(mid) { mutableStateOf(seedMember?.adultReleaseApproved == true) }
    var adultReserve by remember(mid) { mutableStateOf(seedMember?.adultReserveApproved == true) }
    var genderMenu by remember { mutableStateOf(false) }
    var saving by remember { mutableStateOf(false) }
    var saveError by remember { mutableStateOf<String?>(null) }

    fun setPhonePrimary(index: Int) {
        val hasValue = phoneRows.getOrNull(index)?.value?.isNotBlank() == true
        for (i in phoneRows.indices) {
            phoneRows[i] = phoneRows[i].copy(isPrimary = hasValue && i == index)
        }
    }

    fun setEmailPrimary(index: Int) {
        val hasValue = emailRows.getOrNull(index)?.value?.isNotBlank() == true
        for (i in emailRows.indices) {
            emailRows[i] = emailRows[i].copy(isPrimary = hasValue && i == index)
        }
    }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(scroll)
            .imePadding()
            .navigationBarsPadding()
            .padding(horizontal = ScreenHorizontalPadding, vertical = 12.dp),
    ) {
        ScreenTopBar(
            title = if (seedMember == null) tr("members.newMember", "Neues Mitglied") else tr("members.editMember", "Mitglied bearbeiten"),
            onBack = onBack,
        )
        ErrorText(saveError)
        OutlinedTextField(firstName, { firstName = it }, label = { Text(tr("members.firstName", "Vorname")) }, modifier = Modifier.fillMaxWidth())
        OutlinedTextField(lastName, { lastName = it }, label = { Text(tr("members.lastName", "Nachname")) }, modifier = Modifier.fillMaxWidth())
        OutlinedTextField(street, { street = it }, label = { Text(tr("members.street", "Straße")) }, modifier = Modifier.fillMaxWidth())
        OutlinedTextField(postalCode, { postalCode = it }, label = { Text(tr("members.postalCode", "PLZ")) }, modifier = Modifier.fillMaxWidth())
        OutlinedTextField(city, { city = it }, label = { Text(tr("members.city", "Ort")) }, modifier = Modifier.fillMaxWidth())
        LocalizedDatePickerField(birthDate, { birthDate = it }, label = tr("members.birthDate", "Geburtsdatum"), modifier = Modifier.fillMaxWidth())
        Box {
            OutlinedButton(onClick = { genderMenu = true }, modifier = Modifier.fillMaxWidth().padding(top = 8.dp)) {
                Text("${tr("members.gender", "Geschlecht")}: $gender")
            }
            DropdownMenu(expanded = genderMenu, onDismissRequest = { genderMenu = false }) {
                listOf("unknown", "male", "female", "diverse").forEach { g ->
                    DropdownMenuItem(onClick = { gender = g; genderMenu = false }) { Text(g) }
                }
            }
        }

        SectionTitle(tr("members.phonesSection", "Telefonnummern"))
        phoneRows.forEachIndexed { index, row ->
            key("phone", index) {
                Card(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(vertical = 6.dp),
                    elevation = 1.dp,
                ) {
                    Column(modifier = Modifier.padding(10.dp)) {
                        OutlinedTextField(
                            value = row.value,
                            onValueChange = { v ->
                                val cur = phoneRows[index]
                                phoneRows[index] = cur.copy(value = v)
                            },
                            label = { Text(tr("members.phone", "Telefon")) },
                            modifier = Modifier.fillMaxWidth(),
                            singleLine = true,
                        )
                        Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
                            Checkbox(
                                checked = row.isParent,
                                onCheckedChange = { c ->
                                    val cur = phoneRows[index]
                                    phoneRows[index] = cur.copy(isParent = c)
                                },
                            )
                            Text(tr("members.contactIsParent", "Eltern-Telefon"), style = MaterialTheme.typography.body2)
                        }
                        if (row.isParent) {
                            OutlinedTextField(
                                value = row.parentName,
                                onValueChange = { v ->
                                    val cur = phoneRows[index]
                                    phoneRows[index] = cur.copy(parentName = v)
                                },
                                label = { Text(tr("members.parentName", "Name des Erziehungsberechtigten")) },
                                modifier = Modifier.fillMaxWidth(),
                                singleLine = true,
                            )
                        }
                        Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
                            Checkbox(
                                checked = row.isPrimary,
                                onCheckedChange = { c ->
                                    if (c) setPhonePrimary(index) else {
                                        val cur = phoneRows[index]
                                        phoneRows[index] = cur.copy(isPrimary = false)
                                    }
                                },
                            )
                            Text(tr("members.contactPrimary", "Primär"), style = MaterialTheme.typography.body2)
                        }
                        if (phoneRows.size > 1) {
                            TextButton(onClick = { if (phoneRows.size > 1) phoneRows.removeAt(index) }) {
                                Text(tr("members.removePhoneRow", "Nummer entfernen"))
                            }
                        }
                    }
                }
            }
        }
        OutlinedButton(
            onClick = { phoneRows.add(MemberContactEditRow("")) },
            modifier = Modifier.fillMaxWidth(),
        ) { Text(tr("members.addPhone", "Weitere Telefonnummer")) }

        SectionTitle(tr("members.emailsSection", "E-Mail-Adressen"))
        emailRows.forEachIndexed { index, row ->
            key("email", index) {
                Card(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(vertical = 6.dp),
                    elevation = 1.dp,
                ) {
                    Column(modifier = Modifier.padding(10.dp)) {
                        OutlinedTextField(
                            value = row.value,
                            onValueChange = { v ->
                                val cur = emailRows[index]
                                emailRows[index] = cur.copy(value = v)
                            },
                            label = { Text(tr("members.email", "E-Mail")) },
                            modifier = Modifier.fillMaxWidth(),
                            singleLine = true,
                        )
                        Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
                            Checkbox(
                                checked = row.isParent,
                                onCheckedChange = { c ->
                                    val cur = emailRows[index]
                                    emailRows[index] = cur.copy(isParent = c)
                                },
                            )
                            Text(tr("members.contactIsParentEmail", "Eltern-E-Mail"), style = MaterialTheme.typography.body2)
                        }
                        if (row.isParent) {
                            OutlinedTextField(
                                value = row.parentName,
                                onValueChange = { v ->
                                    val cur = emailRows[index]
                                    emailRows[index] = cur.copy(parentName = v)
                                },
                                label = { Text(tr("members.parentName", "Name des Erziehungsberechtigten")) },
                                modifier = Modifier.fillMaxWidth(),
                                singleLine = true,
                            )
                        }
                        Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
                            Checkbox(
                                checked = row.isPrimary,
                                onCheckedChange = { c ->
                                    if (c) setEmailPrimary(index) else {
                                        val cur = emailRows[index]
                                        emailRows[index] = cur.copy(isPrimary = false)
                                    }
                                },
                            )
                            Text(tr("members.contactPrimary", "Primär"), style = MaterialTheme.typography.body2)
                        }
                        if (emailRows.size > 1) {
                            TextButton(onClick = { if (emailRows.size > 1) emailRows.removeAt(index) }) {
                                Text(tr("members.removeEmailRow", "Adresse entfernen"))
                            }
                        }
                    }
                }
            }
        }
        OutlinedButton(
            onClick = { emailRows.add(MemberContactEditRow("")) },
            modifier = Modifier.fillMaxWidth(),
        ) { Text(tr("members.addEmail", "Weitere E-Mail")) }

        RowSwitch(tr("mobile.active", "Aktiv"), active, { active = it })
        RowSwitch(tr("members.testMember", "Testmitglied"), testMembership, { testMembership = it })
        RowSwitch(tr("members.picsInternet", "Bilder Internet"), picsOk, { picsOk = it })
        RowSwitch(tr("members.memberFormHandedOver", "Formular ausgehändigt"), formHanded, { formHanded = it })
        RowSwitch(tr("members.adultRelease", "Erwachsenen-Freigabe"), adultRelease, { adultRelease = it })
        RowSwitch(tr("members.adultReserve", "Erw.-Ersatz"), adultReserve, { adultReserve = it })
        if (seedMember != null) {
            DetailLine("TTR", seedMember.ttr?.toString() ?: "-")
            DetailLine("QTTR", seedMember.qttr?.toString() ?: "-")
        }
        Spacer(modifier = Modifier.height(16.dp))
        Button(
            onClick = {
                saving = true
                saveError = null
                val phoneSnapshot = phoneRows.toList()
                val emailSnapshot = emailRows.toList()
                val contactsBody = memberContactsToSave(phoneSnapshot, emailSnapshot)
                val legacyPhone = primaryPhoneForLegacy(phoneSnapshot).orEmpty()
                val legacyEmail = primaryEmailForLegacy(emailSnapshot).orEmpty()
                dependencies.applicationScope.launch {
                    try {
                        val baseMember = seedMember ?: Member(id = 0, clubId = clubId)
                        val body = baseMember.toSetBody(
                            firstname = firstName.trim(),
                            lastname = lastName.trim(),
                            street = street.trim().ifBlank { null },
                            city = city.trim().ifBlank { null },
                            postalCode = postalCode.trim().ifBlank { null },
                            birthdate = birthDate.trim().ifBlank { null },
                            phone = legacyPhone.ifBlank { null },
                            email = legacyEmail.ifBlank { null },
                            active = active,
                            testMembership = testMembership,
                            picsInInternetAllowed = picsOk,
                            gender = gender,
                            memberFormHandedOver = formHanded,
                            adultReleaseApproved = adultRelease,
                            adultReserveApproved = adultReserve,
                            contacts = contactsBody,
                        )
                        dependencies.membersManager.saveMember(clubId, body)
                        dependencies.membersManager.loadMembers(clubId)
                        val newId = seedMember?.id ?: dependencies.membersManager.state.value.members
                            .filter { it.firstName.trim() == firstName.trim() && it.lastName.trim() == lastName.trim() }
                            .maxByOrNull { it.id }?.id
                        onSaved(newId)
                    } catch (t: Throwable) {
                        saveError = t.message
                    } finally {
                        saving = false
                    }
                }
            },
            enabled = !saving && (seedMember != null || (firstName.trim().isNotBlank() && lastName.trim().isNotBlank())),
            modifier = Modifier.fillMaxWidth().heightIn(min = TouchMinHeight),
        ) { Text(tr("common.save", "Speichern")) }
    }
}

@Composable
private fun RowSwitch(label: String, checked: Boolean, onChecked: (Boolean) -> Unit) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 4.dp),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.SpaceBetween,
    ) {
        Text(label, modifier = Modifier.weight(1f))
        Switch(checked = checked, onCheckedChange = onChecked)
    }
}

@Composable
private fun SettingsScreen(
    dependencies: AppDependencies,
    billingOrdersSection: BillingOrdersSection?,
    onBillingOrdersSectionChange: (BillingOrdersSection?) -> Unit,
    settingsClubAdminRequest: ClubAdminSettingsSection?,
    onConsumeSettingsClubAdminRequest: () -> Unit,
    settingsStammdatenRequest: ClubStammdatenSection?,
    onConsumeSettingsStammdatenRequest: () -> Unit,
) {
    var clubAdminSection by remember { mutableStateOf<ClubAdminSettingsSection?>(null) }
    var stammdatenSection by remember { mutableStateOf<ClubStammdatenSection?>(null) }
    var personalHub by remember { mutableStateOf<PersonalHubDestination?>(null) }

    LaunchedEffect(settingsClubAdminRequest) {
        val r = settingsClubAdminRequest ?: return@LaunchedEffect
        clubAdminSection = r
        stammdatenSection = null
        personalHub = null
        onConsumeSettingsClubAdminRequest()
    }
    LaunchedEffect(settingsStammdatenRequest) {
        val r = settingsStammdatenRequest ?: return@LaunchedEffect
        stammdatenSection = r
        clubAdminSection = null
        personalHub = null
        onConsumeSettingsStammdatenRequest()
    }
    if (billingOrdersSection != null) {
        BillingOrdersFlowScreen(
            section = billingOrdersSection!!,
            dependencies = dependencies,
            onBack = { onBillingOrdersSectionChange(null) },
        )
        return
    }
    if (personalHub != null) {
        PersonalHubFlowScreen(
            destination = personalHub!!,
            dependencies = dependencies,
            onBack = { personalHub = null },
        )
        return
    }
    if (clubAdminSection != null) {
        ClubAdminFlowScreen(
            destination = clubAdminSection!!,
            dependencies = dependencies,
            onBack = { clubAdminSection = null },
        )
        return
    }
    if (stammdatenSection != null) {
        ClubStammdatenFlowScreen(
            destination = stammdatenSection!!,
            dependencies = dependencies,
            onBack = { stammdatenSection = null },
        )
        return
    }
    val authState by dependencies.authManager.state.collectAsState()
    val clubState by dependencies.clubManager.state.collectAsState()
    val androidContext = LocalContext.current
    var sessionStatus by rememberSaveable { mutableStateOf<String?>(null) }
    var isLoading by rememberSaveable { mutableStateOf(false) }
    var showDeleteAccountDialog by rememberSaveable { mutableStateOf(false) }
    val sessionValidMessage = tr("mobile.sessionValid", "Session gültig")
    val sessionInvalidMessage = tr("mobile.sessionInvalid", "Session ungültig")
    val sessionCheckFailedMessage = tr("mobile.sessionCheckFailed", "Session check fehlgeschlagen")

    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState())
            .imePadding()
            .navigationBarsPadding()
            .padding(horizontal = ScreenHorizontalPadding, vertical = 16.dp),
    ) {
        Button(
            onClick = {
                dependencies.applicationScope.launch {
                    dependencies.clubManager.clearSelection()
                }
            },
            modifier = Modifier
                .fillMaxWidth()
                .padding(bottom = 12.dp)
                .heightIn(min = 56.dp),
        ) {
            Text(tr("club.change", "Verein wechseln"), style = MaterialTheme.typography.h6)
        }
        Header(tr("mobile.more", "Mehr"))
        
        DetailLine(tr("mobile.user", "Benutzer"), authState.username ?: "-")
        DetailLine("Backend", dependencies.apiConfig.baseUrl)
        DetailLine("Club", clubState.clubs.firstOrNull { it.id == clubState.currentClubId }?.name ?: "-")
        clubState.currentPermissions?.let { permissions ->
            DetailLine(tr("mobile.role", "Rolle"), permissions.role)
        }
        val clubId = clubState.currentClubId
        val perms = clubState.currentPermissions
        if (clubId != null && perms != null) {
            SectionTitle(tr("mobile.clubAdmin", "Club-Verwaltung"))
            if (perms.canReadApprovals()) {
                TextButton(
                    onClick = { clubAdminSection = ClubAdminSettingsSection.Pending },
                    modifier = Modifier.fillMaxWidth(),
                ) { Text(tr("mobile.pendingApprovals", "Ausstehende Freigaben")) }
            }
            if (perms.canReadClubPermissions()) {
                TextButton(
                    onClick = { clubAdminSection = ClubAdminSettingsSection.Permissions },
                    modifier = Modifier.fillMaxWidth(),
                ) { Text(tr("mobile.permissionsAdmin", "Berechtigungen")) }
            }
            TextButton(
                onClick = { clubAdminSection = ClubAdminSettingsSection.Logs },
                modifier = Modifier.fillMaxWidth(),
            ) { Text(tr("mobile.apiLogs", "API-Logs")) }
            if (perms.canReadTeams()) {
                TextButton(
                    onClick = { stammdatenSection = ClubStammdatenSection.Teams },
                    modifier = Modifier.fillMaxWidth(),
                ) { Text(tr("navigation.teamManagement", "Team-Verwaltung")) }
            }
            if (perms.canReadClubSettings() || perms.canReadPredefinedActivities() || perms.canReadMembers()) {
                SectionTitle(tr("mobile.clubStammdaten", "Verein & Stammdaten"))
                if (perms.canReadClubSettings()) {
                    TextButton(
                        onClick = { stammdatenSection = ClubStammdatenSection.ClubSettings },
                        modifier = Modifier.fillMaxWidth(),
                    ) { Text(tr("clubSettings.title", "Vereinseinstellungen")) }
                }
                if (perms.canReadPredefinedActivities()) {
                    TextButton(
                        onClick = { stammdatenSection = ClubStammdatenSection.PredefinedActivities },
                        modifier = Modifier.fillMaxWidth(),
                    ) { Text(tr("mobile.predefinedActivities", "Standard-Aktivitäten")) }
                }
                if (perms.canReadMembers()) {
                    TextButton(
                        onClick = { stammdatenSection = ClubStammdatenSection.MemberTransfer },
                        modifier = Modifier.fillMaxWidth(),
                    ) { Text(tr("mobile.memberTransfer", "Mitgliedstransfer")) }
                }
            }
        }
        SectionTitle(tr("settings.personalSettings", "Persönliche Einstellungen"))
        TextButton(
            onClick = { personalHub = PersonalHubDestination.PersonalLanguage },
            modifier = Modifier.fillMaxWidth(),
        ) { Text(tr("mobile.language", "Sprache")) }
        TextButton(
            onClick = { personalHub = PersonalHubDestination.MyTischtennisAccount },
            modifier = Modifier.fillMaxWidth(),
        ) { Text(tr("myTischtennisAccount.title", "myTischtennis-Account")) }
        TextButton(
            onClick = { personalHub = PersonalHubDestination.ClickTtAccount },
            modifier = Modifier.fillMaxWidth(),
        ) { Text(tr("navigation.clickTtAccount", "HTTV / click-TT-Account")) }
        TextButton(
            onClick = { personalHub = PersonalHubDestination.ClickTtBrowser },
            modifier = Modifier.fillMaxWidth(),
        ) { Text(tr("navigation.clickTtBrowser", "HTTV / click-TT")) }
        SectionTitle(tr("mobile.ordersBillingSection", "Bestellungen & Abrechnung"))
        TextButton(
            onClick = { onBillingOrdersSectionChange(BillingOrdersSection.Orders) },
            modifier = Modifier.fillMaxWidth(),
        ) { Text(tr("orders.globalTitle", "Bestellungen")) }
        if (clubState.currentPermissions?.canReadMembers() == true) {
            TextButton(
                onClick = { onBillingOrdersSectionChange(BillingOrdersSection.Billing) },
                modifier = Modifier.fillMaxWidth(),
            ) { Text(tr("billing.title", "Abrechnung")) }
        }
        SectionTitle(tr("mobile.legal", "Rechtliches"))
        TextButton(
            onClick = {
                androidContext.startActivity(
                    Intent(Intent.ACTION_VIEW, Uri.parse("${dependencies.apiConfig.baseUrl}/datenschutz")),
                )
            },
            modifier = Modifier.fillMaxWidth(),
        ) { Text("Datenschutzerklärung") }
        TextButton(
            onClick = {
                androidContext.startActivity(
                    Intent(Intent.ACTION_VIEW, Uri.parse("${dependencies.apiConfig.baseUrl}/impressum")),
                )
            },
            modifier = Modifier.fillMaxWidth(),
        ) { Text("Impressum") }
        Button(
            onClick = {
                isLoading = true
                dependencies.applicationScope.launch {
                    sessionStatus = runCatching { dependencies.sessionApi.status() }
                        .fold(
                            onSuccess = { if (it.valid) sessionValidMessage else sessionInvalidMessage },
                            onFailure = { "$sessionCheckFailedMessage: ${it.message}" },
                        )
                    isLoading = false
                }
            },
            enabled = !isLoading,
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = 12.dp)
                .heightIn(min = TouchMinHeight),
        ) {
            Text(tr("mobile.sessionCheck", "Session prüfen"))
        }
        sessionStatus?.let { Text(it, modifier = Modifier.padding(top = 8.dp)) }
        OutlinedButton(
            onClick = {
                dependencies.applicationScope.launch {
                    dependencies.authManager.logout()
                    dependencies.clubManager.clearSelection()
                    dependencies.diaryManager.clear()
                    dependencies.membersManager.clear()
                    dependencies.trainingStatsManager.clear()
                    dependencies.scheduleManager.clear()
                    dependencies.pendingApprovalsManager.clear()
                    dependencies.permissionsAdminManager.clear()
                    dependencies.apiLogsManager.clear()
                    dependencies.clubInternalTournamentsManager.clear()
                    dependencies.officialTournamentsReadManager.clear()
                }
            },
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = 16.dp)
                .heightIn(min = TouchMinHeight),
        ) {
            Text(tr("auth.logout", "Abmelden"))
        }

        SectionTitle("Konto und Daten")
        OutlinedButton(
            onClick = { showDeleteAccountDialog = true },
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = 8.dp)
                .heightIn(min = TouchMinHeight),
        ) {
            Text("Eigenes Konto löschen", color = MaterialTheme.colors.error)
        }

        OutlinedButton(
            onClick = {
                dependencies.applicationScope.launch {
                    // Clear only the club selection so the ClubSelectScreen appears
                    dependencies.clubManager.clearSelection()
                }
            },
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = 8.dp)
                .heightIn(min = TouchMinHeight),
        ) {
            Text(tr("club.change", "Verein wechseln"))
        }
    }

    if (showDeleteAccountDialog) {
        DeleteAccountDialog(
            onDismiss = { showDeleteAccountDialog = false },
            onConfirm = { password, onError ->
                dependencies.applicationScope.launch {
                    runCatching {
                        dependencies.authManager.deleteAccount(password)
                        dependencies.clubManager.clearSelection()
                        dependencies.diaryManager.clear()
                        dependencies.membersManager.clear()
                        dependencies.trainingStatsManager.clear()
                        dependencies.scheduleManager.clear()
                        dependencies.pendingApprovalsManager.clear()
                        dependencies.permissionsAdminManager.clear()
                        dependencies.apiLogsManager.clear()
                        dependencies.clubInternalTournamentsManager.clear()
                        dependencies.officialTournamentsReadManager.clear()
                    }.onSuccess {
                        showDeleteAccountDialog = false
                    }.onFailure {
                        onError(it.message ?: "Konto konnte nicht gelöscht werden.")
                    }
                }
            },
        )
    }
}

@Composable
private fun DeleteAccountDialog(
    onDismiss: () -> Unit,
    onConfirm: (password: String, onError: (String) -> Unit) -> Unit,
) {
    var password by rememberSaveable { mutableStateOf("") }
    var error by rememberSaveable { mutableStateOf<String?>(null) }
    StableAlertDialog(
        onDismissRequest = onDismiss,
        title = { Text("Eigenes Konto löschen") },
        text = {
            Column {
                Text("Diese Aktion löscht dein Benutzerkonto, deine Login-Sessions, Vereinszuordnungen und personenbezogene Integrationsdaten. Geteilte Vereinsdaten bleiben erhalten.")
                error?.let { Text(it, color = MaterialTheme.colors.error, modifier = Modifier.padding(top = 8.dp)) }
                OutlinedTextField(
                    value = password,
                    onValueChange = { password = it },
                    label = { Text("Passwort zur Bestätigung") },
                    modifier = Modifier.fillMaxWidth().padding(top = 8.dp),
                    singleLine = true,
                )
            }
        },
        confirmButton = {
            TextButton(
                onClick = {
                    if (password.isBlank()) {
                        error = "Bitte Passwort eingeben."
                    } else {
                        onConfirm(password) { error = it }
                    }
                },
            ) { Text("Endgültig löschen", color = MaterialTheme.colors.error) }
        },
        dismissButton = {
            TextButton(onClick = onDismiss) { Text("Abbrechen") }
        },
    )
}

@Composable
private fun DiaryEditForm(
    submitLabel: String,
    excludeFromBillingLabel: String,
    onExcludeFromBillingChange: ((date: String, trainingStart: String?, trainingEnd: String?, excludeFromBilling: Boolean) -> Unit)? = null,
    onSubmit: (date: String, trainingStart: String?, trainingEnd: String?, excludeFromBilling: Boolean) -> Unit,
    initialDate: String = "",
    initialStart: String = "",
    initialEnd: String = "",
    initialExcludeFromBilling: Boolean = false,
) {
    var date by rememberSaveable { mutableStateOf(initialDate) }
    var start by rememberSaveable { mutableStateOf(initialStart) }
    var end by rememberSaveable { mutableStateOf(initialEnd) }
    var excludeFromBilling by rememberSaveable { mutableStateOf(initialExcludeFromBilling) }
    var error by rememberSaveable { mutableStateOf<String?>(null) }

    Card(modifier = Modifier.fillMaxWidth().padding(vertical = 12.dp), elevation = 2.dp) {
        Column(modifier = Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
            LocalizedDatePickerField(value = date, onValueChange = { date = it }, label = "Datum")
            Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                OutlinedTextField(value = start, onValueChange = { start = it }, label = { Text("Start") }, modifier = Modifier.weight(1f), singleLine = true)
                OutlinedTextField(value = end, onValueChange = { end = it }, label = { Text("Ende") }, modifier = Modifier.weight(1f), singleLine = true)
            }
            Row(
                modifier = Modifier.fillMaxWidth(),
                verticalAlignment = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.SpaceBetween,
            ) {
                Text(excludeFromBillingLabel)
                Switch(
                    checked = excludeFromBilling,
                    onCheckedChange = {
                        excludeFromBilling = it
                        onExcludeFromBillingChange?.invoke(
                            date,
                            start.takeIf { value -> value.isNotBlank() },
                            end.takeIf { value -> value.isNotBlank() },
                            excludeFromBilling,
                        )
                    },
                )
            }
            ErrorText(error)
            Button(onClick = {
                if (!date.matches(Regex("\\d{4}-\\d{2}-\\d{2}"))) {
                    error = "Bitte Datum auswählen"
                    return@Button
                }
                if (start.isNotBlank() && end.isNotBlank() && start >= end) {
                    error = "Start muss vor Ende liegen"
                    return@Button
                }
                error = null
                onSubmit(date, start.takeIf { it.isNotBlank() }, end.takeIf { it.isNotBlank() }, excludeFromBilling)
            }) {
                Text(submitLabel)
            }
        }
    }
}

@Composable
private fun DiaryListItem(entry: DiaryDate, onClick: () -> Unit) {
    Card(modifier = Modifier.fillMaxWidth().clickable(onClick = onClick), elevation = 1.dp) {
        Column(modifier = Modifier.padding(12.dp)) {
            Text(formatDate(entry.date), fontWeight = FontWeight.SemiBold)
            Text(formatTimeRange(entry.trainingStart, entry.trainingEnd))
            if (entry.diaryTags.isNotEmpty()) Text(entry.diaryTags.joinToString(", ") { it.name })
            if (entry.diaryNotes.isNotEmpty()) Text("${entry.diaryNotes.size} Notizen")
        }
    }
}

@Composable
private fun FormScaffold(
    title: String,
    subtitle: String? = null,
    content: @Composable ColumnScope.() -> Unit,
) {
    val scroll = rememberScrollState()
    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(scroll)
            .imePadding()
            .navigationBarsPadding()
            .padding(horizontal = ScreenHorizontalPadding, vertical = 24.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Text(title, style = MaterialTheme.typography.h5, fontWeight = FontWeight.SemiBold)
        subtitle?.let {
            Text(
                it,
                style = MaterialTheme.typography.body2,
                color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f),
                modifier = Modifier.padding(top = 8.dp),
            )
        }
        Spacer(modifier = Modifier.height(20.dp))
        Column(
            verticalArrangement = Arrangement.spacedBy(12.dp),
            modifier = Modifier.fillMaxWidth(),
            content = content,
        )
    }
}

@Composable
private fun MembersTogglePanel(
    title: String,
    expanded: Boolean,
    onToggle: () -> Unit,
    content: @Composable ColumnScope.() -> Unit,
) {
    Card(
        modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp),
        elevation = 1.dp,
        backgroundColor = MaterialTheme.colors.onSurface.copy(alpha = 0.03f),
    ) {
        Column(modifier = Modifier.fillMaxWidth()) {
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .clickable(onClick = onToggle)
                    .padding(horizontal = 12.dp, vertical = 8.dp),
                verticalAlignment = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.SpaceBetween,
            ) {
                Text(title, fontWeight = FontWeight.SemiBold, style = MaterialTheme.typography.subtitle2)
                Icon(
                    imageVector = if (expanded) Icons.Filled.ExpandLess else Icons.Filled.ExpandMore,
                    contentDescription = null,
                    modifier = Modifier.size(20.dp),
                )
            }
            if (expanded) {
                Column(
                    modifier = Modifier.fillMaxWidth().padding(horizontal = 12.dp, vertical = 8.dp),
                    content = content,
                )
            }
        }
    }
}

@Composable
private fun Header(text: String) {
    Text(text, style = MaterialTheme.typography.h5, fontWeight = FontWeight.SemiBold)
    Spacer(modifier = Modifier.height(12.dp))
}

@Composable
private fun PlanRowMemberAssignExpandable(
    clubId: Int,
    diaryDateId: Int,
    activityApiId: Int,
    subtitle: String,
    dependencies: AppDependencies,
    canWriteDiary: Boolean,
    presentMembers: List<Member>,
    participantRows: List<DiaryTrainingParticipant>,
    expandedId: Int?,
    onExpandedIdChange: (Int?) -> Unit,
    memberAssignMap: Map<Int, Set<Int>>,
    onMemberAssignMapChange: (Map<Int, Set<Int>>) -> Unit,
    busy: Boolean,
    onBusyChange: (Boolean) -> Unit,
    onParticipantsReplace: (List<DiaryTrainingParticipant>) -> Unit,
) {
    val expanded = expandedId == activityApiId
    Column(modifier = Modifier.fillMaxWidth().padding(start = 4.dp, bottom = 8.dp)) {
        TextButton(
            enabled = !busy,
            onClick = {
                if (expanded) {
                    onExpandedIdChange(null)
                } else {
                    onExpandedIdChange(activityApiId)
                    if (!memberAssignMap.containsKey(activityApiId)) {
                        dependencies.applicationScope.launch {
                            onBusyChange(true)
                            try {
                                val links = dependencies.diaryManager.listMemberActivityLinks(clubId, activityApiId)
                                onMemberAssignMapChange(
                                    memberAssignMap + (activityApiId to links.map { it.participantId }.toSet()),
                                )
                            } catch (_: Throwable) {
                                onMemberAssignMapChange(memberAssignMap + (activityApiId to emptySet()))
                            } finally {
                                onBusyChange(false)
                            }
                        }
                    }
                }
            },
        ) {
            Text(
                if (expanded) {
                    tr("diary.hidePlanMemberAssign", "Zuordnung ausblenden")
                } else {
                    "${tr("diary.planMemberAssign", "Wer macht mit?")}: $subtitle"
                },
            )
        }
        if (expanded) {
            if (busy) LoadingInline()
            if (presentMembers.isEmpty()) {
                Text(
                    tr("diary.noPresentMembersForAssign", "Keine als anwesend markierten Teilnehmer."),
                    style = MaterialTheme.typography.caption,
                    modifier = Modifier.padding(8.dp),
                )
            }
            presentMembers.forEach { member ->
                val rowId = participantRows.find { it.memberId == member.id }?.id
                val assigned = rowId != null && (memberAssignMap[activityApiId]?.contains(rowId) == true)
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    verticalAlignment = Alignment.CenterVertically,
                ) {
                    Checkbox(
                        checked = assigned,
                        enabled = canWriteDiary && !busy,
                        onCheckedChange = { want ->
                            dependencies.applicationScope.launch {
                                onBusyChange(true)
                                try {
                                    val pid = dependencies.diaryManager.ensureParticipantRowId(diaryDateId, member.id)
                                    onParticipantsReplace(dependencies.diaryManager.listTrainingParticipants(diaryDateId))
                                    if (want) {
                                        dependencies.diaryManager.addParticipantsToMemberActivity(
                                            clubId,
                                            activityApiId,
                                            listOf(pid),
                                        )
                                    } else {
                                        dependencies.diaryManager.removeParticipantFromMemberActivity(
                                            clubId,
                                            activityApiId,
                                            pid,
                                        )
                                    }
                                    val cur = (memberAssignMap[activityApiId] ?: emptySet()).toMutableSet()
                                    if (want) cur.add(pid) else cur.remove(pid)
                                    onMemberAssignMapChange(memberAssignMap + (activityApiId to cur))
                                } finally {
                                    onBusyChange(false)
                                }
                            }
                        },
                    )
                    Text(member.fullName(), style = MaterialTheme.typography.body2)
                }
            }
        }
    }
}

private fun diaryPlanItemComparator(a: DiaryDateActivityItem, b: DiaryDateActivityItem): Int {
    val c = a.orderId.compareTo(b.orderId)
    if (c != 0) return c
    return a.id.compareTo(b.id)
}

private val DiaryPlanColStart = 60.dp
private val DiaryPlanColGroup = 68.dp
private val DiaryPlanColDuration = 48.dp

@Composable
private fun DiaryPlanQuickIconAction(
    imageVector: ImageVector,
    contentDescription: String,
    enabled: Boolean,
    onClick: () -> Unit,
) {
    Box(
        modifier = Modifier
            .size(width = 33.dp, height = 30.dp)
            .clickable(enabled = enabled, onClick = onClick),
        contentAlignment = Alignment.Center,
    ) {
        Icon(
            imageVector = imageVector,
            contentDescription = contentDescription,
            modifier = Modifier.size(20.dp),
            tint = if (enabled) MaterialTheme.colors.primary
            else MaterialTheme.colors.onSurface.copy(alpha = 0.38f),
        )
    }
}

@Composable
private fun DiaryPlanQuickTextAction(
    label: String,
    enabled: Boolean,
    onClick: () -> Unit,
) {
    OutlinedButton(
        onClick = onClick,
        enabled = enabled,
        modifier = Modifier.heightIn(min = 30.dp),
    ) {
        Text(label, style = MaterialTheme.typography.caption, maxLines = 1)
    }
}

private fun sameTrainingPlanScope(items: List<DiaryDateActivityItem>, item: DiaryDateActivityItem): List<DiaryDateActivityItem> {
    return items.sortedWith(::diaryPlanItemComparator)
}

private fun calculatePlanStartLabels(items: List<DiaryDateActivityItem>, trainingStart: String?): Map<Int, String> {
    var globalCursor = normalizeTime(trainingStart)
    val groupCursors = mutableMapOf<Int, String>()
    val result = linkedMapOf<Int, String>()
    items.forEach { item ->
        val duration = item.duration?.takeIf { it > 0 } ?: 0
        val groupId = item.groupId
        if (groupId == null) {
            val start = latestTime(globalCursor, groupCursors.values)
            start?.let { result[item.id] = it.take(5) }
            val end = if (start != null && duration > 0) addMinutesToTime(start, duration) else start
            globalCursor = end
            if (end != null) {
                groupCursors.keys.toList().forEach { gid ->
                    val cursor = groupCursors[gid]
                    if (isTimeAfter(end, cursor)) {
                        groupCursors[gid] = end
                    }
                }
            }
        } else {
            val start = groupCursors[groupId] ?: globalCursor
            start?.let { result[item.id] = it.take(5) }
            val end = if (start != null && duration > 0) addMinutesToTime(start, duration) else start
            if (end != null) {
                groupCursors[groupId] = end
            }
        }
    }
    return result
}

private fun currentMinuteLabel(): String {
    return SimpleDateFormat("HH:mm", Locale.GERMANY).format(Date())
}

private fun playRawSound(context: Context, resId: Int) {
    runCatching {
        MediaPlayer.create(context.applicationContext, resId)?.apply {
            setOnCompletionListener { player -> player.release() }
            setOnErrorListener { player, _, _ ->
                player.release()
                true
            }
            start()
        }
    }
}

private fun latestTime(base: String?, values: Collection<String>): String? {
    var latest = base
    values.forEach { candidate ->
        if (isTimeAfter(candidate, latest)) latest = candidate
    }
    return latest
}

private fun isTimeAfter(candidate: String?, current: String?): Boolean {
    val c = timeToMinutes(candidate) ?: return false
    val v = timeToMinutes(current) ?: return true
    return c > v
}

private fun timeToMinutes(value: String?): Int? {
    val v = normalizeTime(value) ?: return null
    val h = v.substring(0, 2).toIntOrNull() ?: return null
    val m = v.substring(3, 5).toIntOrNull() ?: return null
    return h * 60 + m
}

private fun normalizeTime(value: String?): String? {
    val v = value?.trim().orEmpty()
    if (v.length >= 5 && v[2] == ':') return if (v.length >= 8) v.substring(0, 8) else "$v:00"
    return null
}

private fun addMinutesToTime(timeHhMmSs: String, minutes: Int): String {
    val h = timeHhMmSs.substring(0, 2).toIntOrNull() ?: return timeHhMmSs
    val m = timeHhMmSs.substring(3, 5).toIntOrNull() ?: return timeHhMmSs
    val total = ((h * 60 + m + minutes) % (24 * 60) + (24 * 60)) % (24 * 60)
    val nh = total / 60
    val nm = total % 60
    return "%02d:%02d:00".format(nh, nm)
}

@Composable
private fun DiaryGroupedPlanTable(
    sortedPlan: List<DiaryDateActivityItem>,
    planGroups: List<DiaryPlanGroup>,
    planStartTimes: Map<Int, String>,
) {
    data class GroupedCellEntry(
        val label: String,
        val duration: String?,
    )

    data class GroupedPlanRow(
        val startTime: String,
        val sharedItems: List<GroupedCellEntry>,
        val groupItems: Map<Int, List<GroupedCellEntry>>,
    )

    val rows = remember(sortedPlan, planGroups, planStartTimes) {
        val byStartTime = linkedMapOf<String, MutableList<DiaryDateActivityItem>>()
        sortedPlan.forEach { item ->
            if (item.isTimeblock) return@forEach
            val key = planStartTimes[item.id]?.take(5) ?: ""
            byStartTime.getOrPut(key) { mutableListOf() }.add(item)
        }

        val toMinutes = { value: String ->
            val parts = value.split(":")
            if (parts.size < 2) Int.MAX_VALUE
            else {
                val hh = parts[0].toIntOrNull() ?: Int.MAX_VALUE
                val mm = parts[1].toIntOrNull() ?: Int.MAX_VALUE
                if (hh == Int.MAX_VALUE || mm == Int.MAX_VALUE) Int.MAX_VALUE
                else hh * 60 + mm
            }
        }

        byStartTime
            .map { (start, items) ->
                val shared = mutableListOf<GroupedCellEntry>()
                val perGroup = mutableMapOf<Int, MutableList<GroupedCellEntry>>()
                items.forEach { item ->
                    val entry = GroupedCellEntry(
                        label = item.displayTitle("Zeitblock"),
                        duration = item.durationText?.takeIf { it.isNotBlank() } ?: item.duration?.toString(),
                    )
                    val gid = item.groupId
                    if (gid == null) {
                        shared.add(entry)
                    } else {
                        perGroup.getOrPut(gid) { mutableListOf() }.add(entry)
                    }
                }
                GroupedPlanRow(
                    startTime = start,
                    sharedItems = shared,
                    groupItems = perGroup.mapValues { it.value.toList() },
                )
            }
            .sortedWith(
                compareBy<GroupedPlanRow> { toMinutes(it.startTime) }
                    .thenBy { row ->
                        row.sharedItems.firstOrNull()?.label
                            ?: row.groupItems.values.firstOrNull()?.firstOrNull()?.label
                            ?: ""
                    },
            )
    }

    if (rows.isEmpty()) return
    Spacer(modifier = Modifier.height(6.dp))

    rows.forEach { row ->
        Row(modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp), verticalAlignment = Alignment.Top) {
            Text(row.startTime, modifier = Modifier.width(DiaryPlanColStart))
            if (row.sharedItems.isNotEmpty()) {
                Column(modifier = Modifier.weight(1f)) {
                    row.sharedItems.forEach { entry ->
                        Text(entry.label)
                    }
                }
                Column(modifier = Modifier.width(DiaryPlanColDuration)) {
                    row.sharedItems.forEach { entry ->
                        Text(entry.duration ?: "", style = MaterialTheme.typography.caption)
                    }
                }
            } else {
                Row(modifier = Modifier.weight(1f), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                    planGroups.forEach { g ->
                        val entries = row.groupItems[g.id].orEmpty()
                        Box(modifier = Modifier.weight(1f)) {
                            if (entries.isNotEmpty()) {
                                Column(modifier = Modifier.padding(start = 4.dp)) {
                                    Text(
                                        g.name ?: "Gruppe ${g.id}",
                                        style = MaterialTheme.typography.caption,
                                        fontWeight = FontWeight.Bold,
                                    )
                                    entries.forEach { entry ->
                                        Text(entry.label)
                                    }
                                }
                            }
                        }
                    }
                }
                Row(modifier = Modifier.width(DiaryPlanColDuration), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                    planGroups.forEach { g ->
                        val entries = row.groupItems[g.id].orEmpty()
                        Box(modifier = Modifier.weight(1f)) {
                            if (entries.isNotEmpty()) {
                                Column(modifier = Modifier.padding(start = 4.dp)) {
                                    entries.forEach { entry ->
                                        Text(entry.duration ?: "", style = MaterialTheme.typography.caption)
                                    }
                                }
                            }
                        }
                    }
                }
            }
            Spacer(modifier = Modifier.width(26.dp))
        }
        Divider()
    }
}

@Composable
private fun DiaryPlanToolbar(
    clubId: Int,
    entry: DiaryDate,
    dependencies: AppDependencies,
    clubName: String,
    planItems: List<DiaryDateActivityItem>,
    planGroups: List<DiaryPlanGroup>,
    activeMembers: List<Member>,
    participants: List<DiaryTrainingParticipant>,
    freeformActivities: List<DiaryFreeformActivity>,
    selectedGroupId: Int?,
    canWriteDiary: Boolean,
    planLoading: Boolean,
    planMutating: Boolean,
    onSelectGroup: (Int?) -> Unit,
    onRefresh: () -> Unit,
    onToggleAddActivity: () -> Unit,
    onCreateDrawing: () -> Unit,
    onToggleGroupActivity: () -> Unit,
    onToggleTrainingGroup: () -> Unit,
    onError: (String?) -> Unit,
) {
    val androidContext = LocalContext.current
    val openCount = planItems.count { !it.isTimeblock && it.duration == null && it.durationText.isNullOrBlank() } +
        planItems.sumOf { item -> item.groupActivities.count { it.duration == null && it.durationText.isNullOrBlank() } }
    val hasTrainingWindow = !entry.trainingStart.isNullOrBlank() || !entry.trainingEnd.isNullOrBlank()
    val isReady = hasTrainingWindow && planItems.isNotEmpty() && openCount == 0
    Surface(modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp), elevation = 1.dp) {
        Row(modifier = Modifier.fillMaxWidth().padding(10.dp), horizontalArrangement = Arrangement.SpaceBetween) {
            Text("${tr("diary.trainingWindow", "Trainingszeit")}: ${formatTimeRange(entry.trainingStart, entry.trainingEnd)}")
            Text(
                if (isReady) tr("diary.statusReadyShort", "Bereit")
                else if (openCount > 0) "${tr("diary.openPlanItemsLabel", "Offen")}: $openCount"
                else tr("diary.statusOpenShort", "Offen"),
                color = if (isReady) Color(0xFF2E7D32) else MaterialTheme.colors.primary,
            )
        }
    }
    val dateStr = formatDate(entry.date)
    val timeStr = formatTimeRange(entry.trainingStart, entry.trainingEnd)
    val timeblockLabel = tr("diary.timeblock", "Zeitblock")
    val sharePlanTitle = tr("diary.sharePdfPlan", "Trainingsplan teilen")
    val shareDayTitle = tr("diary.sharePdfDay", "Tages-PDF teilen")
    var groupFilterMenu by remember { mutableStateOf(false) }
    Row(
        horizontalArrangement = Arrangement.spacedBy(8.dp),
        modifier = Modifier.fillMaxWidth().padding(top = 4.dp).horizontalScroll(rememberScrollState()),
    ) {
        Box {
            OutlinedButton(onClick = { groupFilterMenu = true }, enabled = planGroups.isNotEmpty()) {
                Text(selectedGroupId?.let { id -> planGroups.find { it.id == id }?.name ?: "Gruppe $id" } ?: tr("common.all", "Alle"))
            }
            DropdownMenu(expanded = groupFilterMenu, onDismissRequest = { groupFilterMenu = false }) {
                DropdownMenuItem(onClick = { onSelectGroup(null); groupFilterMenu = false }) { Text(tr("common.all", "Alle")) }
                planGroups.forEach { group ->
                    DropdownMenuItem(onClick = { onSelectGroup(group.id); groupFilterMenu = false }) {
                        Text(group.name ?: "Gruppe ${group.id}")
                    }
                }
            }
        }
        OutlinedButton(onClick = onRefresh, enabled = !planMutating && !planLoading) { Text(tr("mobile.refresh", "Aktualisieren")) }
        OutlinedButton(onClick = onToggleAddActivity, enabled = canWriteDiary && !planMutating) { Text(tr("diary.addPlanActivity", "Neue Aktivitaet")) }
        OutlinedButton(onClick = onCreateDrawing, enabled = canWriteDiary && !planMutating) { Text(tr("diary.createDrawing", "Zeichnung")) }
        if (planGroups.isNotEmpty()) {
            OutlinedButton(onClick = onToggleGroupActivity, enabled = canWriteDiary && !planMutating) {
                Text(tr("diary.addGroupScopedActivity", "Gruppen-Aktivitaet"))
            }
        }
        OutlinedButton(onClick = onToggleTrainingGroup, enabled = canWriteDiary && !planMutating) {
            Text(tr("diary.addTrainingGroup", "Neue Trainingsgruppe"))
        }
        OutlinedButton(
            onClick = {
                runCatching {
                    val file = File(androidContext.cacheDir, "trainingsplan-${entry.id}.pdf")
                    writeTrainingPlanPdf(file, clubName, dateStr, timeStr, planItems, timeblockLabel)
                    sharePdfFile(androidContext, file, sharePlanTitle)
                }.onFailure { onError(it.message) }
            },
        ) { Text(tr("diary.trainingPlanPdfShort", "Plan-PDF")) }
        OutlinedButton(
            onClick = {
                dependencies.applicationScope.launch {
                    try {
                        val linkedIds = mutableMapOf<Int, List<Int>>()
                        planItems.forEach { item ->
                            linkedIds[item.id] = dependencies.diaryManager.listMemberActivityLinks(clubId, item.id).map { it.participantId }
                            item.groupActivities.forEach { nested ->
                                nested.id?.let { nestedId ->
                                    linkedIds[nestedId] = dependencies.diaryManager.listMemberActivityLinks(clubId, nestedId).map { it.participantId }
                                }
                            }
                        }
                        val file = File(androidContext.cacheDir, "trainingstag-${entry.id}.pdf")
                        writeTrainingDaySummaryPdf(
                            file, clubName, dateStr, timeStr, activeMembers, participants,
                            freeformActivities, planItems, planGroups, linkedIds, timeblockLabel,
                        )
                        sharePdfFile(androidContext, file, shareDayTitle)
                    } catch (t: Throwable) {
                        onError(t.message)
                    }
                }
            },
        ) { Text(tr("diary.trainingDaySummaryPdfShort", "Tag-PDF")) }
    }
}

@Composable
private fun DiaryTrainingGroupsEditor(
    groups: List<DiaryPlanGroup>,
    editingGroup: DiaryPlanGroup?,
    name: String,
    lead: String,
    canWriteDiary: Boolean,
    planMutating: Boolean,
    clubId: Int,
    diaryDateId: Int,
    dependencies: AppDependencies,
    onEdit: (DiaryPlanGroup) -> Unit,
    onNameChange: (String) -> Unit,
    onLeadChange: (String) -> Unit,
    onCancel: () -> Unit,
    onDelete: (DiaryPlanGroup) -> Unit,
    onBusy: (Boolean) -> Unit,
    onGroupsUpdated: (List<DiaryPlanGroup>) -> Unit,
    onError: (String?) -> Unit,
) {
    Text(
        tr("diary.trainingGroupsForDay", "Trainingsgruppen dieses Tages"),
        style = MaterialTheme.typography.caption,
        color = MaterialTheme.colors.onSurface.copy(alpha = 0.72f),
        modifier = Modifier.padding(top = 12.dp, bottom = 4.dp),
    )
    groups.forEach { group ->
        if (editingGroup?.id == group.id) {
            Column(modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp)) {
                OutlinedTextField(name, onNameChange, label = { Text(tr("diary.trainingGroupName", "Name der Gruppe")) }, modifier = Modifier.fillMaxWidth(), singleLine = true)
                OutlinedTextField(lead, onLeadChange, label = { Text(tr("diary.trainingGroupLead", "Uebungsleitung (optional)")) }, modifier = Modifier.fillMaxWidth(), singleLine = true)
                Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                    TextButton(
                        enabled = canWriteDiary && !planMutating && name.isNotBlank(),
                        onClick = {
                            dependencies.applicationScope.launch {
                                onBusy(true)
                                try {
                                    dependencies.diaryManager.updateTrainingGroup(group.id, clubId, diaryDateId, name.trim(), lead.trim().takeIf { it.isNotBlank() })
                                    onGroupsUpdated(dependencies.diaryManager.listTrainingGroups(clubId, diaryDateId))
                                } catch (t: Throwable) {
                                    onError(t.message)
                                } finally {
                                    onBusy(false)
                                }
                            }
                        },
                    ) { Text(tr("common.save", "Speichern")) }
                    TextButton(onClick = onCancel) { Text(tr("mobile.cancel", "Abbrechen")) }
                }
            }
        } else {
            Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) {
                Text("${group.name ?: "Gruppe ${group.id}"}${group.lead?.takeIf { it.isNotBlank() }?.let { " ($it)" }.orEmpty()}", style = MaterialTheme.typography.body2, modifier = Modifier.weight(1f))
                TextButton(enabled = canWriteDiary && !planMutating, onClick = { onEdit(group) }) { Text(tr("common.edit", "Bearbeiten")) }
                TextButton(enabled = canWriteDiary && !planMutating, onClick = { onDelete(group) }) { Text(tr("common.delete", "Loeschen")) }
            }
        }
    }
}

@Composable
private fun DiaryDeleteConfirmDialogs(
    showDateConfirm: Boolean,
    groupCandidate: DiaryPlanGroup?,
    canDeleteDate: Boolean,
    clubId: Int,
    diaryDateId: Int,
    dependencies: AppDependencies,
    onDismissDate: () -> Unit,
    onDismissGroup: () -> Unit,
    onDateDeleted: () -> Unit,
    onGroupBusy: (Boolean) -> Unit,
    onGroupUpdated: (List<DiaryDateActivityItem>, List<DiaryPlanGroup>) -> Unit,
    onGroupError: (String?) -> Unit,
) {
    if (showDateConfirm) {
        StableAlertDialog(
            onDismissRequest = onDismissDate,
            title = { Text(tr("diary.confirmDelete", "Trainingstag loeschen")) },
            text = { Text(tr("diary.confirmDeleteDateDetails", "Der leere Trainingstag wird unwiderruflich geloescht.")) },
            confirmButton = {
                TextButton(
                    enabled = canDeleteDate,
                    onClick = {
                        onDismissDate()
                        dependencies.applicationScope.launch {
                            dependencies.diaryManager.deleteDate(clubId, diaryDateId)
                            if (dependencies.diaryManager.state.value.error == null) onDateDeleted()
                        }
                    },
                ) { Text(tr("common.delete", "Loeschen")) }
            },
            dismissButton = { TextButton(onClick = onDismissDate) { Text(tr("mobile.cancel", "Abbrechen")) } },
        )
    }
    groupCandidate?.let { group ->
        StableAlertDialog(
            onDismissRequest = onDismissGroup,
            title = { Text(tr("diary.deleteGroup", "Trainingsgruppe loeschen")) },
            text = { Text("${group.name ?: "Gruppe ${group.id}"} wirklich loeschen?") },
            confirmButton = {
                TextButton(
                    onClick = {
                        onDismissGroup()
                        dependencies.applicationScope.launch {
                            onGroupBusy(true)
                            try {
                                dependencies.diaryManager.deleteTrainingGroup(group.id, clubId, diaryDateId)
                                onGroupUpdated(
                                    dependencies.diaryManager.fetchDateActivities(clubId, diaryDateId),
                                    dependencies.diaryManager.listTrainingGroups(clubId, diaryDateId),
                                )
                            } catch (t: Throwable) {
                                onGroupError(t.message)
                            } finally {
                                onGroupBusy(false)
                            }
                        }
                    },
                ) { Text(tr("common.delete", "Loeschen")) }
            },
            dismissButton = { TextButton(onClick = onDismissGroup) { Text(tr("mobile.cancel", "Abbrechen")) } },
        )
    }
}

@Composable
private fun DiaryQuickAddMemberDialog(
    clubId: Int,
    diaryDateId: Int,
    dependencies: AppDependencies,
    onDismiss: () -> Unit,
    onParticipantsUpdated: (List<DiaryTrainingParticipant>) -> Unit,
) {
    var firstName by rememberSaveable { mutableStateOf("") }
    var lastName by rememberSaveable { mutableStateOf("") }
    var birthDate by rememberSaveable { mutableStateOf("") }
    var gender by rememberSaveable { mutableStateOf("unknown") }
    var busy by remember { mutableStateOf(false) }
    var error by remember { mutableStateOf<String?>(null) }
    StableAlertDialog(
        onDismissRequest = { if (!busy) onDismiss() },
        title = { Text(tr("diary.quickAdd", "Testmitglied schnell hinzufuegen")) },
        text = {
            Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                error?.let { Text(it, color = MaterialTheme.colors.error) }
                OutlinedTextField(firstName, { firstName = it }, label = { Text(tr("members.firstName", "Vorname")) }, singleLine = true)
                OutlinedTextField(lastName, { lastName = it }, label = { Text(tr("members.lastName", "Nachname")) }, singleLine = true)
                LocalizedDatePickerField(birthDate, { birthDate = it }, label = tr("members.birthDate", "Geburtsdatum"))
                DrawingChoiceRowForDiaryGender(gender) { gender = it }
            }
        },
        confirmButton = {
            TextButton(
                enabled = !busy && firstName.isNotBlank() && gender != "unknown",
                onClick = {
                    dependencies.applicationScope.launch {
                        busy = true
                        error = null
                        try {
                            dependencies.membersManager.saveMember(
                                clubId,
                                MemberSetBody(
                                    firstname = firstName.trim(),
                                    lastname = lastName.trim(),
                                    birthdate = birthDate.trim().takeIf { it.isNotBlank() },
                                    gender = gender,
                                    active = true,
                                    testMembership = true,
                                ),
                            )
                            dependencies.membersManager.loadMembers(clubId)
                            val added = dependencies.membersManager.state.value.members
                                .filter { it.firstName == firstName.trim() && it.lastName == lastName.trim() && it.testMembership == true }
                                .maxByOrNull { it.id }
                            if (added != null) {
                                dependencies.diaryManager.addTrainingParticipant(diaryDateId, added.id)
                                onParticipantsUpdated(dependencies.diaryManager.listTrainingParticipants(diaryDateId))
                            }
                            onDismiss()
                        } catch (t: Throwable) {
                            error = t.message
                        } finally {
                            busy = false
                        }
                    }
                },
            ) { Text(tr("mobile.add", "Hinzufuegen")) }
        },
        dismissButton = { TextButton(onClick = onDismiss, enabled = !busy) { Text(tr("mobile.cancel", "Abbrechen")) } },
    )
}

@Composable
private fun DiaryPlanSupplementDialogs(
    clubId: Int,
    diaryDateId: Int,
    dependencies: AppDependencies,
    creatingDrawing: Boolean,
    drawingItem: DiaryDateActivityItem?,
    drawingNestedItem: GroupActivitySummary?,
    editingNestedItem: GroupActivitySummary?,
    selectedGroupId: Int?,
    canWriteDiary: Boolean,
    planMutating: Boolean,
    onDismissDrawing: () -> Unit,
    onDismissEditNested: () -> Unit,
    onBusy: (Boolean) -> Unit,
    onError: (String?) -> Unit,
    onPlanUpdated: (List<DiaryDateActivityItem>) -> Unit,
) {
    if (creatingDrawing || drawingItem != null || drawingNestedItem != null) {
        val topActivity = drawingItem?.predefinedActivity
        val nestedActivity = drawingNestedItem?.groupPredefinedActivity
        val initial = (topActivity ?: nestedActivity)?.let { activity ->
            PredefinedActivityDto(
                id = activity.id ?: 0,
                name = activity.name,
                code = activity.code,
                drawingData = activity.drawingDataForDisplay(),
                imageLink = activity.imageLink,
                duration = drawingItem?.duration ?: drawingNestedItem?.duration,
                durationText = drawingItem?.durationText ?: drawingNestedItem?.durationText,
            )
        } ?: (drawingItem?.drawingData ?: drawingNestedItem?.drawingData)?.let { drawingData ->
            PredefinedActivityDto(
                id = 0,
                name = drawingItem?.displayTitle(tr("diary.timeblock", "Zeitblock"))
                    ?: drawingNestedItem?.groupPredefinedActivity.displayLabel(),
                drawingData = drawingData,
                duration = drawingItem?.duration ?: drawingNestedItem?.duration,
                durationText = drawingItem?.durationText ?: drawingNestedItem?.durationText,
            )
        }
        DiaryCourtDrawingDialog(
            initial = initial,
            onDismiss = onDismissDrawing,
            onSave = { body ->
                dependencies.applicationScope.launch {
                    onBusy(true)
                    onError(null)
                    try {
                        val predefined = if (initial != null && initial.id > 0) {
                            dependencies.diaryManager.updatePredefinedActivity(initial.id, body)
                        } else {
                            dependencies.diaryManager.createPredefinedActivity(body)
                        }
                        val nestedId = drawingNestedItem?.id
                        when {
                            nestedId != null -> dependencies.diaryManager.updatePlanNestedGroupActivity(
                                clubId,
                                nestedId,
                                UpdateNestedPlanGroupActivityRequest(
                                    predefinedActivityId = predefined.id,
                                    duration = body.duration,
                                    durationText = body.durationText,
                                    groupId = drawingNestedItem.groupId,
                                ),
                            )
                            drawingItem != null -> dependencies.diaryManager.updatePlanActivity(
                                clubId,
                                drawingItem.id,
                                UpdateDiaryPlanActivityRequest(
                                    predefinedActivityId = predefined.id,
                                    duration = body.duration,
                                    durationText = body.durationText,
                                    groupId = drawingItem.groupId,
                                ),
                            )
                            else -> dependencies.diaryManager.createPlanActivity(
                                clubId,
                                CreateDiaryPlanActivityRequest(
                                    diaryDateId = diaryDateId,
                                    predefinedActivityId = predefined.id,
                                    activity = predefined.name.orEmpty(),
                                    duration = body.duration,
                                    durationText = body.durationText,
                                    groupId = selectedGroupId,
                                ),
                            )
                        }
                        onPlanUpdated(dependencies.diaryManager.fetchDateActivities(clubId, diaryDateId))
                    } catch (t: Throwable) {
                        onError(t.message)
                    } finally {
                        onBusy(false)
                    }
                }
            },
        )
    }
    editingNestedItem?.let { nested ->
        var duration by remember(nested.id) { mutableStateOf(nested.duration?.toString().orEmpty()) }
        var durationText by remember(nested.id) { mutableStateOf(nested.durationText.orEmpty()) }
        StableAlertDialog(
            onDismissRequest = onDismissEditNested,
            title = { Text(tr("diary.editGroupActivity", "Gruppen-Aktivitaet bearbeiten")) },
            text = {
                Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                    Text(nested.groupPredefinedActivity.displayLabel(), fontWeight = FontWeight.SemiBold)
                    OutlinedTextField(duration, { duration = it.filter(Char::isDigit) }, label = { Text(tr("diary.planDurationMin", "Minuten")) }, singleLine = true)
                    OutlinedTextField(durationText, { durationText = it }, label = { Text(tr("diary.planDurationText", "Dauer (Freitext)")) }, singleLine = true)
                }
            },
            confirmButton = {
                TextButton(
                    enabled = canWriteDiary && !planMutating,
                    onClick = {
                        val id = nested.id ?: return@TextButton
                        dependencies.applicationScope.launch {
                            onBusy(true)
                            try {
                                dependencies.diaryManager.updatePlanNestedGroupActivity(
                                    clubId,
                                    id,
                                    UpdateNestedPlanGroupActivityRequest(
                                        duration = duration.toIntOrNull(),
                                        durationText = durationText.trim().takeIf { it.isNotBlank() },
                                        groupId = nested.groupId,
                                    ),
                                )
                                onPlanUpdated(dependencies.diaryManager.fetchDateActivities(clubId, diaryDateId))
                            } catch (t: Throwable) {
                                onError(t.message)
                            } finally {
                                onBusy(false)
                            }
                        }
                    },
                ) { Text(tr("common.save", "Speichern")) }
            },
            dismissButton = { TextButton(onClick = onDismissEditNested) { Text(tr("mobile.cancel", "Abbrechen")) } },
        )
    }
}

@Composable
private fun DiaryPlanEditableCard(
    item: DiaryDateActivityItem,
    allPlanItems: List<DiaryDateActivityItem>,
    scheduledStart: String?,
    planGroups: List<DiaryPlanGroup>,
    planMutating: Boolean,
    canWriteDiary: Boolean,
    mainImageUrl: String?,
    nestedImageUrls: Map<Int, String>,
    canReadImages: Boolean,
    visibleGroupId: Int?,
    isExpanded: Boolean,
    onToggleExpand: () -> Unit,
    onAssignParticipants: () -> Unit,
    onAssignGroup: () -> Unit,
    onOpenImage: (String) -> Unit,
    onViewDrawing: (String, String) -> Unit,
    onOpenDrawing: () -> Unit,
    onOpenNestedDrawing: (GroupActivitySummary) -> Unit,
    onViewNestedDrawing: (String, String) -> Unit,
    onEdit: () -> Unit,
    onDelete: () -> Unit,
    onMoveUp: () -> Unit,
    onMoveDown: () -> Unit,
    onDeleteNested: (Int) -> Unit,
    onEditNested: (GroupActivitySummary) -> Unit,
    onAssignNested: (GroupActivitySummary) -> Unit,
) {
    val scope = remember(item.id, allPlanItems) { sameTrainingPlanScope(allPlanItems, item) }
    val idx = scope.indexOfFirst { it.id == item.id }
    val canUp = idx > 0
    val canDown = idx >= 0 && idx < scope.lastIndex

    val timeblockLabel = tr("diary.timeblock", "Zeitblock")
    val title = item.displayTitle(timeblockLabel).ifBlank {
        "${tr("mobile.activityFallback", "Aktivität")} ${item.id}"
    }
    val duration = item.durationText?.trim()?.takeIf { it.isNotEmpty() }
    val groupLine = item.planGroup?.name?.takeIf { it.isNotBlank() }
        ?: item.groupId?.let { gid -> planGroups.find { it.id == gid }?.name ?: "Gruppe $gid" }
    val drawingRaw = item.predefinedActivity.drawingDataForDisplay() ?: item.drawingData
    val hasMainVisual = drawingRaw != null || (canReadImages && mainImageUrl != null)

    val canMutate = canWriteDiary && !planMutating
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 1.dp),
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .clickable { onToggleExpand() }
                .padding(vertical = 2.dp),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            Text(
                scheduledStart ?: "—",
                style = MaterialTheme.typography.caption,
                modifier = Modifier.width(DiaryPlanColStart),
            )
            Row(modifier = Modifier.weight(1f), verticalAlignment = Alignment.CenterVertically) {
                if (hasMainVisual) {
                    DiaryPlanQuickIconAction(
                        imageVector = Icons.Filled.Visibility,
                        contentDescription = tr("diary.showImage", "Bild/Zeichnung anzeigen"),
                        enabled = !planMutating,
                        onClick = {
                            if (drawingRaw != null) {
                                onViewDrawing(drawingRaw, title)
                            } else {
                                mainImageUrl?.let(onOpenImage)
                            }
                        },
                    )
                }
                Text(
                    title,
                    fontWeight = FontWeight.SemiBold,
                    maxLines = 1,
                    overflow = TextOverflow.Ellipsis,
                    style = MaterialTheme.typography.body2,
                    modifier = Modifier.weight(1f),
                )
                if (item.isTimeblock) {
                    Text(
                        " · $timeblockLabel",
                        style = MaterialTheme.typography.caption,
                        color = MaterialTheme.colors.primary,
                        maxLines = 1,
                    )
                }
            }
            Text(
                groupLine ?: "—",
                style = MaterialTheme.typography.caption,
                maxLines = 1,
                overflow = TextOverflow.Ellipsis,
                modifier = Modifier.width(DiaryPlanColGroup),
            )
            Text(
                duration ?: "—",
                style = MaterialTheme.typography.caption,
                maxLines = 1,
                modifier = Modifier.width(DiaryPlanColDuration),
            )
            Icon(
                imageVector = if (isExpanded) Icons.Filled.ExpandLess else Icons.Filled.ExpandMore,
                contentDescription = tr("mobile.more", "Mehr"),
                modifier = Modifier.size(20.dp),
                tint = MaterialTheme.colors.onSurface.copy(alpha = 0.75f),
            )
        }
        if (isExpanded) {
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(start = DiaryPlanColStart, top = 3.dp, bottom = 3.dp, end = 4.dp)
                    .background(MaterialTheme.colors.onSurface.copy(alpha = 0.04f))
                    .padding(horizontal = 6.dp, vertical = 4.dp),
                verticalAlignment = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.spacedBy(4.dp),
            ) {
                DiaryPlanQuickTextAction(
                    label = tr("diary.planActionUp", "↑ Hoch"),
                    enabled = canMutate && canUp,
                    onClick = onMoveUp,
                )
                DiaryPlanQuickTextAction(
                    label = tr("diary.planActionDown", "↓ Runter"),
                    enabled = canMutate && canDown,
                    onClick = onMoveDown,
                )
                DiaryPlanQuickTextAction(
                    label = tr("diary.planAssignGroup", "Zuordnen"),
                    enabled = canMutate,
                    onClick = onAssignParticipants,
                )
                DiaryPlanQuickTextAction(
                    label = tr("diary.group", "Gruppe"),
                    enabled = canMutate,
                    onClick = onAssignGroup,
                )
                DiaryPlanQuickTextAction(
                    label = tr("common.edit", "Bearbeiten"),
                    enabled = canMutate,
                    onClick = onEdit,
                )
                if (!item.isTimeblock) {
                    DiaryPlanQuickTextAction(
                        label = tr("diary.activityDrawing", "Zeichnung"),
                        enabled = canMutate,
                        onClick = onOpenDrawing,
                    )
                }
                DiaryPlanQuickTextAction(
                    label = tr("common.delete", "Löschen"),
                    enabled = canMutate,
                    onClick = onDelete,
                )
            }
        }
        item.groupActivities
            .filter { visibleGroupId == null || it.groupId == visibleGroupId }
            .sortedBy { it.orderId ?: it.id ?: 0 }
            .forEach { ga ->
                val sub = ga.groupPredefinedActivity.displayLabel()
                val line = if (sub.isNotEmpty()) sub else "${tr("mobile.activityFallback", "Aktivität")} ${ga.id}"
                val nestedDuration = ga.durationText?.trim()?.takeIf { it.isNotEmpty() }
                val nestedDrawingRaw = ga.groupPredefinedActivity.drawingDataForDisplay() ?: ga.drawingData
                val nestedImageUrl = ga.id?.let(nestedImageUrls::get)
                val hasNestedVisual = nestedDrawingRaw != null || (canReadImages && nestedImageUrl != null)
                Row(
                    modifier = Modifier.fillMaxWidth().padding(start = DiaryPlanColStart, top = 0.dp, bottom = 0.dp),
                    verticalAlignment = Alignment.CenterVertically,
                ) {
                    if (hasNestedVisual) {
                        DiaryPlanQuickIconAction(
                            imageVector = Icons.Filled.Visibility,
                            contentDescription = tr("diary.showImage", "Bild/Zeichnung anzeigen"),
                            enabled = !planMutating,
                            onClick = {
                                if (nestedDrawingRaw != null) {
                                    onViewNestedDrawing(nestedDrawingRaw, line)
                                } else {
                                    nestedImageUrl?.let(onOpenImage)
                                }
                            },
                        )
                    }
                    Text(
                        "· $line",
                        style = MaterialTheme.typography.caption,
                        modifier = Modifier.weight(1f),
                        maxLines = 2,
                        overflow = TextOverflow.Ellipsis,
                    )
                    if (nestedDuration != null) {
                        Text(
                            nestedDuration,
                            style = MaterialTheme.typography.caption,
                            maxLines = 1,
                            overflow = TextOverflow.Ellipsis,
                            modifier = Modifier.width(DiaryPlanColDuration),
                        )
                    }
                    val nid = ga.id
                    if (nid != null) {
                        DiaryPlanQuickIconAction(
                            imageVector = Icons.Filled.Edit,
                            contentDescription = tr("common.edit", "Bearbeiten"),
                            enabled = canWriteDiary && !planMutating,
                            onClick = { onEditNested(ga) },
                        )
                        DiaryPlanQuickTextAction(
                            label = tr("diary.assignShort", "Zuordnen"),
                            enabled = canWriteDiary && !planMutating,
                            onClick = { onAssignNested(ga) },
                        )
                        DiaryPlanQuickTextAction(
                            label = tr("diary.activityDrawing", "Zeichnung"),
                            enabled = canWriteDiary && !planMutating,
                            onClick = { onOpenNestedDrawing(ga) },
                        )
                        DiaryPlanQuickIconAction(
                            imageVector = Icons.Filled.Delete,
                            contentDescription = tr("diary.planDeleteNested", "Gruppenübung löschen"),
                            enabled = canWriteDiary && !planMutating,
                            onClick = { onDeleteNested(nid) },
                        )
                    }
                }
            }
        Divider()
    }
}

@Composable
private fun SectionTitle(text: String) {
    Spacer(modifier = Modifier.height(16.dp))
    Divider()
    Text(text, style = MaterialTheme.typography.subtitle1, fontWeight = FontWeight.SemiBold, modifier = Modifier.padding(top = 8.dp))
}

@Composable
private fun DrawingChoiceRowForDiaryGender(selected: String, onSelected: (String) -> Unit) {
    Column {
        Text(tr("members.gender", "Geschlecht"), style = MaterialTheme.typography.caption)
        Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
            listOf("male" to "M", "female" to "W", "diverse" to "D").forEach { (value, label) ->
                OutlinedButton(onClick = { onSelected(value) }) {
                    Text(if (selected == value) "[$label]" else label)
                }
            }
        }
    }
}

@Composable
private fun DetailLine(label: String, value: String) {
    Row(modifier = Modifier.fillMaxWidth().padding(vertical = 3.dp)) {
        Text(label, modifier = Modifier.weight(1f), fontWeight = FontWeight.SemiBold)
        Text(value, modifier = Modifier.weight(1f))
    }
}

@Composable
private fun ErrorText(message: String?) {
    if (!message.isNullOrBlank()) {
        Text(message, color = MaterialTheme.colors.error, modifier = Modifier.padding(vertical = 8.dp))
    }
}

@Composable
private fun LoadingScreen(label: String) {
    Column(modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally) {
        CircularProgressIndicator()
        Text(label, modifier = Modifier.padding(top = 16.dp), style = MaterialTheme.typography.subtitle1)
        Text(
            tr("mobile.loadingHint", "Ein Moment bitte …"),
            style = MaterialTheme.typography.caption,
            color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f),
            modifier = Modifier.padding(top = 8.dp, start = 32.dp, end = 32.dp),
        )
    }
}

@Composable
private fun LoadingInline() {
    Row(modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp), horizontalArrangement = Arrangement.Center) {
        CircularProgressIndicator()
    }
}

@Composable
private fun EmptyText(text: String) {
    Text(text, modifier = Modifier.padding(vertical = 16.dp))
}

@Composable
private fun FlowRowText(items: List<String>) {
    if (items.isEmpty()) {
        Text("-", modifier = Modifier.padding(vertical = 4.dp))
    } else {
        Text(items.joinToString(", "), modifier = Modifier.padding(vertical = 4.dp))
    }
}

private const val MEMBER_CONTACT_EMPTY = "–"

private fun formatMemberPhonesLine(member: Member): String {
    val phoneContacts = member.contacts
        .filter { it.type == "phone" && it.value.isNotBlank() }
        .sortedWith(compareBy<MemberContactDto> { !it.isPrimary }.thenBy { it.id ?: 0 })
    val pieces = phoneContacts.map { c ->
        val base = c.value.trim()
        if (c.isParent) {
            val pName = c.parentName
            val label = when {
                !pName.isNullOrBlank() -> pName.trim()
                else -> "Eltern"
            }
            "$base ($label)"
        } else {
            base
        }
    }.toMutableList()
    val legacyPhone = member.phone
    if (pieces.isEmpty() && !legacyPhone.isNullOrBlank()) {
        pieces.add(legacyPhone.trim())
    }
    return if (pieces.isEmpty()) MEMBER_CONTACT_EMPTY else pieces.joinToString(", ")
}

private fun formatMemberEmailsLine(member: Member): String {
    val emailContacts = member.contacts
        .filter { it.type == "email" && it.value.isNotBlank() }
        .sortedWith(compareBy<MemberContactDto> { !it.isPrimary }.thenBy { it.id ?: 0 })
    val pieces = emailContacts.map { c ->
        val base = c.value.trim()
        if (c.isParent) {
            val pName = c.parentName
            val label = when {
                !pName.isNullOrBlank() -> pName.trim()
                else -> "Eltern"
            }
            "$base ($label)"
        } else {
            base
        }
    }.toMutableList()
    val legacyEmail = member.email
    if (pieces.isEmpty() && !legacyEmail.isNullOrBlank()) {
        pieces.add(legacyEmail.trim())
    }
    return if (pieces.isEmpty()) MEMBER_CONTACT_EMPTY else pieces.joinToString(", ")
}

private fun memberHasFormattedPhones(member: Member): Boolean =
    formatMemberPhonesLine(member) != MEMBER_CONTACT_EMPTY

private fun memberHasFormattedEmails(member: Member): Boolean =
    formatMemberEmailsLine(member) != MEMBER_CONTACT_EMPTY

private fun extractEmailAddressesFromMember(member: Member): List<String> {
    val raw = formatMemberEmailsLine(member)
    if (raw == MEMBER_CONTACT_EMPTY) return emptyList()
    return raw.split(",").mapNotNull { part ->
        val trimmed = part.trim()
        if (trimmed.isEmpty()) return@mapNotNull null
        val withoutParen = trimmed.replace(Regex("\\s*\\([^)]*\\)\\s*$"), "").trim()
        withoutParen.takeIf { it.contains("@") }
    }.distinct()
}

private fun memberHasValidLastTrainingDate(member: Member): Boolean {
    val raw = member.lastTraining?.trim().orEmpty()
    if (raw.isEmpty()) return false
    val day = raw.take(10)
    return runCatching { java.time.LocalDate.parse(day) }.isSuccess
}

private fun memberDataQualityIssues(member: Member, requirements: MemberDataQualityRequirements): List<String> {
    val issues = mutableListOf<String>()
    val hasTrainingGroup = member.trainingGroups.isNotEmpty()
    if (member.birthDate.isNullOrBlank()) {
        issues.add("birthdate")
    }
    if (requirements.requirePhone && !memberHasFormattedPhones(member)) issues.add("phone")
    if (requirements.requireEmail && !memberHasFormattedEmails(member)) issues.add("email")
    if (requirements.requireStreet && member.street.isNullOrBlank()) issues.add("street")
    if (requirements.requirePostalCode && member.postalCode.isNullOrBlank()) issues.add("postal")
    if (requirements.requireCity && member.city.isNullOrBlank()) issues.add("city")
    if (member.gender.isNullOrBlank() || member.gender == "unknown") issues.add("gender")
    if (!hasTrainingGroup) issues.add("training-group")
    return issues
}

private fun memberOpenTasksCount(member: Member, requirements: MemberDataQualityRequirements): Int {
    var n = 0
    if (member.memberFormHandedOver != true) n++
    if (member.testMembership == true) n++
    if (member.notInTraining == true) n++
    if (memberDataQualityIssues(member, requirements).isNotEmpty()) n++
    if (member.trainingGroups.isEmpty()) n++
    if (member.active && member.ttr == null && member.qttr == null) n++
    return n
}

private fun buildMembersCsvExport(rows: List<Member>): String {
    fun esc(s: String) = s.replace(";", ",")
    val header = "Name;Status;TTR;QTTR;E-Mail;Telefon;Letztes Training;Teilnahmen"
    val lines = rows.map { m ->
        listOf(
            esc(m.fullName()),
            esc(
                when {
                    !m.active -> "inaktiv"
                    m.testMembership == true -> "test"
                    else -> "aktiv"
                },
            ),
            m.ttr?.toString().orEmpty(),
            m.qttr?.toString().orEmpty(),
            esc(formatMemberEmailsLine(m).let { if (it == MEMBER_CONTACT_EMPTY) "" else it }),
            esc(formatMemberPhonesLine(m).let { if (it == MEMBER_CONTACT_EMPTY) "" else it }),
            esc(m.lastTraining.orEmpty()),
            (m.trainingParticipations ?: 0).toString(),
        ).joinToString(";")
    }
    return (listOf(header) + lines).joinToString("\n")
}

private fun Member.fullName(): String = listOf(firstName, lastName).filter { it.isNotBlank() }.joinToString(" ").ifBlank { "Mitglied $id" }

private fun formatDate(value: String): String = formatIsoDateForDisplay(value, fallback = value.take(10))

private fun formatTimeRange(start: String?, end: String?): String {
    val parts = listOfNotNull(start?.takeIf { it.isNotBlank() }, end?.takeIf { it.isNotBlank() })
    return if (parts.isEmpty()) "Keine Zeiten" else parts.joinToString(" - ")
}

/** Anzeige wie HTML time (HH:mm); API erwartet oft HH:mm:ss. */
private fun diaryTimeForFormField(apiTime: String?): String {
    val t = apiTime?.trim().orEmpty()
    if (t.isEmpty()) return ""
    return t.take(5)
}

private fun diaryTimeFieldToApi(value: String): String? {
    val t = value.trim()
    if (t.isEmpty()) return null
    return when {
        t.matches(Regex("""\d{2}:\d{2}:\d{2}""")) -> t
        t.matches(Regex("""\d{2}:\d{2}""")) -> "$t:00"
        else -> t
    }
}

/** Nächster freier Trainingstag aus [TrainingTimeDto] (Wochentag 0=So … 6=Sa, wie Web/Kalender). */
private data class NextDiarySlotSuggestion(
    val date: String,
    val trainingStart: String,
    val trainingEnd: String,
)

private fun localDateToJsWeekday(d: java.time.LocalDate): Int =
    when (d.dayOfWeek) {
        java.time.DayOfWeek.SUNDAY -> 0
        java.time.DayOfWeek.MONDAY -> 1
        java.time.DayOfWeek.TUESDAY -> 2
        java.time.DayOfWeek.WEDNESDAY -> 3
        java.time.DayOfWeek.THURSDAY -> 4
        java.time.DayOfWeek.FRIDAY -> 5
        java.time.DayOfWeek.SATURDAY -> 6
    }

private fun trainingTimeStartToMinutes(start: String): Int? {
    val t = start.trim().take(5)
    val parts = t.split(':')
    if (parts.size < 2) return null
    val h = parts[0].toIntOrNull() ?: return null
    val m = parts[1].toIntOrNull() ?: return null
    return h * 60 + m
}

/** Nächster freier Kalendertag: früheste Startzeit über alle nicht ausgeschlossenen Gruppen an diesem Wochentag (0=So … 6=Sa). */
private fun findNextQuickDiarySlotAcrossGroups(
    groups: List<TrainingGroupDto>,
    existingDiaryDatesYyyyMmDd: Set<String>,
): NextDiarySlotSuggestion? {
    val eligible = groups.filter { !it.excludeFromQuickDiaryCreate }
    val today = java.time.LocalDate.now()
    for (offset in 0..366) {
        val check = today.plusDays(offset.toLong())
        val wd = localDateToJsWeekday(check)
        val norm = check.toString().take(10)
        if (norm in existingDiaryDatesYyyyMmDd) continue
        data class SlotCand(
            val startMin: Int,
            val suggestion: NextDiarySlotSuggestion,
            val groupOrder: Int,
            val groupId: Int,
            val timeId: Int,
        )
        val cands = buildList {
            for (g in eligible) {
                for (time in g.trainingTimes) {
                    if (time.weekday != wd || time.startTime.isBlank()) continue
                    val sm = trainingTimeStartToMinutes(time.startTime) ?: continue
                    add(
                        SlotCand(
                            startMin = sm,
                            suggestion = NextDiarySlotSuggestion(
                                date = norm,
                                trainingStart = time.startTime.trim().take(5),
                                trainingEnd = time.endTime.trim().take(5),
                            ),
                            groupOrder = g.sortOrder,
                            groupId = g.id,
                            timeId = time.id,
                        ),
                    )
                }
            }
        }
        if (cands.isEmpty()) continue
        val best = cands.minWith(
            compareBy<SlotCand> { it.startMin }
                .thenBy { it.groupOrder }
                .thenBy { it.groupId }
                .thenBy { it.timeId },
        )
        return best.suggestion
    }
    return null
}

private fun nextDiarySlotFromTrainingTimes(
    trainingTimes: List<TrainingTimeDto>,
    existingDiaryDatesYyyyMmDd: Set<String>,
): NextDiarySlotSuggestion? {
    if (trainingTimes.isEmpty()) return null
    val sorted = trainingTimes.sortedWith(
        compareBy<TrainingTimeDto> { it.weekday }.thenBy { it.startTime }.thenBy { it.id },
    )
    val today = java.time.LocalDate.now()
    for (offset in 0..13) {
        val check = today.plusDays(offset.toLong())
        val wd = localDateToJsWeekday(check)
        val timesForDay = sorted.filter { it.weekday == wd }
        if (timesForDay.isEmpty()) continue
        val time = timesForDay.first()
        val norm = check.toString().take(10)
        if (norm !in existingDiaryDatesYyyyMmDd) {
            return NextDiarySlotSuggestion(
                date = norm,
                trainingStart = time.startTime.trim().take(5),
                trainingEnd = time.endTime.trim().take(5),
            )
        }
    }
    return null
}

@Composable
private fun tr(key: String, fallback: String): String = MobileStrings.get(LocalLanguageCode.current, key, fallback)

@Composable
private fun tabTitle(tab: MainTab): String = when (tab) {
    MainTab.Home -> tr("navigation.home", "Start")
    MainTab.Diary -> tr("navigation.diary", "Tagebuch")
    MainTab.Members -> tr("navigation.members", "Mitglieder")
    MainTab.Schedule -> tr("navigation.schedule", "Terminplan")
    MainTab.FriendlyMatches -> "Freundschaftsspiele"
    MainTab.Calendar -> tr("navigation.calendar", "Kalender")
    MainTab.Tournaments -> tr("navigation.clubTournaments", "Turniere")
    MainTab.OfficialParticipations -> tr("navigation.tournamentParticipations", "Turnierteilnahmen")
    MainTab.Stats -> tr("navigation.statistics", "Statistik")
    MainTab.Settings -> tr("mobile.more", "Mehr")
}
