package ru.forbion.f7cloud.feature.files

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.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.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Checkbox
import androidx.compose.material3.Icon
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.RadioButtonDefaults
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.text.PlatformTextStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupProperties
import androidx.compose.ui.window.DialogProperties
import coil.compose.AsyncImage
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
import ru.forbion.f7cloud.core.designsystem.F7AlertDialog
import ru.forbion.f7cloud.core.designsystem.F7Colors
import ru.forbion.f7cloud.core.designsystem.F7Radius
import ru.forbion.f7cloud.core.designsystem.F7Spacing
import ru.forbion.f7cloud.core.designsystem.F7OutlinedField
import ru.forbion.f7cloud.core.designsystem.F7PrimaryButton
import ru.forbion.f7cloud.core.designsystem.F7SecondaryButton
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

@Composable
fun FilesNavigationSidebar(
    serverUrl: String,
    visible: Boolean,
    browseMode: FilesBrowseMode,
    storageStats: FilesStorageStats,
    folderTree: List<FilesFolderTreeNode>,
    expandedTreePaths: Set<String>,
    expandedSections: Set<String>,
    onDismiss: () -> Unit,
    onBrowseModeClick: (FilesBrowseMode) -> Unit,
    onFolderPathClick: (String) -> Unit,
    onToggleTreePath: (String) -> Unit,
    onToggleSection: (String) -> Unit,
    onWebOnlyClick: (String) -> Unit,
    onCreateClick: (() -> Unit)? = null,
) {
    val base = serverUrl.trimEnd('/')
    AnimatedVisibility(
        visible = visible,
        enter = slideInHorizontally(initialOffsetX = { -it }),
        exit = slideOutHorizontally(targetOffsetX = { -it }),
    ) {
        Box(modifier = Modifier.fillMaxSize()) {
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .background(Color.Black.copy(alpha = 0.18f))
                    .clickable(
                        interactionSource = remember { MutableInteractionSource() },
                        indication = null,
                        onClick = onDismiss,
                    ),
            )
            Column(
                modifier = Modifier
                    .fillMaxHeight()
                    .fillMaxWidth(0.9f)
                    .background(F7Colors.Background)
                    .verticalScroll(rememberScrollState())
                    .padding(start = 10.dp, top = 10.dp, end = 16.dp, bottom = 26.dp),
                verticalArrangement = Arrangement.spacedBy(4.dp),
            ) {
                onCreateClick?.let { create ->
                    FilesSidebarCreateButton(
                        onClick = create,
                        modifier = Modifier.padding(bottom = 4.dp),
                    )
                }
                FilesSidebarRow(
                    serverUrl = base,
                    iconPath = "files/folder-gray.svg",
                    title = "Все файлы",
                    selected = browseMode == FilesBrowseMode.AllFiles,
                    onClick = { onBrowseModeClick(FilesBrowseMode.AllFiles) },
                )
                FilesSidebarRow(
                    serverUrl = base,
                    iconPath = "files/person-gray.svg",
                    title = "Личные файлы",
                    selected = browseMode == FilesBrowseMode.Personal,
                    onClick = { onBrowseModeClick(FilesBrowseMode.Personal) },
                )
                FilesSidebarRow(
                    serverUrl = base,
                    iconPath = "files/reload-gray.svg",
                    title = "Недавно изменённые",
                    selected = browseMode == FilesBrowseMode.Recent,
                    onClick = { onBrowseModeClick(FilesBrowseMode.Recent) },
                )
                FilesSidebarRow(
                    serverUrl = base,
                    iconPath = "files/star-gray.svg",
                    title = "Избранные",
                    selected = browseMode == FilesBrowseMode.Favorites,
                    onClick = { onBrowseModeClick(FilesBrowseMode.Favorites) },
                )
                FilesSidebarExpandableSection(
                    serverUrl = base,
                    title = "Опубликованные ресурсы",
                    iconPath = "files/shared-gray.svg",
                    expanded = "sharing" in expandedSections,
                    onToggle = { onToggleSection("sharing") },
                ) {
                    listOf(
                        "Предоставленные вам",
                        "Доступные для других",
                        "Доступные по ссылке",
                        "Запросы файлов",
                        "Удалённые общие ресурсы",
                        "Ожидающие подтверждения",
                    ).forEach { title ->
                        FilesSidebarChildRow(
                            serverUrl = base,
                            title = title,
                            onClick = { onWebOnlyClick(title) },
                        )
                    }
                }
                if (folderTree.isNotEmpty()) {
                    FilesSidebarExpandableSection(
                        serverUrl = base,
                        title = "Дерево папок",
                        iconPath = "files/main-folder-icon-black.svg",
                        expanded = "tree" in expandedSections,
                        onToggle = { onToggleSection("tree") },
                    ) {
                        folderTree.forEach { node ->
                            FilesTreeNodeRows(
                                serverUrl = base,
                                node = node,
                                depth = 0,
                                expandedTreePaths = expandedTreePaths,
                                onFolderPathClick = onFolderPathClick,
                                onToggleTreePath = onToggleTreePath,
                            )
                        }
                    }
                }
                FilesSidebarRow(
                    serverUrl = base,
                    iconPath = "files/many-person-gray.svg",
                    title = "Командные папки",
                    onClick = { onWebOnlyClick("Командные папки") },
                )
                FilesSidebarRow(
                    serverUrl = base,
                    iconPath = "files/tags-gray.svg",
                    title = "Метки",
                    onClick = { onWebOnlyClick("Метки") },
                )
                FilesSidebarRow(
                    serverUrl = base,
                    iconPath = "files/store-gray.svg",
                    title = "Внешнее хранилище",
                    onClick = { onWebOnlyClick("Внешнее хранилище") },
                )
                Spacer(Modifier.height(12.dp))
                HorizontalDivider(color = F7Colors.Border)
                Spacer(Modifier.height(12.dp))
                FilesSidebarRow(
                    serverUrl = base,
                    iconPath = "files/trash-gray.svg",
                    title = "Корзина",
                    onClick = { onWebOnlyClick("Корзина") },
                )
                val usage = storageStats.usedLabel.ifBlank {
                    formatStorageLabel(storageStats.usedBytes, storageStats.totalBytes)
                }
                if (usage.isNotBlank()) {
                    Spacer(Modifier.height(8.dp))
                    // Живая тема: квота по центру, серым 16/20, без прогресс-бара.
                    Text(
                        text = "Использовано $usage",
                        style = MaterialTheme.typography.bodyLarge,
                        color = F7Colors.TextSecondary,
                        textAlign = TextAlign.Center,
                        modifier = Modifier.fillMaxWidth(),
                    )
                }
            }
        }
    }
}

