package com.tsmobile.app.ui.screens

import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.*
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.tsmobile.app.data.ChannelInfo
import com.tsmobile.app.data.ClientInfo
import com.tsmobile.app.data.ConnectionState
import com.tsmobile.app.ui.components.*
import com.tsmobile.app.ui.theme.ThemeMode
import com.tsmobile.app.ui.theme.UiTokens
import com.tsmobile.app.ui.theme.semanticColors
import com.tsmobile.app.viewmodel.*

// 📐 各区域布局参数（分别控制左/右边距）
// 中部：频道行两端对齐
private val TREE_LEFT_INDENT = 24.5.dp
private val TREE_RIGHT_INDENT = 24.5.dp
// 中部：成员行两端对齐（独立于频道行）
private val CLIENT_LEFT_INDENT = 0.dp
private val CLIENT_RIGHT_INDENT = 24.5.dp
// 底部：当前频道栏、语音控制栏两端对齐
private val FOOTER_LEFT_INDENT = 14.5.dp
private val FOOTER_RIGHT_INDENT = 14.5.dp

/** 去除 spacer 前缀和多余的破折号：`[spacer19]--- xxx ---` → `xxx` */
private fun formatChannelName(name: String): String {
    return name
        .replace(Regex("^\\[spacer\\d+]"), "")  // 去掉 [spacer19] 前缀
        .replace(Regex("^[-—]+\\s*"), "")         // 去掉开头的破折号
        .replace(Regex("\\s*[-—]+$"), "")         // 去掉结尾的破折号
        .trim()
}

/** 判断是否是 spacer 分隔频道 */
private fun isSpacerChannel(name: String): Boolean {
    return Regex("^\\[spacer\\d+]").containsMatchIn(name)
}

