package ru.forbion.f7cloud.feature.mail

import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
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.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.compose.viewModel
import ru.forbion.f7cloud.core.auth.AuthSession
import androidx.activity.compose.BackHandler
import ru.forbion.f7cloud.core.designsystem.F7Colors
import ru.forbion.f7cloud.core.designsystem.F7ErrorState
import ru.forbion.f7cloud.core.designsystem.F7OverlayDismissHandler
import ru.forbion.f7cloud.core.designsystem.f7SwipeFromRightToDismiss
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import ru.forbion.f7cloud.core.network.NetworkFactory
import ru.forbion.f7cloud.feature.files.LocalFileOpener
import kotlinx.coroutines.flow.distinctUntilChanged
import java.io.File

@Composable
fun MailScreen(
    session: AuthSession,
    modifier: Modifier = Modifier,
    openMessageId: Int? = null,
    openMailboxId: Int? = null,
    pushRefreshRequest: Int = 0,
    sidebarOpen: Boolean = false,
    onSidebarOpenChange: (Boolean) -> Unit = {},
    settingsOpen: Boolean = false,
    onSettingsOpenChange: (Boolean) -> Unit = {},
    onOpenMessageConsumed: () -> Unit = {},
    onMessageOpenStateChange: (Boolean) -> Unit = {},
    onUnauthorized: () -> Unit = {},
) {
    val context = LocalContext.current
    val cacheRepository = remember { MailCacheRepository(context) }
    val vm: MailViewModel = viewModel(
        factory = object : ViewModelProvider.Factory {
            @Suppress("UNCHECKED_CAST")
            override fun <T : ViewModel> create(modelClass: Class<T>): T =
                MailViewModel(cacheRepository = cacheRepository) as T
        },
    )
    val state by vm.state.collectAsState()
    val httpClient = remember(session.username, session.appPassword, session.trustAllCerts) {
        NetworkFactory.newAuthedClient(
            session.username,
            session.appPassword,
            session.trustAllCerts,
        )
    }

    LaunchedEffect(session.serverUrl, session.username) {
        vm.load(session)
    }
    LaunchedEffect(pushRefreshRequest) {
        if (pushRefreshRequest > 0) {
            vm.load(session, forceRefresh = true)
        }
    }
    LaunchedEffect(openMessageId) {
        val messageId = openMessageId ?: return@LaunchedEffect
        vm.openDeepLink(session, messageId, openMailboxId)
        onOpenMessageConsumed()
    }
    LaunchedEffect(state.unauthorized) {
        if (state.unauthorized) onUnauthorized()
    }
    LaunchedEffect(state.selectedMessageId) {
        onMessageOpenStateChange(state.selectedMessageId != null)
    }

    val composeLauncher = rememberLauncherForActivityResult(
        ActivityResultContracts.StartActivityForResult(),
    ) {
        vm.load(session, forceRefresh = true)
    }

    val openCompose: () -> Unit = {
        val account = state.selectedFolder?.let { folder ->
            state.accounts.find { it.id == folder.accountId }
        } ?: state.accounts.firstOrNull()
        if (account == null || account.id <= 0 || account.email.isBlank()) {
            Toast.makeText(context, "Учётная запись почты не найдена", Toast.LENGTH_SHORT).show()
        } else {
            composeLauncher.launch(
                MailComposeActivity.intent(
                    context,
                    MailComposeLaunch(
                        username = session.username,
                        password = session.appPassword,
                        serverUrl = session.serverUrl,
                        trustAllCerts = session.trustAllCerts,
                        accountId = account.id,
                        accountEmail = account.email,
                        accountName = account.name,
                        mailboxId = state.selectedFolder?.mailboxId,
                    ),
                ),
            )
        }
    }

    val openComposeForMessageWithSchedule: (
        MailComposeMode,
        MailMessageDetail,
        MailSendLaterPreset,
        Long?,
    ) -> Unit = { mode, detail, sendPreset, customSendAtEpochSeconds ->
        val account = state.selectedFolder?.let { folder ->
            state.accounts.find { it.id == folder.accountId }
        } ?: state.accounts.firstOrNull()
        if (account == null || account.id <= 0 || account.email.isBlank()) {
            Toast.makeText(context, "Учётная запись почты не найдена", Toast.LENGTH_SHORT).show()
        } else {
            val base = MailComposeLaunch(
                username = session.username,
                password = session.appPassword,
                serverUrl = session.serverUrl,
                trustAllCerts = session.trustAllCerts,
                accountId = account.id,
                accountEmail = account.email,
                accountName = account.name,
                mailboxId = state.selectedFolder?.mailboxId,
                initialSendPreset = sendPreset,
                initialCustomSendAtEpochSeconds = customSendAtEpochSeconds,
            )
            composeLauncher.launch(
                MailComposeActivity.intent(
                    context,
                    MailReplyHelper.buildComposeLaunch(base, detail, mode, account.email),
                ),
            )
        }
    }

    val openComposeForMessage: (MailComposeMode, MailMessageDetail) -> Unit = { mode, detail ->
        openComposeForMessageWithSchedule(mode, detail, MailSendLaterPreset.NOW, null)
    }

    val openScheduledReplyForMessage: (MailMessageDetail, MailSendLaterOption) -> Unit = { detail, option ->
        if (option.preset != MailSendLaterPreset.NOW) {
            openComposeForMessageWithSchedule(
                MailComposeMode.REPLY,
                detail,
                option.preset,
                option.sendAtEpochSeconds,
            )
        }
    }

    var searchFilterOpen by remember { mutableStateOf(false) }
    var moveSheetOpen by remember { mutableStateOf(false) }
    var tagsSheetOpen by remember { mutableStateOf(false) }
    var snoozeSheetOpen by remember { mutableStateOf(false) }

    val mailBackState = MailBackStackState(
        searchFilterOpen = searchFilterOpen,
        snoozeSheetOpen = snoozeSheetOpen,
        tagsSheetOpen = tagsSheetOpen,
        moveSheetOpen = moveSheetOpen,
        settingsOpen = settingsOpen,
        editingAccountId = state.editingAccountId,
        sidebarOpen = sidebarOpen,
        messageOpen = state.selectedMessageId != null,
    )
    val mailCanGoBack = mailBackState.canGoBack()
    val mailNavigateBack: () -> Boolean = {
        navigateMailBack(
            state = mailBackState,
            closeSearchFilter = { searchFilterOpen = false },
            closeSnoozeSheet = { snoozeSheetOpen = false },
            closeTagsSheet = { tagsSheetOpen = false },
            closeMoveSheet = { moveSheetOpen = false },
            closeAccountSettings = { vm.closeAccountSettings() },
            closeSettings = {
                vm.closeAccountSettings()
                onSettingsOpenChange(false)
            },
            closeSidebar = { onSidebarOpenChange(false) },
            closeMessage = { vm.closeMessage() },
        )
    }

    MailSettingsSheet(
        visible = settingsOpen,
        serverUrl = session.serverUrl,
        accounts = state.accounts,
        mailboxes = state.mailboxes,
        appSettings = state.appSettings,
        appSettingsLoading = state.appSettingsLoading,
        appSettingsSaving = state.appSettingsSaving,
        editingAccountId = state.editingAccountId,
        accountSettings = state.accountSettings,
        accountSettingsLoading = state.accountSettingsLoading,
        accountSettingsSaving = state.accountSettingsSaving,
        onLoadAppSettings = { vm.loadAppSettings(session) },
        onAccountClick = { vm.openAccountSettings(session, it) },
        onAccountSettingsBack = { vm.closeAccountSettings() },
        onSaveAccountSettings = { vm.saveAccountSettings(session, it) },
        onShowThreadedChange = { vm.setShowThreaded(session, it) },
        onHighlightExternalChange = { vm.setHighlightExternalAddresses(session, it) },
        onRemoveTrustedSender = { vm.removeTrustedSender(session, it) },
        onCreateTextBlock = { title, content -> vm.createTextBlock(session, title, content) },
        onDeleteTextBlock = { vm.deleteTextBlock(session, it) },
        onDismiss = {
            vm.closeAccountSettings()
            onSettingsOpenChange(false)
        },
        onSwipeBack = { mailNavigateBack() },
        onOpenSieveFilters = { accountId -> vm.openSieveFilters(session, accountId) },
    )

    MailSieveFiltersSheet(
        visible = state.sieveAccountId != null,
        rules = state.sieveRules,
        folders = state.mailboxes
            .filter { it.accountId == state.sieveAccountId && !it.isInbox && it.specialRole.isNullOrBlank() }
            .map { it.displayName },
        loading = state.sieveLoading,
        saving = state.sieveSaving,
        error = state.sieveError,
        onDismiss = { vm.closeSieveFilters() },
        onAdd = { rule -> vm.addFilterRule(session, rule) },
        onUpdate = { index, rule -> vm.updateFilterRule(session, index, rule) },
        onDelete = { index -> vm.deleteFilterRule(session, index) },
        onToggle = { index, enable -> vm.toggleFilterRule(session, index, enable) },
    )

    BackHandler(enabled = mailCanGoBack) {
        mailNavigateBack()
    }
    F7OverlayDismissHandler(
        enabled = mailCanGoBack,
        onDismiss = { mailNavigateBack() },
    )

    MailSearchParamsSheet(
        visible = searchFilterOpen,
        params = state.searchParams,
        onDismiss = { searchFilterOpen = false },
        onSearch = { vm.applySearchParams(session, it) },
        onClear = { vm.clearSearchParams(session) },
    )

    Box(
        modifier = modifier
            .fillMaxSize()
            .f7SwipeFromRightToDismiss(
                enabled = mailCanGoBack,
                onDismiss = { mailNavigateBack() },
            ),
    ) {
    if (state.selectedMessageId != null) {
        val messageAccountId = state.selectedFolder?.accountId
            ?: state.accounts.firstOrNull()?.id
        val moveMailboxes = remember(state.mailboxes, messageAccountId) {
            state.mailboxes.filter { mailbox ->
                messageAccountId == null || mailbox.accountId == messageAccountId
            }
        }
        val availableTags = remember(state.messages) {
            state.messages
                .flatMap { it.tags }
                .filter { it.imapLabel.isNotBlank() }
                .distinctBy { it.imapLabel }
        }
        val snoozeOptions = remember { MailComposeSchedule.options() }
        Box(
            modifier = Modifier
                .fillMaxSize()
                .f7SwipeFromRightToDismiss(
                    enabled = mailCanGoBack,
                    onDismiss = { mailNavigateBack() },
                ),
        ) {
            val detail = state.messageDetail
            val htmlBodyReady = detail != null && detail.looksLikeHtml() && detail.htmlBodyForDisplay().isNotBlank()
            Column(modifier = Modifier.fillMaxSize()) {
                if (!state.error.isNullOrBlank()) {
                    Text(
                        text = state.error.orEmpty(),
                        color = F7Colors.Error,
                        style = MaterialTheme.typography.bodyMedium,
                        modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
                    )
                }
                if (htmlBodyReady) {
                    Column(
                        modifier = Modifier
                            .fillMaxWidth()
                            .weight(1f, fill = false)
                            .verticalScroll(rememberScrollState()),
                    ) {
                        MailMessageDetailHeader(
                            detail = detail,
                            threadMessages = state.threadMessages,
                            threadExpanded = state.threadExpanded,
                            session = session,
                            messageAccountId = messageAccountId,
                            vm = vm,
                            openComposeForMessage = openComposeForMessage,
                            onTagsSheetOpen = { tagsSheetOpen = true },
                            onMoveSheetOpen = { moveSheetOpen = true },
                            onSnoozeSheetOpen = { snoozeSheetOpen = true },
                            closeMessage = { mailNavigateBack() },
                        )
                        MailMessageDetailView(
                            session = session,
                            detail = detail,
                            loading = state.messageLoading,
                            httpClient = httpClient,
                            modifier = Modifier
                                .fillMaxWidth()
                                .padding(horizontal = 16.dp),
                            showHeader = false,
                            htmlBodyExternal = true,
                            onAttachmentClick = { attachment ->
                                val messageId = detail.id
                                vm.downloadAttachment(session, messageId, attachment) { result ->
                                    result.onSuccess { bytes ->
                                        val safeName = attachment.fileName.replace(Regex("[^a-zA-Z0-9._-]"), "_")
                                        val file = File(context.cacheDir, "mail_$safeName")
                                        file.writeBytes(bytes)
                                        LocalFileOpener.openExternal(context, file, attachment.mime)
                                    }.onFailure {
                                        Toast.makeText(context, it.message ?: "Ошибка загрузки", Toast.LENGTH_SHORT).show()
                                    }
                                }
                            },
                        )
                    }
                    key(detail.id) {
                        MailMessageBodyView(
                            session = session,
                            messageId = detail.id,
                            html = detail.htmlBodyForDisplay(),
                            attachments = detail.attachments,
                            client = httpClient,
                            modifier = Modifier
                                .weight(1f)
                                .fillMaxWidth()
                                .padding(horizontal = 16.dp),
                        )
                    }
                } else {
                    Column(
                        modifier = Modifier
                            .weight(1f)
                            .verticalScroll(rememberScrollState()),
                    ) {
                        MailMessageDetailHeader(
                            detail = detail,
                            threadMessages = state.threadMessages,
                            threadExpanded = state.threadExpanded,
                            session = session,
                            messageAccountId = messageAccountId,
                            vm = vm,
                            openComposeForMessage = openComposeForMessage,
                            onTagsSheetOpen = { tagsSheetOpen = true },
                            onMoveSheetOpen = { moveSheetOpen = true },
                            onSnoozeSheetOpen = { snoozeSheetOpen = true },
                            closeMessage = { mailNavigateBack() },
                        )
                        MailMessageDetailView(
                            session = session,
                            detail = detail,
                            loading = state.messageLoading,
                            httpClient = httpClient,
                            modifier = Modifier
                                .fillMaxWidth()
                                .padding(horizontal = 16.dp),
                            showHeader = false,
                            htmlBodyExternal = false,
                            onAttachmentClick = { attachment ->
                                val messageId = detail?.id ?: return@MailMessageDetailView
                                vm.downloadAttachment(session, messageId, attachment) { result ->
                                    result.onSuccess { bytes ->
                                        val safeName = attachment.fileName.replace(Regex("[^a-zA-Z0-9._-]"), "_")
                                        val file = File(context.cacheDir, "mail_$safeName")
                                        file.writeBytes(bytes)
                                        LocalFileOpener.openExternal(context, file, attachment.mime)
                                    }.onFailure {
                                        Toast.makeText(context, it.message ?: "Ошибка загрузки", Toast.LENGTH_SHORT).show()
                                    }
                                }
                            },
                        )
                        Spacer(Modifier.height(120.dp))
                    }
                }
            }
            // Зона свайпа справа — WebView не отдаёт жесты Compose, поэтому ловим на краю поверх.
            Box(
                modifier = Modifier
                    .align(Alignment.CenterEnd)
                    .fillMaxHeight()
                    .width(24.dp)
                    .f7SwipeFromRightToDismiss(
                        enabled = mailCanGoBack,
                        edgeFraction = 1f,
                        onDismiss = { mailNavigateBack() },
                    ),
            )
            state.messageDetail?.let { detail ->
                MailReplyFab(
                    serverUrl = session.serverUrl,
                    onClick = { openComposeForMessage(MailComposeMode.REPLY, detail) },
                    modifier = Modifier
                        .align(Alignment.BottomEnd)
                        .padding(end = 16.dp, bottom = 88.dp),
                )
            }
            MailMoveMailboxSheet(
                visible = moveSheetOpen,
                mailboxes = moveMailboxes,
                onDismiss = { moveSheetOpen = false },
                onSelect = { mailbox ->
                    state.messageDetail?.let { detail ->
                        vm.moveMessage(session, detail.id, mailbox.id)
                    }
                },
            )
            MailMessageTagsSheet(
                visible = tagsSheetOpen,
                availableTags = availableTags,
                selectedTags = state.messageDetail?.tags.orEmpty(),
                onDismiss = { tagsSheetOpen = false },
                onToggle = { tag ->
                    val detail = state.messageDetail ?: return@MailMessageTagsSheet
                    val hasTag = detail.tags.any { it.imapLabel == tag.imapLabel }
                    vm.toggleMessageTag(session, detail.id, tag, add = !hasTag)
                },
            )
            MailSnoozeSheet(
                visible = snoozeSheetOpen,
                options = snoozeOptions,
                onDismiss = { snoozeSheetOpen = false },
                onSelect = { option ->
                    val detail = state.messageDetail ?: return@MailSnoozeSheet
                    openScheduledReplyForMessage(detail, option)
                },
            )
            // «Создать задачу/событие из письма» — паритет живого веба:
            // префилл темой письма и отправителем.
            val createDefaults = state.messageDetail?.let { detail ->
                detail.subject.ifBlank { "(без темы)" } to
                    "Из письма от ${detail.from.ifBlank { detail.fromEmail }}"
            }
            MailCreateTaskSheet(
                visible = state.createTaskSheetOpen,
                taskLists = state.createTargets,
                defaultSummary = createDefaults?.first.orEmpty(),
                defaultDescription = createDefaults?.second.orEmpty(),
                creating = state.creatingFromMessage,
                onDismiss = vm::dismissCreateSheets,
                onCreate = { listHref, summary, description, due ->
                    vm.createTaskFromMessage(session, listHref, summary, description, due)
                },
            )
            MailCreateEventSheet(
                visible = state.createEventSheetOpen,
                calendars = state.createTargets,
                defaultTitle = createDefaults?.first.orEmpty(),
                defaultDescription = createDefaults?.second.orEmpty(),
                creating = state.creatingFromMessage,
                onDismiss = vm::dismissCreateSheets,
                onCreate = { calHref, title, description, date, start, end, allDay ->
                    vm.createEventFromMessage(session, calHref, title, description, date, start, end, allDay)
                },
            )
        }
    } else {
        MailInboxScreen(
            session = session,
            modifier = Modifier.fillMaxSize(),
            selectedFolder = state.selectedFolder,
            loading = state.loading && state.messages.isEmpty() && state.outboxMessages.isEmpty(),
            loadingMore = state.loadingMore,
            error = state.error,
            searchQuery = state.searchQuery,
            searchParams = state.searchParams,
            isOutbox = state.selectedFolder?.specialRole == "outbox",
            messages = state.messages,
            outboxMessages = state.outboxMessages,
            nextCursor = state.nextCursor,
            onSearchChange = { vm.setSearchQuery(session, it) },
            onQuickFilterToggle = { vm.toggleQuickFilter(session, it) },
            onComposeClick = openCompose,
            onFilterClick = { searchFilterOpen = true },
            onMessageClick = { vm.openMessage(session, it) },
            onOutboxRetry = { vm.retryOutboxMessage(session, it) },
            onOutboxDelete = { vm.deleteOutboxMessage(session, it) },
            onLoadMore = { vm.loadMore(session) },
            refreshing = state.loading,
            onRefresh = { vm.load(session, forceRefresh = true) },
        )
    }
        MailNavigationSidebar(
            serverUrl = session.serverUrl,
            visible = sidebarOpen,
            accounts = state.accounts,
            folders = state.folders,
            selected = state.selectedFolder,
            collapsedAccountIds = state.collapsedAccountIds,
            onDismiss = { onSidebarOpenChange(false) },
            onComposeClick = {
                onSidebarOpenChange(false)
                openCompose()
            },
            onRefreshClick = { vm.load(session, forceRefresh = true) },
            onFolderClick = {
                vm.selectFolder(session, it)
                onSidebarOpenChange(false)
            },
            onToggleAccountCollapsed = { vm.toggleAccountCollapsed(it) },
            onPriorityInboxClick = {
                state.folders.firstOrNull { it.specialRole == "inbox" && it.filter == MailListFilter.ALL }
                    ?.let {
                        vm.selectFolder(session, it)
                        onSidebarOpenChange(false)
                    }
            },
            onOutboxClick = {
                vm.selectOutbox(session)
                onSidebarOpenChange(false)
            },
        )
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun MailInboxScreen(
    session: AuthSession,
    modifier: Modifier = Modifier,
    selectedFolder: MailFolderEntry?,
    loading: Boolean,
    loadingMore: Boolean,
    error: String?,
    searchQuery: String,
    searchParams: MailSearchParams,
    isOutbox: Boolean,
    messages: List<MailMessage>,
    outboxMessages: List<MailOutboxMessage>,
    nextCursor: Long?,
    onSearchChange: (String) -> Unit,
    onQuickFilterToggle: (MailQuickFilter) -> Unit,
    onComposeClick: () -> Unit,
    onFilterClick: () -> Unit,
    onMessageClick: (Int) -> Unit,
    onOutboxRetry: (Int) -> Unit,
    onOutboxDelete: (Int) -> Unit,
    onLoadMore: () -> Unit,
    refreshing: Boolean = false,
    onRefresh: () -> Unit = {},
) {
    val listItems = remember(messages) { buildMailInboxListItems(messages) }
    val folderTitle = mailFolderListTitle(selectedFolder, searchQuery, searchParams)
    Column(
        modifier = modifier
            .fillMaxSize()
            .padding(horizontal = 16.dp, vertical = 12.dp),
        verticalArrangement = Arrangement.spacedBy(12.dp),
    ) {
        if (!isOutbox) {
            MailInboxToolbar(
                serverUrl = session.serverUrl,
                query = searchQuery,
                onQueryChange = onSearchChange,
                onComposeClick = onComposeClick,
                onFilterClick = onFilterClick,
            )
            MailQuickFilterChips(
                searchParams = searchParams,
                onFilterToggle = onQuickFilterToggle,
            )
        } else {
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceBetween,
                verticalAlignment = Alignment.CenterVertically,
            ) {
                Text(
                    "Исходящие",
                    style = MaterialTheme.typography.titleMedium,
                    fontWeight = FontWeight.SemiBold,
                    color = F7Colors.TextPrimary,
                )
                if (outboxMessages.isNotEmpty()) {
                    Text(
                        "${outboxMessages.size}",
                        style = MaterialTheme.typography.bodyMedium,
                        color = F7Colors.TextSecondary,
                    )
                }
            }
        }
        if (!folderTitle.isNullOrBlank()) {
            Text(
                folderTitle,
                style = MaterialTheme.typography.titleMedium,
                fontWeight = FontWeight.SemiBold,
                color = F7Colors.TextPrimary,
            )
        }
        PullToRefreshBox(
            isRefreshing = refreshing && !loading,
            onRefresh = onRefresh,
            modifier = Modifier
                .weight(1f)
                .fillMaxWidth(),
        ) {
        Box(modifier = Modifier.fillMaxSize()) {
            if (loading) {
                Box(
                    modifier = Modifier.fillMaxSize(),
                    contentAlignment = Alignment.Center,
                ) {
                    CircularProgressIndicator(color = F7Colors.Primary)
                }
                return@Box
            }
            if (isOutbox) {
                if (outboxMessages.isEmpty()) {
                    Box(
                        modifier = Modifier.fillMaxSize(),
                        contentAlignment = Alignment.Center,
                    ) {
                        Text("Нет неотправленных писем", color = F7Colors.TextSecondary)
                    }
                } else {
                    LazyColumn(
                        modifier = Modifier.fillMaxSize(),
                        verticalArrangement = Arrangement.spacedBy(8.dp),
                    ) {
                        items(outboxMessages, key = { it.id }) { message ->
                            MailOutboxRow(
                                message = message,
                                onRetry = { onOutboxRetry(message.id) },
                                onDelete = { onOutboxDelete(message.id) },
                            )
                        }
                    }
                }
                return@Box
            }
            if (messages.isEmpty() && !error.isNullOrBlank()) {
                F7ErrorState(
                    message = error,
                    onRetry = onRefresh,
                    modifier = Modifier.fillMaxSize(),
                )
            } else if (messages.isEmpty()) {
                // Живая тема: пустая папка — цветная иконка почты 64dp + текст 16/20
                Column(
                    modifier = Modifier.fillMaxSize(),
                    verticalArrangement = Arrangement.Center,
                    horizontalAlignment = Alignment.CenterHorizontally,
                ) {
                    coil.compose.AsyncImage(
                        model = "file:///android_asset/mailchrome/mail-icon-color.svg",
                        contentDescription = null,
                        modifier = Modifier.size(64.dp),
                    )
                    Spacer(Modifier.height(12.dp))
                    Text(
                        if (searchQuery.isNotBlank() || searchParams.isActive()) {
                            "Ничего не найдено"
                        } else {
                            "Нет писем"
                        },
                        color = F7Colors.TextSecondary,
                    )
                }
            } else {
                val listState = rememberLazyListState()
                LaunchedEffect(listState, messages.size, nextCursor, loadingMore) {
                    snapshotFlow {
                        val info = listState.layoutInfo
                        val lastVisible = info.visibleItemsInfo.lastOrNull()?.index ?: 0
                        val total = info.totalItemsCount
                        total > 0 && lastVisible >= total - 3
                    }
                        .distinctUntilChanged()
                        .collect { nearEnd ->
                            if (nearEnd && nextCursor != null && !loadingMore) {
                                onLoadMore()
                            }
                        }
                }
                LazyColumn(
                    state = listState,
                    modifier = Modifier.fillMaxSize(),
                    // Живая тема: зазор между карточками писем 2px
                    verticalArrangement = Arrangement.spacedBy(2.dp),
                ) {
                    items(
                        listItems,
                        key = { item ->
                            when (item) {
                                is MailInboxListItem.YearHeader -> "year-${item.year}"
                                is MailInboxListItem.MessageItem -> item.message.id
                            }
                        },
                    ) { item ->
                        when (item) {
                            is MailInboxListItem.YearHeader -> MailYearHeader(item.year)
                            is MailInboxListItem.MessageItem -> MailEnvelopeRow(
                                message = item.message,
                                serverUrl = session.serverUrl,
                                onClick = { onMessageClick(item.message.id) },
                            )
                        }
                    }
                    if (loadingMore) {
                        item {
                            Box(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .padding(16.dp),
                                contentAlignment = Alignment.Center,
                            ) {
                                CircularProgressIndicator(
                                    modifier = Modifier.height(24.dp),
                                    color = F7Colors.Primary,
                                    strokeWidth = 2.dp,
                                )
                            }
                        }
                    }
                }
            }
        }
        }
    }
}

@Composable
private fun MailMessageDetailHeader(
    detail: MailMessageDetail?,
    threadMessages: List<MailMessage>,
    threadExpanded: Boolean,
    session: AuthSession,
    messageAccountId: Int?,
    vm: MailViewModel,
    openComposeForMessage: (MailComposeMode, MailMessageDetail) -> Unit,
    onTagsSheetOpen: () -> Unit,
    onMoveSheetOpen: () -> Unit,
    onSnoozeSheetOpen: () -> Unit,
    closeMessage: () -> Unit,
) {
    val message = detail ?: return
    MailThreadSection(
        currentMessageId = message.id,
        threadMessages = threadMessages,
        expanded = threadExpanded,
        onToggleExpanded = { vm.toggleThreadExpanded() },
        onMessageClick = { vm.openMessage(session, it) },
        modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
    )
    MailMessageSenderCard(
        fromName = message.from,
        fromEmail = message.fromEmail,
        subject = message.subject,
        to = message.to,
        cc = message.cc,
        dateInt = message.dateInt,
        flagged = message.flags.flagged,
        seen = message.flags.seen,
        important = message.flags.important,
        serverUrl = session.serverUrl,
        onReply = { openComposeForMessage(MailComposeMode.REPLY, message) },
        onForward = { openComposeForMessage(MailComposeMode.FORWARD, message) },
        onToggleStar = { vm.toggleStar(session, message.id) },
        onMarkUnread = {
            if (message.flags.seen) vm.toggleRead(session, message.id)
        },
        onDelete = {
            vm.deleteMessage(session, message.id)
            closeMessage()
        },
        onToggleImportant = { vm.toggleImportant(session, message.id) },
        onMarkSpam = {
            messageAccountId?.let { vm.markAsSpam(session, message.id, it) }
        },
        onEditTags = onTagsSheetOpen,
        onMove = onMoveSheetOpen,
        onSnooze = onSnoozeSheetOpen,
        onCreateTask = { vm.openCreateTaskSheet(session) },
        onCreateEvent = { vm.openCreateEventSheet(session) },
    )
}

@Composable
private fun MailMessageDetailView(
    session: AuthSession,
    detail: MailMessageDetail?,
    loading: Boolean,
    httpClient: okhttp3.OkHttpClient,
    modifier: Modifier = Modifier,
    showHeader: Boolean = true,
    htmlBodyExternal: Boolean = false,
    onAttachmentClick: (MailAttachment) -> Unit,
) {
    if (detail == null) {
        if (!showHeader) return
        Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
            CircularProgressIndicator(color = F7Colors.Primary)
        }
        return
    }

    val bodyLoading = loading && !detail.hasBodyContent()
    val embedded = !showHeader

    Column(
        modifier = modifier.then(
            if (showHeader) Modifier.fillMaxSize() else Modifier.fillMaxWidth(),
        ),
    ) {
        if (showHeader) {
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .background(F7Colors.Background)
                    .padding(bottom = 8.dp),
            ) {
                Text(
                    detail.subject.ifBlank { "(без темы)" },
                    style = MaterialTheme.typography.titleMedium,
                    fontWeight = FontWeight.SemiBold,
                    color = F7Colors.TextPrimary,
                    maxLines = 3,
                    overflow = TextOverflow.Ellipsis,
                )
                Spacer(Modifier.height(10.dp))
                Row(verticalAlignment = Alignment.CenterVertically) {
                    MailAvatar(name = detail.from, email = detail.fromEmail, modifier = Modifier.size(40.dp))
                    Spacer(Modifier.width(8.dp))
                    Column(modifier = Modifier.weight(1f)) {
                        Row(verticalAlignment = Alignment.CenterVertically) {
                            Text(
                                "От:",
                                style = MaterialTheme.typography.bodySmall,
                                color = F7Colors.TextSecondary,
                            )
                            Spacer(Modifier.width(6.dp))
                            Text(
                                detail.from.ifBlank { "Неизвестный" },
                                style = MaterialTheme.typography.bodyMedium,
                                fontWeight = FontWeight.Medium,
                                color = F7Colors.TextPrimary,
                                maxLines = 1,
                                overflow = TextOverflow.Ellipsis,
                                modifier = Modifier.weight(1f, fill = false),
                            )
                        }
                        if (detail.to.isNotBlank()) {
                            Text(
                                "Кому: ${detail.to}",
                                style = MaterialTheme.typography.bodySmall,
                                color = F7Colors.TextSecondary,
                                maxLines = 2,
                                overflow = TextOverflow.Ellipsis,
                                modifier = Modifier.padding(top = 2.dp),
                            )
                        }
                        if (detail.cc.isNotBlank()) {
                            Text(
                                "Копия: ${detail.cc}",
                                style = MaterialTheme.typography.bodySmall,
                                color = F7Colors.TextSecondary,
                                maxLines = 2,
                                overflow = TextOverflow.Ellipsis,
                                modifier = Modifier.padding(top = 2.dp),
                            )
                        }
                    }
                }
                if (detail.dateInt > 0) {
                    Text(
                        formatMessageDate(detail.dateInt),
                        style = MaterialTheme.typography.labelSmall,
                        color = F7Colors.TextSecondary,
                        modifier = Modifier.padding(top = 6.dp),
                    )
                }
            }
        }
        if (detail.attachments.isNotEmpty()) {
            Spacer(Modifier.height(8.dp))
            detail.attachments.forEach { attachment ->
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .clip(RoundedCornerShape(8.dp))
                        .background(F7Colors.SurfaceMuted)
                        .clickable { onAttachmentClick(attachment) }
                        .padding(horizontal = 12.dp, vertical = 8.dp),
                    verticalAlignment = Alignment.CenterVertically,
                ) {
                    Text("📎", modifier = Modifier.padding(end = 8.dp))
                    Column(modifier = Modifier.weight(1f)) {
                        Text(
                            attachment.fileName,
                            style = MaterialTheme.typography.bodyMedium,
                            maxLines = 1,
                            overflow = TextOverflow.Ellipsis,
                        )
                        val sizeLabel = formatAttachmentSize(attachment.size)
                        if (sizeLabel.isNotBlank()) {
                            Text(sizeLabel, style = MaterialTheme.typography.labelSmall, color = F7Colors.TextSecondary)
                        }
                    }
                }
                Spacer(Modifier.height(4.dp))
            }
        }
        if (bodyLoading) {
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(vertical = 32.dp),
                contentAlignment = Alignment.Center,
            ) {
                CircularProgressIndicator(
                    modifier = Modifier.height(28.dp),
                    color = F7Colors.Primary,
                    strokeWidth = 2.dp,
                )
            }
        } else if (!htmlBodyExternal) {
            if (detail.looksLikeHtml() && detail.htmlBodyForDisplay().isNotBlank()) {
                key(detail.id) {
                    MailMessageBodyView(
                        session = session,
                        messageId = detail.id,
                        html = detail.htmlBodyForDisplay(),
                        attachments = detail.attachments,
                        client = httpClient,
                        modifier = Modifier
                            .fillMaxWidth()
                            .then(if (showHeader) Modifier.weight(1f) else Modifier),
                    )
                }
            } else {
                val plainBody = MailBodyHtml.normalizePlainForDisplay(
                    detail.plainBodyForDisplay().ifBlank { "(пустое письмо)" },
                )
                if (embedded) {
                    Text(
                        plainBody,
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(top = 8.dp),
                        style = MaterialTheme.typography.bodyMedium,
                    )
                } else {
                    Column(
                        modifier = Modifier
                            .fillMaxWidth()
                            .weight(1f)
                            .verticalScroll(rememberScrollState())
                            .padding(top = 8.dp),
                    ) {
                        Text(plainBody, style = MaterialTheme.typography.bodyMedium)
                    }
                }
            }
        }
    }
}