@Composable
private fun FilesTreeNodeRows(
    serverUrl: String,
    node: FilesFolderTreeNode,
    depth: Int,
    expandedTreePaths: Set<String>,
    onFolderPathClick: (String) -> Unit,
    onToggleTreePath: (String) -> Unit,
) {
    val expanded = node.path in expandedTreePaths
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(start = (depth * 12).dp)
            .clip(RoundedCornerShape(8.dp))
            .clickable { onFolderPathClick(node.path) }
            .padding(horizontal = 8.dp, vertical = 8.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        if (node.children.isNotEmpty()) {
            Text(
                text = if (expanded) "▾" else "▸",
                modifier = Modifier
                    .clickable(
                        interactionSource = remember { MutableInteractionSource() },
                        indication = null,
                        onClick = { onToggleTreePath(node.path) },
                    )
                    .padding(end = 4.dp),
                color = F7Colors.TextSecondary,
            )
        } else {
            Spacer(Modifier.width(14.dp))
        }
        AsyncImage(
            model = "$serverUrl/themes/forbion/images/files/folder-gray.svg",
            contentDescription = null,
            modifier = Modifier.size(16.dp),
            contentScale = ContentScale.Fit,
        )
        Spacer(Modifier.width(8.dp))
        Text(
            text = node.displayName,
            style = MaterialTheme.typography.bodyMedium,
            maxLines = 1,
            overflow = TextOverflow.Ellipsis,
        )
    }
    if (expanded) {
        node.children.forEach { child ->
            FilesTreeNodeRows(
                serverUrl = serverUrl,
                node = child,
                depth = depth + 1,
                expandedTreePaths = expandedTreePaths,
                onFolderPathClick = onFolderPathClick,
                onToggleTreePath = onToggleTreePath,
            )
        }
    }
}

/**
 * Градиентная кнопка «Создать или загрузить» сверху панели (живая тема: 40dp, r100,
 * градиент 104°, белая папка слева, разделитель и плюс справа).
 */
@Composable
fun FilesSidebarCreateButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
    Row(
        modifier = modifier
            .fillMaxWidth()
            .height(40.dp)
            .clip(RoundedCornerShape(F7Radius.pill))
            .background(
                Brush.linearGradient(listOf(Color(0xFFC0FF7B), Color(0xFF7CBC3D))),
            )
            .clickable(onClick = onClick)
            .padding(horizontal = 14.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        AsyncImage(
            model = FileIcons.chromeAsset("folder-white.svg"),
            contentDescription = null,
            modifier = Modifier.size(16.dp),
        )
        Text(
            text = "Создать или загрузить",
            modifier = Modifier
                .weight(1f)
                .padding(horizontal = 12.dp),
            style = MaterialTheme.typography.bodyLarge,
            fontWeight = FontWeight.Medium,
            color = Color(0xFFFDFDFD),
            maxLines = 1,
            overflow = TextOverflow.Ellipsis,
        )
        Box(
            modifier = Modifier
                .width(2.dp)
                .height(40.dp)
                .background(Color.White),
        )
        AsyncImage(
            model = FileIcons.chromeAsset("plus-white.svg"),
            contentDescription = "Создать",
            modifier = Modifier
                .padding(start = 14.dp)
                .size(16.dp),
        )
    }
}

@Composable
private fun FilesSidebarRow(
    serverUrl: String,
    iconPath: String,
    title: String,
    selected: Boolean = false,
    onClick: () -> Unit,
) {
    // Живая тема: строка minHeight 40, r8, иконка 20 серым, текст 16/20 #151515,
    // активная — фон #ECF9DE; правых «⋮» в навигации нет.
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .heightIn(min = 40.dp)
            .clip(RoundedCornerShape(8.dp))
            .background(if (selected) F7Colors.PrimaryLight else Color.Transparent)
            .clickable(onClick = onClick)
            .padding(horizontal = 8.dp, vertical = 8.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        AsyncImage(
            model = "$serverUrl/themes/forbion/images/$iconPath",
            contentDescription = null,
            modifier = Modifier.size(20.dp),
            contentScale = ContentScale.Fit,
        )
        Spacer(Modifier.width(8.dp))
        Text(
            text = title,
            modifier = Modifier.weight(1f),
            style = MaterialTheme.typography.bodyLarge,
            fontWeight = FontWeight.Medium,
            color = F7Colors.TextPrimary,
            maxLines = 1,
            overflow = TextOverflow.Ellipsis,
        )
    }
}

@Composable
private fun FilesSidebarChildRow(
    serverUrl: String,
    title: String,
    onClick: () -> Unit,
) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(start = 28.dp)
            .clip(RoundedCornerShape(8.dp))
            .clickable(onClick = onClick)
            .padding(horizontal = 8.dp, vertical = 8.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Text(
            text = title,
            modifier = Modifier.weight(1f),
            style = MaterialTheme.typography.bodyLarge,
            fontWeight = FontWeight.Medium,
            color = F7Colors.TextPrimary,
            maxLines = 2,
            overflow = TextOverflow.Ellipsis,
        )
    }
}

@Composable
private fun FilesSidebarExpandableSection(
    serverUrl: String,
    title: String,
    iconPath: String,
    expanded: Boolean,
    onToggle: () -> Unit,
    content: @Composable () -> Unit,
) {
    Column {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .heightIn(min = 40.dp)
                .clip(RoundedCornerShape(8.dp))
                .clickable(onClick = onToggle)
                .padding(horizontal = 8.dp, vertical = 8.dp),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            AsyncImage(
                model = "$serverUrl/themes/forbion/images/$iconPath",
                contentDescription = null,
                modifier = Modifier.size(20.dp),
                contentScale = ContentScale.Fit,
            )
            Spacer(Modifier.width(8.dp))
            Text(
                text = title,
                modifier = Modifier.weight(1f),
                style = MaterialTheme.typography.bodyLarge,
                fontWeight = FontWeight.Medium,
            )
            Text(text = if (expanded) "▴" else "▾", color = F7Colors.TextSecondary)
        }
        if (expanded) content()
    }
}