/**
 * 频道列表页。
 * 三段式布局：头部 + 频道树 + 当前频道栏。
 */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ChannelListScreen(
    channelViewModel: ChannelViewModel,
    voiceViewModel: VoiceViewModel,
    serverViewModel: ServerViewModel,
    onNavigateToChat: () -> Unit,
    onNavigateToServerConfig: () -> Unit,
) {
    val context = LocalContext.current
    val syncState by channelViewModel.syncState.collectAsState()
    val switchState by channelViewModel.switchState.collectAsState()
    val showSwitchDialog by channelViewModel.showSwitchDialog.collectAsState()
    val pendingChannel by channelViewModel.pendingSwitchChannel.collectAsState()
    val connectionState by serverViewModel.connectionState.collectAsState()

    // 卡片状态
    val showVoiceCard by voiceViewModel.showVoiceCard.collectAsState()
    val showChannelDetailCard by channelViewModel.showChannelDetailCard.collectAsState()
    val channelDetail by channelViewModel.channelDetailInfo.collectAsState()
    val currentChannelId by channelViewModel.currentChannelId.collectAsState()

    // ViewModel 重建保护：如果 syncState 不是 Synchronized（ViewModel 被系统回收后重建），
    // 重新触发首次同步，否则频道列表页会永远停在加载态。
    // 仅在连接已建立或正在重连时才同步，避免断联后无用的同步尝试。
    LaunchedEffect(Unit) {
        android.util.Log.i("ChannelListScreen", "Screen appeared: syncState=$syncState, " +
            "channels=${channelViewModel.channels.value.size}, clients=${channelViewModel.clients.value.size}, " +
            "connectionState=$connectionState")
        val canSync = connectionState is ConnectionState.Connected
        if (syncState !is SyncState.Synchronized && canSync) {
            android.util.Log.w("ChannelListScreen", "ViewModel was recreated (syncState=$syncState), re-triggering sync")
            channelViewModel.performInitialSyncWithRetry()
        } else if (syncState !is SyncState.Synchronized) {
            android.util.Log.w("ChannelListScreen", "Not connected (connectionState=$connectionState), skipping futile sync")
        }
    }

    // 成员操作菜单状态
    var selectedClient by remember { mutableStateOf<ClientInfo?>(null) }
    var showClientMenu by remember { mutableStateOf(false) }
    var showServerDetail by remember { mutableStateOf(false) }

    // Poke 对话框目标（独立于 selectedClient，避免二级菜单关闭时被清空）
    var pokeTarget by remember { mutableStateOf<Pair<Int, String>?>(null) } // (clientId, nickname)

    // 防抖：服务器详情按钮上次点击时间
    var lastServerDetailClickTime by remember { mutableLongStateOf(0L) }

    // 服务器状态（提前声明，供侧栏 drawerContent 读取）
    val serverInfo by serverViewModel.serverInfo.collectAsState()
    val serverState by serverViewModel.state.collectAsState()
    val themeMode by serverViewModel.themeMode.collectAsState()

    // 侧栏抽屉状态
    val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)

    // 同步 showServerDetail 与抽屉开合
    LaunchedEffect(showServerDetail) {
        if (showServerDetail) drawerState.open() else drawerState.close()
    }
    // 手势滑动打开时同步状态并加载数据
    LaunchedEffect(drawerState.isOpen) {
        if (drawerState.isOpen && !showServerDetail) {
            serverViewModel.fetchServerInfo()
            showServerDetail = true
        }
    }
    // 手势滑动关闭或编程关闭后同步状态
    LaunchedEffect(drawerState.isClosed) {
        if (drawerState.isClosed && showServerDetail) {
            showServerDetail = false
        }
    }

    ModalNavigationDrawer(
        drawerState = drawerState,
        drawerContent = {
            ServerDetailCard(
                serverInfo = serverInfo,
                serverName = serverInfo?.name ?: "",
                serverAddress = serverState.address,
                connectionState = connectionState,
                onDisconnect = {
                    showServerDetail = false
                    serverViewModel.disconnect()
                    onNavigateToServerConfig()
                },
                onDismiss = { showServerDetail = false },
            )
        },
    ) {
        Box(modifier = Modifier.fillMaxSize().systemBarsPadding()) {
        Column(modifier = Modifier.fillMaxSize()) {
            // 头部
            ChannelListHeader(
                connectionState = connectionState,
                serverName = serverInfo?.name ?: "",
                serverAddress = serverState.address,
                onlineCount = serverInfo?.clientsOnline ?: 0,
                themeMode = themeMode,
                onShowServerDetail = {
                    val now = System.currentTimeMillis()
                    if (now - lastServerDetailClickTime >= 500) {
                        lastServerDetailClickTime = now
                        serverViewModel.fetchServerInfo()
                        showServerDetail = true
                    }
                },
                onToggleTheme = { serverViewModel.toggleTheme() },
            )


            // 切换状态指示器
            if (switchState != ChannelSwitchState.Idle) {
                ChannelSwitchingIndicator(
                    state = switchState,
                    onRetry = { channelViewModel.retryChannelSwitch() },
                    onCancel = { channelViewModel.cancelChannelSwitch() },
                )
            }

            // 中部：频道树或同步加载态
            Box(modifier = Modifier.weight(1f)) {
                when (syncState) {
                    SyncState.Unsynced, SyncState.Syncing -> SyncLoadingIndicator()
                    is SyncState.SyncFailed -> SyncFailedView(
                        onRetry = { channelViewModel.retrySync() },
                    )
                    SyncState.Synchronized -> ChannelTreeContent(
                        channelViewModel = channelViewModel,
                        onChannelClick = { channel ->
                            channelViewModel.onChannelClicked(channel)
                        },
                        onChannelLongClick = { channel ->
                            channelViewModel.openChannelDetailCard(channel.id)
                        },
                        onClientClick = { client ->
                            selectedClient = client
                            showClientMenu = true
                        },
                    )
                }
            }

            // 中部-底部：当前频道栏
            CurrentChannelBar(
                channelViewModel = channelViewModel,
                onClick = onNavigateToChat,
            )

            // 底部：语音控制栏
            VoiceControlBar(
                voiceViewModel = voiceViewModel,
                onExpand = { voiceViewModel.openVoiceCard() },
                leftIndent = FOOTER_LEFT_INDENT,
                rightIndent = FOOTER_RIGHT_INDENT,
            )
        }

    }
    } // ModalNavigationDrawer

    // ─── 弹窗层 ───

    // 语音卡弹窗（BottomSheet）
    if (showVoiceCard) {
        ModalBottomSheet(
            onDismissRequest = { voiceViewModel.closeVoiceCard() },
        ) {
            VoiceCard(
                voiceViewModel = voiceViewModel,
                channelViewModel = channelViewModel,
                onDismiss = { voiceViewModel.closeVoiceCard() },
                onPokeClient = { clientId, message ->
                    serverViewModel.pokeClient(clientId, message)
                },
            )
        }
    }

    // 频道详情卡弹窗（BottomSheet）
    if (showChannelDetailCard) {
        ModalBottomSheet(
            onDismissRequest = { channelViewModel.closeChannelDetailCard() },
        ) {
            ChannelDetailCard(
                channelDetail = channelDetail,
                isCurrentChannel = channelDetail?.id == currentChannelId,
                onSwitchChannel = {
                    channelViewModel.switchToDetailChannel()
                },
                onDismiss = { channelViewModel.closeChannelDetailCard() },
            )
        }
    }

    // 成员操作菜单
    if (showClientMenu && selectedClient != null) {
        ClientActionMenu(
            client = selectedClient!!,
            isSelf = selectedClient!!.id == channelViewModel.selfClientId.collectAsState().value,
            onPoke = {
                pokeTarget = selectedClient!!.id to selectedClient!!.nickname
                showClientMenu = false
                selectedClient = null
            },
            onCopyNickname = {
                copyToClipboard(context, selectedClient!!.nickname)
            },
            onCopyUID = {
                copyToClipboard(context, selectedClient!!.uid)
            },
            onKickFromServer = {
                serverViewModel.kickClient(selectedClient!!.id, 5, "")
            },
            onKickFromChannel = {
                serverViewModel.kickClient(selectedClient!!.id, 4, "")
            },
            onDismiss = {
                showClientMenu = false
                selectedClient = null
            },
        )
    }

    // Poke 弹窗（成员列表长按）
    pokeTarget?.let { (targetId, targetName) ->
        PokeDialog(
            targetName = targetName,
            onConfirm = { message ->
                serverViewModel.pokeClient(targetId, message)
                pokeTarget = null
            },
            onDismiss = {
                pokeTarget = null
            },
        )
    }

    // Poke 弹窗（语音卡成员长按）已经在 VoiceCard 内部处理

    // 频道切换确认弹窗（有密码时带输入框）
    if (showSwitchDialog && pendingChannel != null) {
        ChannelConfirmDialog(
            channelName = pendingChannel!!.name,
            isPassword = pendingChannel!!.isPassword,
            onConfirm = { password ->
                channelViewModel.confirmSwitchDialog(password)
            },
            onDismiss = {
                channelViewModel.dismissSwitchDialog()
            },
            isError = switchState is ChannelSwitchState.Failed,
            errorMessage = (switchState as? ChannelSwitchState.Failed)?.error ?: "密码错误，请重试",
        )
    }
}