@Composable
fun FilesSettingsSheet(
    visible: Boolean,
    config: FilesUserConfig,
    onDismiss: () -> Unit,
    onToggle: (key: String, value: String) -> Unit,
    onDefaultViewChange: (String) -> Unit,
) {
    if (!visible) return
    androidx.compose.ui.window.Dialog(
        onDismissRequest = onDismiss,
        properties = DialogProperties(usePlatformDefaultWidth = false),
    ) {
        Surface(
            modifier = Modifier.fillMaxSize(),
            color = F7Colors.Surface,
        ) {
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .verticalScroll(rememberScrollState())
                    .padding(20.dp),
            ) {
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.SpaceBetween,
                    verticalAlignment = Alignment.CenterVertically,
                ) {
                    Text(
                        "Настройки файлов",
                        style = MaterialTheme.typography.titleLarge,
                        fontWeight = FontWeight.SemiBold,
                        color = F7Colors.TextPrimary,
                    )
                    TextButton(onClick = onDismiss) {
                        Text("✕", fontSize = 20.sp, color = F7Colors.TextSecondary)
                    }
                }
                Spacer(Modifier.height(16.dp))
                FilesSettingsSection("Основные") {
                    FilesSettingsSwitch("Сначала избранное", config.sortFavoritesFirst) {
                        onToggle("sort_favorites_first", if (it) "1" else "0")
                    }
                    FilesSettingsSwitch("Начинать список с папок", config.sortFoldersFirst) {
                        onToggle("sort_folders_first", if (it) "1" else "0")
                    }
                    FilesSettingsSwitch("Включить отображение дерева папок", config.folderTree) {
                        onToggle("folder_tree", if (it) "1" else "0")
                    }
                }
                FilesSettingsSection("Вид по умолчанию") {
                    FilesSettingsRadio("Все файлы", config.defaultView == "files") {
                        onDefaultViewChange("files")
                    }
                    FilesSettingsRadio("Личные файлы", config.defaultView == "personal") {
                        onDefaultViewChange("personal")
                    }
                }
                FilesSettingsSection("Внешний вид") {
                    FilesSettingsSwitch("Показывать скрытые файлы", config.showHidden) {
                        onToggle("show_hidden", if (it) "1" else "0")
                    }
                    FilesSettingsSwitch("Показать колонку с типом файла", config.showMimeColumn) {
                        onToggle("show_mime_column", if (it) "1" else "0")
                    }
                    FilesSettingsSwitch("Показать расширения файлов", config.showExtensions) {
                        onToggle("show_files_extensions", if (it) "1" else "0")
                    }
                    FilesSettingsSwitch("Обрезать пред. просмотр", config.cropImagePreviews) {
                        onToggle("crop_image_previews", if (it) "1" else "0")
                    }
                }
            }
        }
    }
}

@Composable
private fun FilesSettingsSection(title: String, content: @Composable () -> Unit) {
    Text(
        text = title,
        style = MaterialTheme.typography.labelLarge,
        fontWeight = FontWeight.SemiBold,
        modifier = Modifier.padding(vertical = 8.dp),
    )
    Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
        content()
    }
    Spacer(Modifier.height(12.dp))
}

@Composable
private fun FilesSettingsSwitch(
    title: String,
    checked: Boolean,
    onCheckedChange: (Boolean) -> Unit,
) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 6.dp),
        horizontalArrangement = Arrangement.SpaceBetween,
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Text(title, modifier = Modifier.weight(1f), style = MaterialTheme.typography.bodyMedium)
        Switch(
            checked = checked,
            onCheckedChange = onCheckedChange,
            colors = SwitchDefaults.colors(
                checkedThumbColor = Color.White,
                checkedTrackColor = F7Colors.Primary,
            ),
        )
    }
}

@Composable
private fun FilesSettingsRadio(
    title: String,
    selected: Boolean,
    onClick: () -> Unit,
) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .clickable(onClick = onClick)
            .padding(vertical = 6.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        RadioButton(
            selected = selected,
            onClick = onClick,
            colors = RadioButtonDefaults.colors(selectedColor = F7Colors.Primary),
        )
        Text(title, style = MaterialTheme.typography.bodyMedium)
    }
}

@Composable
fun FilesSearchBar(
    serverUrl: String,
    query: String,
    onQueryChange: (String) -> Unit,
    modifier: Modifier = Modifier,
) {
    Box(
        modifier = modifier
            .height(40.dp)
            .shadow(
                elevation = 2.dp,
                shape = RoundedCornerShape(100.dp),
                spotColor = Color(0xFFCBCBCB),
            )
            .clip(RoundedCornerShape(100.dp))
            .background(F7Colors.Background)
            .border(1.dp, F7Colors.Border, RoundedCornerShape(100.dp)),
        contentAlignment = Alignment.CenterStart,
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .fillMaxHeight()
                .padding(horizontal = 12.dp),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            AsyncImage(
                model = FileIcons.chromeAsset("search-gray.svg"),
                contentDescription = null,
                modifier = Modifier.size(16.dp),
                contentScale = ContentScale.Fit,
            )
            BasicTextField(
                value = query,
                onValueChange = onQueryChange,
                modifier = Modifier
                    .weight(1f)
                    .padding(horizontal = 8.dp),
                singleLine = true,
                textStyle = TextStyle(
                    fontSize = 14.sp,
                    fontWeight = FontWeight.Medium,
                    color = F7Colors.TextPrimary,
                    platformStyle = PlatformTextStyle(includeFontPadding = false),
                ),
                cursorBrush = SolidColor(F7Colors.Primary),
                decorationBox = { inner ->
                    Box(
                        modifier = Modifier.fillMaxHeight(),
                        contentAlignment = Alignment.CenterStart,
                    ) {
                        if (query.isEmpty()) {
                            Text(
                                "Поиск",
                                style = TextStyle(
                                    fontSize = 14.sp,
                                    fontWeight = FontWeight.Medium,
                                    color = F7Colors.TextSecondary,
                                    platformStyle = PlatformTextStyle(includeFontPadding = false),
                                ),
                            )
                        }
                        inner()
                    }
                },
            )
            if (query.isNotEmpty()) {
                Icon(
                    imageVector = Icons.Filled.Close,
                    contentDescription = "Очистить поиск",
                    tint = F7Colors.TextSecondary,
                    modifier = Modifier
                        .size(20.dp)
                        .clickable { onQueryChange("") },
                )
            }
        }
    }
}