private fun copyToClipboard(context: Context, text: String) {
    val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    val clip = ClipData.newPlainText("text", text)
    clipboard.setPrimaryClip(clip)
}

/**
 * 频道切换状态指示器。
 */
@Composable
private fun ChannelSwitchingIndicator(
    state: ChannelSwitchState,
    onRetry: () -> Unit,
    onCancel: () -> Unit,
) {
    when (state) {
        is ChannelSwitchState.Requesting -> {
            LinearProgressIndicator(
                modifier = Modifier.fillMaxWidth(),
            )
        }
        is ChannelSwitchState.WaitingServerEvent -> {
            LinearProgressIndicator(
                modifier = Modifier.fillMaxWidth(),
            )
        }
        is ChannelSwitchState.Failed -> {
            Snackbar(
                // 📐 调整切换失败提示对齐：贴边设为 0
                modifier = Modifier,
                action = {
                    Row {
                        TextButton(onClick = onRetry) {
                            Text("重试")
                        }
                        TextButton(onClick = onCancel) {
                            Text("取消")
                        }
                    }
                },
            ) {
                Text(state.error)
            }
        }
        else -> { /* Idle: 不显示任何指示 */ }
    }
}

/**
 * 头部组件。
 *
 * 与 ChatHeader 统一样式：
 * 左侧 = IconButton（紧贴边缘）
 * 中部 = Column(weight(1f), centerHorizontally)
 * 右侧 = IconButton（紧贴边缘）
 */
@Composable
private fun ChannelListHeader(
    connectionState: ConnectionState?,
    serverName: String,
    serverAddress: String,
    onlineCount: Int,
    themeMode: ThemeMode,
    onShowServerDetail: () -> Unit,
    onToggleTheme: () -> Unit,
) {
    AppTopBar(
        title = serverName.ifEmpty {
            if (connectionState is ConnectionState.Connected) serverAddress else "未连接"
        },
        subtitle = listOfNotNull(
            serverAddress.takeIf { it.isNotEmpty() },
            "${onlineCount}人在线",
        ).joinToString(" · "),
        navigationIcon = Icons.Default.Menu,
        navigationContentDescription = "服务器详情",
        onNavigationClick = onShowServerDetail,
        actionIcon = when (themeMode) {
            ThemeMode.LIGHT -> Icons.Default.LightMode
            ThemeMode.DARK -> Icons.Default.DarkMode
            ThemeMode.SYSTEM -> Icons.Default.SettingsBrightness
        },
        actionContentDescription = "切换主题",
        onActionClick = onToggleTheme,
    )
}

/**
 * 同步加载态组件。
 */
@Composable
private fun SyncLoadingIndicator() {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center,
    ) {
        Column(horizontalAlignment = Alignment.CenterHorizontally) {
            CircularProgressIndicator()
            Spacer(modifier = Modifier.height(16.dp))
            Text("正在同步服务器数据...")
        }
    }
}

/**
 * 同步失败组件。
 */
@Composable
private fun SyncFailedView(onRetry: () -> Unit) {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center,
    ) {
        Column(horizontalAlignment = Alignment.CenterHorizontally) {
            Text("同步失败", color = MaterialTheme.colorScheme.error)
            Spacer(modifier = Modifier.height(8.dp))
            Button(onClick = onRetry) {
                Text("重试")
            }
        }
    }
}

/**
 * 频道树内容。
 *
 * 直接从 Repository 源数据计算频道树，不依赖 ViewModel 的 stateIn StateFlow，
 * 避免 stateIn 初始空值导致的白屏问题。
 */
@Composable
private fun ChannelTreeContent(
    channelViewModel: ChannelViewModel,
    onChannelClick: (ChannelInfo) -> Unit,
    onChannelLongClick: (ChannelInfo) -> Unit,
    onClientClick: (ClientInfo) -> Unit,
) {
    val channels by channelViewModel.channels.collectAsState()
    val clients by channelViewModel.clients.collectAsState()
    val expandedChannelIds by channelViewModel.expandedChannelIds.collectAsState()
    val selfClientId by channelViewModel.selfClientId.collectAsState()

    // 直接从源数据计算频道树，不经过 stateIn，无初始空值时序问题
    val tree = remember(channels, clients, expandedChannelIds) {
        channelViewModel.buildChannelTree(channels, clients, expandedChannelIds)
    }

    LazyColumn(modifier = Modifier.fillMaxSize()) {
        tree.forEach { node ->
            channelTreeNodeItems(
                node = node,
                depth = 0,
                selfClientId = selfClientId,
                onChannelClick = onChannelClick,
                onChannelLongClick = onChannelLongClick,
                onClientClick = onClientClick,
            )
        }
    }
}

/**
 * 递归添加 LazyColumn 项。
 * 子频道默认展开且不支持收缩；spacer 频道渲染为分隔线。
 */
private fun LazyListScope.channelTreeNodeItems(
    node: ChannelTreeNode,
    depth: Int,
    selfClientId: Int,
    onChannelClick: (ChannelInfo) -> Unit,
    onChannelLongClick: (ChannelInfo) -> Unit,
    onClientClick: (ClientInfo) -> Unit,
) {
    item(key = "channel_${node.channel.id}") {
        if (isSpacerChannel(node.channel.name)) {
            // spacer 频道 → 显示标题文字或纯分隔线
            val label = formatChannelName(node.channel.name)
            ChannelSeparator(label = label)
        } else {
            ChannelRow(
                node = node,
                depth = depth,
                onClick = { onChannelClick(node.channel) },
                onLongClick = { onChannelLongClick(node.channel) },
            )
        }
    }

    // 始终显示成员和子频道（不依赖展开状态）
    node.clients.forEach { client ->
        item(key = "client_${client.id}") {
            ClientRow(
                client = client,
                depth = depth + 1,
                isSelf = client.id == selfClientId,
                onClick = { onClientClick(client) },
            )
        }
    }

    node.children.forEach { child ->
        channelTreeNodeItems(
            node = child,
            depth = depth + 1,
            selfClientId = selfClientId,
            onChannelClick = onChannelClick,
            onChannelLongClick = onChannelLongClick,
            onClientClick = onClientClick,
        )
    }
}