@Composable
fun FilesListHeader(
    serverUrl: String,
    showMimeColumn: Boolean,
    sortColumn: FilesSortColumn,
    sortDirection: FilesSortDirection,
    selectionMode: Boolean,
    allSelected: Boolean,
    onSelectAllToggle: () -> Unit,
    onSortColumnClick: (FilesSortColumn) -> Unit,
) {
    val base = serverUrl.trimEnd('/')
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 6.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Checkbox(
            checked = allSelected,
            onCheckedChange = { onSelectAllToggle() },
            colors = CheckboxDefaults.colors(checkedColor = F7Colors.Primary),
        )
        FilesHeaderColumn(
            serverUrl = base,
            iconPath = "files/file-default.svg",
            title = "Имя",
            active = sortColumn == FilesSortColumn.Name,
            descending = sortDirection == FilesSortDirection.Desc,
            onClick = { onSortColumnClick(FilesSortColumn.Name) },
            modifier = Modifier.weight(1.2f),
        )
        FilesHeaderColumn(
            serverUrl = base,
            iconPath = "files/grid-files-gray.svg",
            title = "Тип",
            active = sortColumn == FilesSortColumn.Type,
            descending = sortDirection == FilesSortDirection.Desc,
            onClick = { onSortColumnClick(FilesSortColumn.Type) },
            modifier = Modifier.weight(if (showMimeColumn) 1f else 1f),
        )
        FilesHeaderColumn(
            serverUrl = base,
            iconPath = "files/reload-gray.svg",
            title = "Изменён",
            active = sortColumn == FilesSortColumn.Modified,
            descending = sortDirection == FilesSortDirection.Desc,
            onClick = { onSortColumnClick(FilesSortColumn.Modified) },
            modifier = Modifier.weight(1f),
        )
        if (showMimeColumn) {
            FilesHeaderColumn(
                serverUrl = base,
                iconPath = "files/persons.svg",
                title = "Люди",
                active = false,
                descending = false,
                onClick = {},
                modifier = Modifier.weight(1f),
            )
        }
    }
}

@Composable
private fun FilesHeaderColumn(
    serverUrl: String,
    iconPath: String,
    title: String,
    active: Boolean,
    descending: Boolean,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
) {
    Row(
        modifier = modifier
            .clip(RoundedCornerShape(6.dp))
            .clickable(onClick = onClick)
            .padding(vertical = 4.dp),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(4.dp),
    ) {
        AsyncImage(
            model = "$serverUrl/themes/forbion/images/$iconPath",
            contentDescription = null,
            modifier = Modifier.size(14.dp),
            contentScale = ContentScale.Fit,
        )
        Text(
            text = title,
            style = MaterialTheme.typography.labelMedium,
            color = if (active) F7Colors.Primary else F7Colors.TextSecondary,
            maxLines = 1,
        )
        if (active) {
            Text(
                text = when {
                    title == "Имя" && descending -> "Я–А"
                    title == "Имя" -> "А–Я"
                    descending -> "↓"
                    else -> "↑"
                },
                color = F7Colors.Primary,
                fontSize = 12.sp,
            )
        }
    }
}

/**
 * Панель выделения по живой теме: 40dp, чекбокс 16 «выбрать все», «Выбрать все» чёрным +
 * «(Выбран N)» серым, справа — пилюля «Действия» 25dp (без иконки).
 */
@Composable
fun FilesBulkActionsBar(
    serverUrl: String,
    selectedCount: Int,
    allSelected: Boolean = false,
    onToggleSelectAll: (() -> Unit)? = null,
    onActionsClick: (Rect) -> Unit,
) {
    var anchorBounds by remember { mutableStateOf<Rect?>(null) }
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .height(40.dp)
            .padding(horizontal = 12.dp),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(8.dp),
    ) {
        F7ThemeCheckbox(checked = allSelected, onToggle = { onToggleSelectAll?.invoke() })
        Text(
            text = buildAnnotatedString {
                append("Выбрать все ")
                withStyle(SpanStyle(color = F7Colors.TextSecondary)) {
                    append("(Выбран $selectedCount)")
                }
            },
            modifier = Modifier.weight(1f),
            style = MaterialTheme.typography.bodyMedium,
            fontWeight = FontWeight.Medium,
            color = F7Colors.TextPrimary,
            maxLines = 1,
            overflow = TextOverflow.Ellipsis,
        )
        Box(
            modifier = Modifier
                .height(25.dp)
                .clip(RoundedCornerShape(100.dp))
                .background(F7Colors.Background)
                .border(1.dp, F7Colors.Border, RoundedCornerShape(100.dp))
                .clickable { anchorBounds?.let(onActionsClick) }
                .onGloballyPositioned { coords ->
                    anchorBounds = coords.boundsInWindow()
                }
                .padding(horizontal = 8.dp),
            contentAlignment = Alignment.Center,
        ) {
            Text(
                "Действия",
                style = MaterialTheme.typography.bodyMedium,
                color = F7Colors.TextPrimary,
            )
        }
    }
}