/**
 * 频道行组件。
 *
 * 对应 docs/UI架构设计.md 2.2 频道树交互：
 * - 点击频道名 → 切换频道（或弹密码框）
 * - 长按频道 → 弹出频道详情卡
 * - 子频道默认展开且不支持收缩
 */
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun ChannelRow(
    node: ChannelTreeNode,
    depth: Int,
    onClick: () -> Unit,
    onLongClick: () -> Unit,
) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .combinedClickable(onClick = onClick, onLongClick = onLongClick)
            .padding(vertical = 12.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        // 📐 左侧内容缩进：修改 TREE_LEFT_INDENT
        Spacer(modifier = Modifier.width(TREE_LEFT_INDENT + (depth * 24).dp))
        Icon(
            imageVector = Icons.Default.Folder,
            contentDescription = null,
            tint = MaterialTheme.colorScheme.primary,
            modifier = Modifier.size(24.dp),
        )

        Spacer(modifier = Modifier.width(8.dp))

        // 频道名
        Text(
            text = node.channel.name,
            style = MaterialTheme.typography.bodyLarge,
            modifier = Modifier.weight(1f),
        )

        // 状态图标
        if (node.channel.isPassword) {
            Icon(
                Icons.Default.Lock,
                contentDescription = "有密码",
                modifier = Modifier.size(16.dp),
            )
        }

        // 成员人数
        if (node.clients.isNotEmpty()) {
            Text(
                text = "(${node.clients.size}人)",
                style = MaterialTheme.typography.bodySmall,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
            )
        }
        // 📐 右侧内容缩进：修改 TREE_RIGHT_INDENT
        Spacer(modifier = Modifier.width(TREE_RIGHT_INDENT))
    }
}

/**
 * 频道分隔线组件。
 * 用于 spacer 频道，显示标题文字或纯分隔线。
 */
@Composable
private fun ChannelSeparator(label: String) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            // 📐 调整分隔线内容对齐：贴边设为 0
            .padding(vertical = 4.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        HorizontalDivider(modifier = Modifier.weight(1f))
        Text(
            text = label,
            style = MaterialTheme.typography.labelMedium,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
        )
        HorizontalDivider(modifier = Modifier.weight(1f))
    }
}

/**
 * 成员行组件。
 */
@Composable
private fun ClientRow(
    client: ClientInfo,
    depth: Int,
    isSelf: Boolean,
    onClick: () -> Unit,
) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .clickable(onClick = onClick)
            .padding(vertical = 12.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        // 📐 左侧内容缩进：修改 CLIENT_LEFT_INDENT
        Spacer(modifier = Modifier.width(CLIENT_LEFT_INDENT + (depth * 24).dp))
        Spacer(modifier = Modifier.width(24.dp)) // 对齐频道行的频道图标

        // 用户图标
        Icon(
            imageVector = Icons.Default.Person,
            contentDescription = null,
            tint = if (isSelf) MaterialTheme.colorScheme.primary
            else MaterialTheme.colorScheme.onSurfaceVariant,
        )

        Spacer(modifier = Modifier.width(8.dp))

        // 昵称
        Text(
            text = client.nickname,
            style = MaterialTheme.typography.bodyMedium,
            fontWeight = if (isSelf) FontWeight.Bold else FontWeight.Normal,
            modifier = Modifier.weight(1f),
        )

        // 服务器组图标（可选）
        if (client.serverGroups.isNotEmpty()) {
            Icon(
                imageVector = Icons.Default.Shield,
                contentDescription = "管理员",
                modifier = Modifier.size(16.dp),
                tint = MaterialTheme.colorScheme.tertiary,
            )
        }
        // 📐 右侧内容缩进：修改 CLIENT_RIGHT_INDENT
        Spacer(modifier = Modifier.width(CLIENT_RIGHT_INDENT))
    }
}

/**
 * 当前频道栏。
 */