@Composable
fun FilesListRow(
    serverUrl: String,
    item: FileItem,
    selected: Boolean,
    showExtensions: Boolean,
    onSelectedChange: () -> Unit,
    onOpenClick: () -> Unit,
    onMenuClick: (Rect) -> Unit,
) {
    var menuAnchor by remember { mutableStateOf<Rect?>(null) }
    // Имя + расширение (расширение серым, инлайн) — по макету моб. Файлов (F-18/F-22).
    val hasExt = !item.isDirectory && item.name.contains('.')
    val baseName = if (hasExt) item.name.substringBeforeLast('.') else item.name
    val extension = if (hasExt && showExtensions) "." + item.name.substringAfterLast('.') else ""
    // Строка 44dp по боевой теме (_mobile-files.css): чекбокс 16 в колонке 44, иконка 30
    // (зелёная звезда 16 поверх у избранных), имя, размер 56 по центру, дата 88 вправо, ⋮ в 32.
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .height(44.dp)
            .background(if (selected) F7Colors.PrimaryLight else Color.Transparent)
            .clickable(onClick = onOpenClick),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Box(modifier = Modifier.size(44.dp), contentAlignment = Alignment.Center) {
            F7ThemeCheckbox(checked = selected, onToggle = onSelectedChange)
        }
        Box(modifier = Modifier.size(30.dp)) {
            AsyncImage(
                model = FileIcons.iconUrl(serverUrl, item.name, item.isDirectory),
                contentDescription = null,
                modifier = Modifier.size(30.dp),
                contentScale = ContentScale.Fit,
            )
            if (item.favorite) {
                AsyncImage(
                    model = FileIcons.chromeAsset("star-green-full.svg"),
                    contentDescription = "В избранном",
                    modifier = Modifier
                        .size(16.dp)
                        .offset(x = 20.dp, y = (-4).dp),
                )
            }
        }
        Text(
            text = buildAnnotatedString {
                append(baseName)
                if (extension.isNotEmpty()) {
                    withStyle(SpanStyle(color = F7Colors.TextSecondary)) { append(" $extension") }
                }
            },
            modifier = Modifier
                .weight(1f)
                .padding(start = F7Spacing.sm, end = F7Spacing.xs),
            style = MaterialTheme.typography.bodyMedium,
            fontWeight = FontWeight.Medium,
            color = F7Colors.TextPrimary,
            maxLines = 1,
            overflow = TextOverflow.Ellipsis,
        )
        item.size?.takeIf { !item.isDirectory }?.let { size ->
            Text(
                text = formatFileSize(size),
                modifier = Modifier.width(56.dp),
                style = MaterialTheme.typography.bodyMedium,
                color = F7Colors.TextPrimary,
                maxLines = 1,
                textAlign = TextAlign.Center,
            )
        }
        item.lastModified?.let { mtime ->
            Text(
                text = formatRelativeTime(mtime),
                modifier = Modifier.width(88.dp),
                style = MaterialTheme.typography.bodyMedium,
                color = F7Colors.TextPrimary,
                maxLines = 1,
                overflow = TextOverflow.Ellipsis,
                textAlign = TextAlign.End,
            )
        }
        Box(
            modifier = Modifier
                .width(32.dp)
                .height(44.dp)
                .clickable(
                    interactionSource = remember { MutableInteractionSource() },
                    indication = null,
                    onClick = { menuAnchor?.let(onMenuClick) },
                )
                .onGloballyPositioned { coords ->
                    menuAnchor = coords.boundsInWindow()
                },
            contentAlignment = Alignment.Center,
        ) {
            AsyncImage(
                model = FileIcons.chromeAsset("dots-vertic-gray.svg"),
                contentDescription = "Действия",
                modifier = Modifier.size(16.dp),
                contentScale = ContentScale.Fit,
            )
        }
    }
}

/** Относительное время как в вебе (moment.js): «5 мин. назад», «вчера», иначе дата. */
internal fun formatRelativeTime(epochMillis: Long): String =
    android.text.format.DateUtils.getRelativeTimeSpanString(
        epochMillis,
        System.currentTimeMillis(),
        android.text.format.DateUtils.MINUTE_IN_MILLIS,
        android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE,
    ).toString()

@Composable
fun FileActionMenuPopup(
    expanded: Boolean,
    serverUrl: String,
    anchorBounds: Rect?,
    entries: List<FileContextMenuEntry>,
    onDismiss: () -> Unit,
    onAction: (FileContextAction) -> Unit,
) {
    if (!expanded || anchorBounds == null) return
    val base = serverUrl.trimEnd('/')
    val screenHeightPx = with(LocalDensity.current) {
        LocalConfiguration.current.screenHeightDp.dp.toPx()
    }
    val openAbove = anchorBounds.center.y > screenHeightPx / 2f
    Popup(
        alignment = if (openAbove) Alignment.BottomEnd else Alignment.TopEnd,
        offset = IntOffset(
            x = anchorBounds.right.toInt() - 280,
            y = if (openAbove) anchorBounds.top.toInt() - 8 else anchorBounds.bottom.toInt() + 8,
        ),
        onDismissRequest = onDismiss,
        properties = PopupProperties(focusable = true),
    ) {
        Surface(
            modifier = Modifier
                .widthIn(min = 280.dp, max = 340.dp)
                .border(1.dp, F7Colors.Border, RoundedCornerShape(8.dp)),
            shape = RoundedCornerShape(8.dp),
            shadowElevation = 4.dp,
            color = Color.White,
        ) {
            Column(modifier = Modifier.padding(4.dp)) {
                entries.forEachIndexed { index, entry ->
                    if (entry.destructive && index > 0) {
                        HorizontalDivider(
                            modifier = Modifier.padding(vertical = 4.dp),
                            color = F7Colors.Border.copy(alpha = 0.6f),
                        )
                    }
                    Row(
                        modifier = Modifier
                            .fillMaxWidth()
                            .heightIn(min = 25.dp)
                            .clip(RoundedCornerShape(6.dp))
                            .clickable {
                                onDismiss()
                                onAction(entry.action)
                            }
                            .padding(horizontal = 8.dp, vertical = 4.dp),
                        verticalAlignment = Alignment.CenterVertically,
                        horizontalArrangement = Arrangement.spacedBy(8.dp),
                    ) {
                        AsyncImage(
                            model = "$base/themes/forbion/images/${entry.iconPath}",
                            contentDescription = null,
                            modifier = Modifier.size(16.dp),
                            contentScale = ContentScale.Fit,
                        )
                        Text(
                            entry.label,
                            modifier = Modifier.weight(1f),
                            style = MaterialTheme.typography.bodyMedium,
                            color = if (entry.destructive) F7Colors.Error else F7Colors.TextPrimary,
                            maxLines = 2,
                            overflow = TextOverflow.Ellipsis,
                        )
                        if (entry.showChevron) {
                            Text("›", color = F7Colors.TextSecondary, fontSize = 18.sp)
                        }
                    }
                }
            }
        }
    }
}