@Composable
private fun CurrentChannelBar(
    channelViewModel: ChannelViewModel,
    onClick: () -> Unit,
) {
    val currentChannelId by channelViewModel.currentChannelId.collectAsState()
    val channels by channelViewModel.channels.collectAsState()
    val channelClients by channelViewModel.channelClients.collectAsState()

    val currentChannel = channels.find { it.id == currentChannelId }
    val clients = channelClients[currentChannelId] ?: emptyList()

    if (currentChannel != null) {
        val selfClientId by channelViewModel.selfClientId.collectAsState()
        val previewCandidates = clients.sortedByDescending { it.id == selfClientId }
        val semanticColors = MaterialTheme.semanticColors

        Column(
            modifier = Modifier
                .fillMaxWidth()
                .height(68.dp)
                .clickable(onClick = onClick)
                .background(MaterialTheme.colorScheme.surfaceVariant)
                .padding(
                    start = FOOTER_LEFT_INDENT,
                    end = FOOTER_RIGHT_INDENT,
                    top = UiTokens.Spacing.ExtraSmall,
                    bottom = UiTokens.Spacing.ExtraSmall,
                ),
            verticalArrangement = Arrangement.spacedBy(UiTokens.Spacing.ExtraSmall),
        ) {
            Row(
                modifier = Modifier.fillMaxWidth(),
                verticalAlignment = Alignment.CenterVertically,
            ) {
                Icon(
                    Icons.Default.LocationOn,
                    contentDescription = null,
                    tint = MaterialTheme.colorScheme.primary,
                    modifier = Modifier.size(UiTokens.Size.IconMedium),
                )
                Spacer(Modifier.width(UiTokens.Spacing.Small))
                Text(
                    text = currentChannel.name,
                    style = MaterialTheme.typography.bodyMedium,
                    fontWeight = FontWeight.Medium,
                    maxLines = 1,
                    overflow = TextOverflow.Ellipsis,
                    modifier = Modifier.weight(1f),
                )
                if (currentChannel.isPassword) {
                    Spacer(Modifier.width(UiTokens.Spacing.ExtraSmall))
                    Icon(
                        Icons.Default.Lock,
                        contentDescription = "有密码",
                        modifier = Modifier.size(UiTokens.Size.IconSmall),
                        tint = MaterialTheme.colorScheme.onSurfaceVariant,
                    )
                }
                Spacer(Modifier.width(UiTokens.Spacing.Small))
                Surface(
                    shape = MaterialTheme.shapes.small,
                    color = MaterialTheme.colorScheme.secondaryContainer,
                    contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
                ) {
                    Text(
                        text = if (currentChannel.maxClients >= 0) {
                            "${clients.size}/${currentChannel.maxClients}"
                        } else {
                            "${clients.size}"
                        },
                        style = MaterialTheme.typography.labelSmall,
                        modifier = Modifier.padding(horizontal = UiTokens.Spacing.Small, vertical = UiTokens.Spacing.ExtraSmall),
                    )
                }
            }

            BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
                val maxPreviewCount = when {
                    maxWidth >= 380.dp -> 5
                    maxWidth >= 320.dp -> 4
                    else -> 3
                }
                val visibleClients = previewCandidates.take(maxPreviewCount)
                val hiddenClientCount = clients.size - visibleClients.size

                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.spacedBy(UiTokens.Spacing.ExtraSmall),
                    verticalAlignment = Alignment.CenterVertically,
                ) {
                    visibleClients.forEach { client ->
                        val isSelf = client.id == selfClientId
                        val isGrouped = client.serverGroups.isNotEmpty()
                        MemberPreviewTag(
                            name = client.nickname,
                            containerColor = when {
                                isSelf -> MaterialTheme.colorScheme.primaryContainer
                                isGrouped -> MaterialTheme.colorScheme.tertiaryContainer
                                else -> MaterialTheme.colorScheme.surfaceContainerHighest
                            },
                            contentColor = when {
                                isSelf -> MaterialTheme.colorScheme.onPrimaryContainer
                                isGrouped -> MaterialTheme.colorScheme.onTertiaryContainer
                                else -> MaterialTheme.colorScheme.onSurfaceVariant
                            },
                            showPresence = !isSelf && !isGrouped,
                            presenceColor = semanticColors.success,
                            modifier = Modifier.weight(1f, fill = false),
                        )
                    }
                    if (hiddenClientCount > 0) {
                        Text(
                            text = "+$hiddenClientCount",
                            style = MaterialTheme.typography.labelSmall,
                            color = MaterialTheme.colorScheme.onSurfaceVariant,
                            maxLines = 1,
                        )
                    }
                }
            }
        }
    }
}

@Composable
private fun MemberPreviewTag(
    name: String,
    containerColor: Color,
    contentColor: Color,
    showPresence: Boolean = false,
    presenceColor: Color = Color.Unspecified,
    modifier: Modifier = Modifier,
) {
    Surface(
        modifier = modifier,
        shape = MaterialTheme.shapes.small,
        color = containerColor,
        contentColor = contentColor,
    ) {
        Row(
            modifier = Modifier
                .widthIn(max = 96.dp)
                .height(24.dp)
                .padding(horizontal = UiTokens.Spacing.Small),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            if (showPresence) {
                Box(
                    modifier = Modifier
                        .size(6.dp)
                        .background(presenceColor, CircleShape),
                )
                Spacer(Modifier.width(UiTokens.Spacing.ExtraSmall))
            }
            Text(
                text = name,
                style = MaterialTheme.typography.labelSmall,
                maxLines = 1,
                overflow = TextOverflow.Ellipsis,
                modifier = Modifier.weight(1f, fill = false),
            )
        }
    }
}