@Composable
fun FilesBulkActionMenuPopup(
    expanded: Boolean,
    serverUrl: String,
    anchorBounds: Rect?,
    onDismiss: () -> Unit,
    onAction: (FilesBulkAction) -> Unit,
) {
    if (!expanded || anchorBounds == null) return
    val base = serverUrl.trimEnd('/')
    val screenHeightPx = with(LocalDensity.current) {
        LocalConfiguration.current.screenHeightDp.dp.toPx()
    }
    val openAbove = anchorBounds.center.y > screenHeightPx / 2f
    val entries = listOf(
        Triple(FilesBulkAction.Favorite, "Добавить в избранное", "files/star-gray.svg"),
        Triple(FilesBulkAction.Download, "Скачать", "files/download-icon-gray.svg"),
        Triple(FilesBulkAction.Delete, "Удалить", "files/trash-gray.svg"),
    )
    Popup(
        alignment = if (openAbove) Alignment.BottomStart else Alignment.TopStart,
        offset = IntOffset(
            x = anchorBounds.left.toInt(),
            y = if (openAbove) anchorBounds.top.toInt() - 8 else anchorBounds.bottom.toInt() + 8,
        ),
        onDismissRequest = onDismiss,
        properties = PopupProperties(focusable = true),
    ) {
        Surface(
            modifier = Modifier
                .widthIn(min = 240.dp, max = 300.dp)
                .border(1.dp, F7Colors.Border, RoundedCornerShape(8.dp)),
            shape = RoundedCornerShape(8.dp),
            shadowElevation = 4.dp,
            color = Color.White,
        ) {
            Column(modifier = Modifier.padding(4.dp)) {
                entries.forEachIndexed { index, (action, label, icon) ->
                    if (index == entries.lastIndex) {
                        HorizontalDivider(
                            modifier = Modifier.padding(vertical = 4.dp),
                            color = F7Colors.Border.copy(alpha = 0.6f),
                        )
                    }
                    Row(
                        modifier = Modifier
                            .fillMaxWidth()
                            .clickable {
                                onDismiss()
                                onAction(action)
                            }
                            .padding(horizontal = 14.dp, vertical = 11.dp),
                        verticalAlignment = Alignment.CenterVertically,
                        horizontalArrangement = Arrangement.spacedBy(12.dp),
                    ) {
                        AsyncImage(
                            model = "$base/themes/forbion/images/$icon",
                            contentDescription = null,
                            modifier = Modifier.size(18.dp),
                            contentScale = ContentScale.Fit,
                        )
                        Text(
                            label,
                            style = MaterialTheme.typography.bodyMedium,
                            color = if (action == FilesBulkAction.Delete) F7Colors.Error else F7Colors.TextPrimary,
                        )
                    }
                }
            }
        }
    }
}

@Composable
fun FilesDetailsSheet(
    serverUrl: String,
    ownerLabel: String,
    item: FileItem?,
    tab: FileDetailsTab,
    details: FileDetailsData?,
    loading: Boolean,
    onDismiss: () -> Unit,
    onTabChange: (FileDetailsTab) -> Unit,
    onToggleFavorite: () -> Unit,
    onWebOnlyAction: (String) -> Unit,
) {
    if (item == null) return
    var commentDraft by remember(item.relativePath) { mutableStateOf("") }
    // Живая тема: панель на весь экран, фон #FBFBFB, без скруглений.
    androidx.compose.ui.window.Dialog(
        onDismissRequest = onDismiss,
        properties = androidx.compose.ui.window.DialogProperties(usePlatformDefaultWidth = false),
    ) {
        Surface(
            modifier = Modifier.fillMaxSize(),
            color = F7Colors.Background,
        ) {
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(horizontal = 16.dp, vertical = 12.dp),
            ) {
                // Крестик 40dp — правый верхний угол
                Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
                    Box(
                        modifier = Modifier
                            .size(40.dp)
                            .clip(RoundedCornerShape(8.dp))
                            .clickable(onClick = onDismiss),
                        contentAlignment = Alignment.Center,
                    ) {
                        Icon(
                            Icons.Filled.Close,
                            contentDescription = "Закрыть",
                            tint = F7Colors.TextSecondary,
                            modifier = Modifier.size(20.dp),
                        )
                    }
                }
                // Заголовок 18/600 + звезда 20 справа (живая тема)
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.spacedBy(8.dp),
                    verticalAlignment = Alignment.CenterVertically,
                ) {
                    Text(
                        item.name,
                        modifier = Modifier.weight(1f),
                        style = MaterialTheme.typography.titleMedium.copy(fontSize = 18.sp),
                        fontWeight = FontWeight.SemiBold,
                        color = F7Colors.TextPrimary,
                        maxLines = 1,
                        overflow = TextOverflow.Ellipsis,
                    )
                    AsyncImage(
                        model = FileIcons.chromeAsset(
                            if (item.favorite) "star-green-full.svg" else "star-gray.svg",
                        ),
                        contentDescription = "Избранное",
                        modifier = Modifier
                            .size(20.dp)
                            .clickable(onClick = onToggleFavorite),
                    )
                }
                Spacer(Modifier.height(8.dp))
                // Чип владельца (пилюля 25dp) слева, «дата / размер» серым справа
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    verticalAlignment = Alignment.CenterVertically,
                ) {
                    Row(
                        modifier = Modifier
                            .height(25.dp)
                            .clip(RoundedCornerShape(100.dp))
                            .background(F7Colors.Background)
                            .border(1.dp, F7Colors.Border, RoundedCornerShape(100.dp))
                            .padding(horizontal = 4.dp),
                        verticalAlignment = Alignment.CenterVertically,
                        horizontalArrangement = Arrangement.spacedBy(4.dp),
                    ) {
                        Box(
                            modifier = Modifier
                                .size(16.dp)
                                .clip(CircleShape)
                                .background(F7Colors.PrimaryLight),
                            contentAlignment = Alignment.Center,
                        ) {
                            Text(
                                ownerLabel.firstOrNull()?.uppercaseChar()?.toString() ?: "?",
                                style = MaterialTheme.typography.labelSmall,
                                color = F7Colors.Primary,
                                fontWeight = FontWeight.SemiBold,
                            )
                        }
                        Text(
                            ownerLabel,
                            style = MaterialTheme.typography.labelMedium,
                            color = F7Colors.TextPrimary,
                            maxLines = 1,
                            overflow = TextOverflow.Ellipsis,
                        )
                    }
                    Spacer(Modifier.weight(1f))
                    val dateSize = buildList {
                        item.lastModified?.let {
                            add(SimpleDateFormat("d MMMM", Locale("ru")).format(Date(it)))
                        }
                        item.size?.let { add(formatFileSize(it)) }
                    }
                    if (dateSize.isNotEmpty()) {
                        Text(
                            dateSize.joinToString(" / "),
                            style = MaterialTheme.typography.bodyMedium,
                            color = F7Colors.TextSecondary,
                        )
                    }
                }
                Spacer(Modifier.height(16.dp))
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.spacedBy(12.dp),
                ) {
                    FilesDetailsTabPill(
                        iconAsset = "comment-icon-black.svg",
                        contentDescription = "Обсуждение",
                        selected = tab == FileDetailsTab.Events,
                        onClick = { onTabChange(FileDetailsTab.Events) },
                        modifier = Modifier.weight(1f),
                    )
                    FilesDetailsTabPill(
                        iconAsset = "people-icon-black.svg",
                        contentDescription = "Общий доступ",
                        selected = tab == FileDetailsTab.Sharing,
                        onClick = { onTabChange(FileDetailsTab.Sharing) },
                        modifier = Modifier.weight(1f),
                    )
                }
                Spacer(Modifier.height(16.dp))
                Box(
                    modifier = Modifier
                        .weight(1f)
                        .fillMaxWidth(),
                ) {
                    when {
                        loading -> {
                            Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
                                CircularProgressIndicator(color = F7Colors.Primary)
                            }
                        }
                        tab == FileDetailsTab.Sharing -> {
                            FilesSharingTabContent(
                                serverUrl = serverUrl,
                                shares = details?.shares.orEmpty(),
                                error = details?.error,
                                onWebOnlyAction = onWebOnlyAction,
                            )
                        }
                        else -> {
                            FilesEventsTabContent(
                                serverUrl = serverUrl,
                                ownerLabel = ownerLabel,
                                activities = details?.activities.orEmpty(),
                                commentDraft = commentDraft,
                                onCommentChange = { commentDraft = it },
                                onSubmitComment = {
                                    if (commentDraft.isBlank()) return@FilesEventsTabContent
                                    onWebOnlyAction("Комментарии")
                                    commentDraft = ""
                                },
                                error = details?.error,
                            )
                        }
                    }
                }
            }
        }
    }
}

@Composable
private fun FilesDetailsTabPill(
    iconAsset: String,
    contentDescription: String,
    selected: Boolean,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
) {
    // Живая тема: пилюля 30dp r100; активная — фон #ECF9DE, рамка #70B62B 20%; иконка темы 16.
    val shape = RoundedCornerShape(100.dp)
    Box(
        modifier = modifier
            .height(30.dp)
            .clip(shape)
            .background(if (selected) F7Colors.PrimaryLight else F7Colors.Background)
            .border(
                1.dp,
                if (selected) F7Colors.Primary.copy(alpha = 0.2f) else F7Colors.Border,
                shape,
            )
            .clickable(onClick = onClick),
        contentAlignment = Alignment.Center,
    ) {
        AsyncImage(
            model = FileIcons.chromeAsset(iconAsset),
            contentDescription = contentDescription,
            modifier = Modifier.size(16.dp),
        )
    }
}

@Composable
private fun FilesSharingTabContent(
    serverUrl: String,
    shares: List<FileShareEntry>,
    error: String?,
    onWebOnlyAction: (String) -> Unit,
) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState()),
        verticalArrangement = Arrangement.spacedBy(12.dp),
    ) {
        F7OutlinedField(
            value = "",
            onValueChange = {},
            label = "Введите адрес эл. почты или идентификатор...",
            modifier = Modifier
                .fillMaxWidth()
                .clickable { onWebOnlyAction("Добавление получателей") },
        )
        if (!error.isNullOrBlank()) {
            Text(error, color = F7Colors.Error, style = MaterialTheme.typography.bodySmall)
        }
        shares.forEach { share ->
            FilesShareRow(serverUrl = serverUrl, share = share)
        }
        F7OutlinedField(
            value = "",
            onValueChange = {},
            label = "Введите имена или команды",
            modifier = Modifier
                .fillMaxWidth()
                .clickable { onWebOnlyAction("Добавление пользователей") },
        )
        FilesSharingInfoRow(
            serverUrl = serverUrl,
            iconPath = "files/many-person-gray.svg",
            title = "Другие пользователи, имеющие доступ",
            showChevron = true,
            onClick = { onWebOnlyAction("Список пользователей") },
        )
        FilesSharingInfoRow(
            serverUrl = serverUrl,
            iconPath = "files/button-shared-for-files.svg",
            title = "Внутренняя ссылка",
            subtitle = "Для тех, у кого уже есть доступ",
            showPlus = true,
            onClick = { onWebOnlyAction("Внутренняя ссылка") },
        )
    }
}

@Composable
private fun FilesShareRow(
    serverUrl: String,
    share: FileShareEntry,
) {
    val base = serverUrl.trimEnd('/')
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .clip(RoundedCornerShape(12.dp))
            .background(Color(0xFFF7F7F7))
            .padding(12.dp),
        verticalAlignment = Alignment.Top,
        horizontalArrangement = Arrangement.spacedBy(10.dp),
    ) {
        Box(
            modifier = Modifier
                .size(36.dp)
                .clip(RoundedCornerShape(18.dp))
                .background(Color(0xFFECECEC)),
            contentAlignment = Alignment.Center,
        ) {
            AsyncImage(
                model = "$base/themes/forbion/images/files/button-shared-for-files.svg",
                contentDescription = null,
                modifier = Modifier.size(18.dp),
                contentScale = ContentScale.Fit,
            )
        }
        Column(modifier = Modifier.weight(1f)) {
            Text(
                share.label,
                style = MaterialTheme.typography.bodyMedium,
                fontWeight = FontWeight.Medium,
            )
            Text(
                share.permissionsLabel,
                style = MaterialTheme.typography.bodySmall,
                color = F7Colors.Primary,
            )
        }
    }
}

@Composable
private fun FilesSharingInfoRow(
    serverUrl: String,
    iconPath: String,
    title: String,
    subtitle: String? = null,
    showChevron: Boolean = false,
    showPlus: Boolean = false,
    onClick: () -> Unit,
) {
    val base = serverUrl.trimEnd('/')
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .clip(RoundedCornerShape(12.dp))
            .clickable(onClick = onClick)
            .padding(vertical = 8.dp),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(10.dp),
    ) {
        Box(
            modifier = Modifier
                .size(36.dp)
                .clip(RoundedCornerShape(18.dp))
                .background(Color(0xFFF0F0F0)),
            contentAlignment = Alignment.Center,
        ) {
            AsyncImage(
                model = "$base/themes/forbion/images/$iconPath",
                contentDescription = null,
                modifier = Modifier.size(18.dp),
                contentScale = ContentScale.Fit,
            )
        }
        Column(modifier = Modifier.weight(1f)) {
            Text(title, style = MaterialTheme.typography.bodyMedium)
            if (!subtitle.isNullOrBlank()) {
                Text(subtitle, style = MaterialTheme.typography.bodySmall, color = F7Colors.TextSecondary)
            }
        }
        when {
            showPlus -> Text("+", fontSize = 22.sp, color = F7Colors.TextSecondary)
            showChevron -> Text("›", fontSize = 20.sp, color = F7Colors.TextSecondary)
        }
    }
}

@Composable
private fun FilesEventsTabContent(
    serverUrl: String,
    ownerLabel: String,
    activities: List<FileActivityEntry>,
    commentDraft: String,
    onCommentChange: (String) -> Unit,
    onSubmitComment: () -> Unit,
    error: String?,
) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState()),
        verticalArrangement = Arrangement.spacedBy(12.dp),
    ) {
        Row(
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.spacedBy(10.dp),
        ) {
            Box(
                modifier = Modifier
                    .size(36.dp)
                    .clip(RoundedCornerShape(18.dp))
                    .background(Color(0xFFE8F5D6)),
                contentAlignment = Alignment.Center,
            ) {
                Text(
                    ownerLabel.firstOrNull()?.uppercaseChar()?.toString() ?: "?",
                    color = F7Colors.Primary,
                    fontWeight = FontWeight.SemiBold,
                )
            }
            Text(ownerLabel, style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Medium)
        }
        Row(
            modifier = Modifier.fillMaxWidth(),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.spacedBy(8.dp),
        ) {
            F7OutlinedField(
                value = commentDraft,
                onValueChange = onCommentChange,
                label = "Новый комментарий",
                modifier = Modifier.weight(1f),
                onEnter = onSubmitComment,
            )
            Box(
                modifier = Modifier
                    .size(40.dp)
                    .clip(CircleShape)
                    .background(
                        Brush.linearGradient(
                            listOf(F7Colors.PrimaryGradientStart, F7Colors.PrimaryGradientEnd),
                        ),
                    )
                    .clickable(onClick = onSubmitComment),
                contentAlignment = Alignment.Center,
            ) {
                Icon(
                    Icons.AutoMirrored.Filled.ArrowForward,
                    contentDescription = "Отправить",
                    tint = Color.White,
                    modifier = Modifier.size(18.dp),
                )
            }
        }
        Text(
            "@ для упоминаний, : для эмодзи, / для интеллектуального выбора",
            style = MaterialTheme.typography.labelSmall,
            color = F7Colors.TextSecondary,
        )
        if (!error.isNullOrBlank()) {
            Text(error, color = F7Colors.Error, style = MaterialTheme.typography.bodySmall)
        }
        if (activities.isEmpty()) {
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 32.dp),
                horizontalAlignment = Alignment.CenterHorizontally,
            ) {
                AsyncImage(
                    model = "${serverUrl.trimEnd('/')}/themes/forbion/images/files/grid-files-gray.svg",
                    contentDescription = null,
                    modifier = Modifier.size(48.dp),
                    contentScale = ContentScale.Fit,
                )
                Spacer(Modifier.height(12.dp))
                Text(
                    "Пока не произошло ни одного события",
                    style = MaterialTheme.typography.bodyMedium,
                    color = F7Colors.TextSecondary,
                )
            }
        } else {
            activities.forEach { activity ->
                Column(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(vertical = 4.dp),
                ) {
                    Text(
                        activity.author,
                        style = MaterialTheme.typography.labelMedium,
                        fontWeight = FontWeight.SemiBold,
                    )
                    Text(activity.message, style = MaterialTheme.typography.bodyMedium)
                    if (activity.timestamp > 0L) {
                        Text(
                            formatModified(activity.timestamp),
                            style = MaterialTheme.typography.labelSmall,
                            color = F7Colors.TextSecondary,
                        )
                    }
                }
            }
        }
    }
}

@Composable
fun FilesRenameDialog(
    item: FileItem?,
    onDismiss: () -> Unit,
    onConfirm: (String) -> Unit,
) {
    if (item == null) return
    var name by remember(item.relativePath) { mutableStateOf(item.name) }
    F7AlertDialog(
        title = "Переименовать",
        onDismiss = onDismiss,
        confirmText = "Сохранить",
        onConfirm = { onConfirm(name) },
        confirmEnabled = name.trim().isNotBlank() && name.trim() != item.name,
    ) {
        F7OutlinedField(
            value = name,
            onValueChange = { name = it },
            label = "Новое имя",
        )
    }
}

fun formatFileSize(size: Long): String = when {
    size >= 1_073_741_824 -> "%.1f ГБ".format(size / 1_073_741_824.0)
    size >= 1_048_576 -> "%.1f МБ".format(size / 1_048_576.0)
    size >= 1024 -> "%.0f КБ".format(size / 1024.0)
    else -> "$size Б"
}

fun formatModified(millis: Long): String {
    val format = SimpleDateFormat("dd.MM.yyyy HH:mm", Locale("ru"))
    return format.format(Date(millis))
}

private fun formatStorageLabel(used: Long, total: Long): String {
    if (used <= 0L && total <= 0L) return ""
    return if (total > 0L) {
        "${formatFileSize(used)} из ${formatFileSize(total)}"
    } else {
        formatFileSize(used)
    }
}
