package com.github.nullptroma.wallenc.ui.screens.sync

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.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.clickable
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
import androidx.compose.material.icons.rounded.Add
import androidx.compose.material.icons.rounded.Delete
import androidx.compose.material.icons.rounded.ExpandLess
import androidx.compose.material.icons.rounded.ExpandMore
import androidx.compose.material.icons.rounded.Sync
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
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.layout.navigationBarsPadding
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.github.nullptroma.wallenc.ui.R
import com.github.nullptroma.wallenc.ui.elements.AnimatedFloatingBackButton
import com.github.nullptroma.wallenc.ui.resources.UserNotification
import com.github.nullptroma.wallenc.domain.interfaces.StorageSyncGroupEncryptionKind
import java.util.UUID

@Composable
fun StorageSyncScreen(
    modifier: Modifier = Modifier,
    viewModel: StorageSyncViewModel = hiltViewModel(),
) {
    val state by viewModel.state.collectAsStateWithLifecycle()
    val snackbarHostState = remember { SnackbarHostState() }
    val userMessageText = when (val um = state.userMessage) {
        is UserNotification.TextRes -> {
            if (um.formatArgs.isEmpty()) {
                stringResource(um.id)
            } else {
                stringResource(um.id, *um.formatArgs.toTypedArray())
            }
        }
        is UserNotification.Plain -> um.message
        null -> null
    }
    LaunchedEffect(state.userMessage) {
        val text = userMessageText ?: return@LaunchedEffect
        snackbarHostState.showSnackbar(text)
        viewModel.consumeUserMessage()
    }

    var pendingRemoveGroupId by remember { mutableStateOf<String?>(null) }
    var pendingRemoveStorage by remember { mutableStateOf<Pair<String, UUID>?>(null) }
    val pickerGroupId = state.pickerGroupId
    if (pickerGroupId != null) {
        StoragePickerScreen(
            modifier = modifier,
            state = state,
            groupId = pickerGroupId,
            onBack = viewModel::closePicker,
            onAddStorage = viewModel::addStorageToCurrentGroup,
            onToggleVault = viewModel::toggleVaultExpanded,
            snackbarHostState = snackbarHostState,
        )
        return
    }

    val storageByUuid = state.vaults
        .flatMap { vault -> flattenStorageTree(vault.storages) }
        .associateBy { it.uuid }

    val groupEditLocked = state.isBusy || state.isStorageSyncRunning

    Scaffold(
        modifier = modifier,
        contentWindowInsets = WindowInsets(0.dp),
        snackbarHost = { SnackbarHost(snackbarHostState) },
        floatingActionButton = {
            FloatingActionButton(
                onClick = { if (!groupEditLocked) viewModel.createGroup() },
                modifier = Modifier.alpha(if (groupEditLocked) 0.38f else 1f),
            ) {
                Icon(
                    imageVector = Icons.Rounded.Add,
                    contentDescription = stringResource(R.string.sync_fab_create_group_cd),
                )
            }
        },
    ) { inner ->
        Column(
            modifier = Modifier
                .padding(inner)
                .fillMaxSize()
                .padding(16.dp),
            verticalArrangement = Arrangement.spacedBy(12.dp),
        ) {
            Row(
                horizontalArrangement = Arrangement.spacedBy(8.dp),
                verticalAlignment = Alignment.CenterVertically,
            ) {
                Button(
                    onClick = viewModel::runSyncNow,
                    enabled = !groupEditLocked && !state.anyVaultStoragesScanning,
                ) {
                    Icon(
                        Icons.Rounded.Sync,
                        contentDescription = null,
                        modifier = Modifier.padding(end = 8.dp),
                    )
                    Text(stringResource(id = R.string.sync_run_now))
                }
            }

            when {
                state.isStorageSyncRunning -> {
                    Column(
                        modifier = Modifier.fillMaxWidth(),
                        verticalArrangement = Arrangement.spacedBy(6.dp),
                    ) {
                        Text(
                            text = stringResource(R.string.sync_progress_section_title),
                            style = MaterialTheme.typography.titleSmall,
                        )
                        state.storageSyncProgressLabel?.let { line ->
                            Text(
                                text = line,
                                style = MaterialTheme.typography.bodySmall,
                                color = MaterialTheme.colorScheme.onSurfaceVariant,
                            )
                        }
                        val frac = state.storageSyncProgressFraction
                        if (frac != null) {
                            LinearProgressIndicator(
                                progress = { frac },
                                modifier = Modifier.fillMaxWidth(),
                            )
                        } else {
                            LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
                        }
                    }
                }
                state.isBusy -> {
                    Column(
                        modifier = Modifier.fillMaxWidth(),
                        verticalArrangement = Arrangement.spacedBy(6.dp),
                    ) {
                        Text(
                            text = stringResource(R.string.sync_groups_busy_section_title),
                            style = MaterialTheme.typography.titleSmall,
                        )
                        LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
                    }
                }
            }

            Text(
                text = stringResource(id = R.string.sync_groups_title),
                style = MaterialTheme.typography.titleMedium,
            )
            LazyColumn(
                modifier = Modifier.fillMaxSize(),
                verticalArrangement = Arrangement.spacedBy(10.dp),
            ) {
                items(state.groups, key = { it.id }) { group ->
                    Card(
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(vertical = 2.dp),
                        colors = CardDefaults.cardColors(
                            containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
                        ),
                    ) {
                        Column(
                            modifier = Modifier
                                .fillMaxWidth()
                                .padding(12.dp),
                            verticalArrangement = Arrangement.spacedBy(8.dp),
                        ) {
                            Row(
                                modifier = Modifier.fillMaxWidth(),
                                horizontalArrangement = Arrangement.SpaceBetween,
                                verticalAlignment = Alignment.Top,
                            ) {
                                Text(
                                    text = group.id,
                                    style = MaterialTheme.typography.titleMedium,
                                    modifier = Modifier
                                        .weight(1f)
                                        .padding(end = 4.dp),
                                    maxLines = 4,
                                )
                                IconButton(
                                    onClick = { pendingRemoveGroupId = group.id },
                                    enabled = !groupEditLocked,
                                ) {
                                    Icon(
                                        imageVector = Icons.Rounded.Delete,
                                        contentDescription = stringResource(id = R.string.sync_remove_group),
                                    )
                                }
                            }

                            if (group.storageUuids.isEmpty()) {
                                Text(
                                    text = stringResource(id = R.string.sync_group_empty),
                                    style = MaterialTheme.typography.bodySmall,
                                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                                )
                            } else {
                                if (group.incompatibleStorageUuids.isNotEmpty()) {
                                    Text(
                                        text = stringResource(
                                            id = R.string.sync_group_incompatible_warning,
                                            group.incompatibleStorageUuids.size,
                                        ),
                                        style = MaterialTheme.typography.bodySmall,
                                        color = MaterialTheme.colorScheme.error,
                                    )
                                }
                                Text(
                                    text = stringResource(
                                        R.string.sync_group_policy_line,
                                        groupPolicyLabel(group.encryptionKind),
                                    ),
                                    style = MaterialTheme.typography.bodySmall,
                                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                                )

                                group.storageUuids.forEach { storageUuid ->
                                    val storage = storageByUuid[storageUuid]
                                    val titleText = storage?.name
                                        ?: stringResource(R.string.sync_storage_missing_title)
                                    val encLabel = encryptionKindLabel(storage?.encryptionKind)
                                    val statusLine = when {
                                        storage != null && !storage.isReachable ->
                                            stringResource(R.string.sync_storage_unreachable)
                                        storage == null && state.anyVaultStoragesScanning ->
                                            stringResource(R.string.sync_storage_pending_vault_scan)
                                        storage == null && !state.anyVaultStoragesScanning ->
                                            stringResource(R.string.sync_storage_not_in_vaults)
                                        else -> null
                                    }
                                    val statusColor = when {
                                        storage != null && !storage.isReachable ->
                                            MaterialTheme.colorScheme.error
                                        storage == null ->
                                            MaterialTheme.colorScheme.tertiary
                                        else -> MaterialTheme.colorScheme.onSurfaceVariant
                                    }
                                    Card(
                                        modifier = Modifier.fillMaxWidth(),
                                        colors = CardDefaults.cardColors(
                                            containerColor = MaterialTheme.colorScheme.surfaceContainer,
                                        ),
                                    ) {
                                        Row(
                                            modifier = Modifier
                                                .fillMaxWidth()
                                                .padding(12.dp),
                                            horizontalArrangement = Arrangement.SpaceBetween,
                                            verticalAlignment = Alignment.Top,
                                        ) {
                                            Column(
                                                modifier = Modifier.weight(1f),
                                                verticalArrangement = Arrangement.spacedBy(4.dp),
                                            ) {
                                                Text(
                                                    text = titleText,
                                                    style = MaterialTheme.typography.titleSmall,
                                                )
                                                Text(
                                                    text = storageUuid.toString(),
                                                    style = MaterialTheme.typography.bodySmall,
                                                    fontFamily = FontFamily.Monospace,
                                                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                                                )
                                                statusLine?.let { line ->
                                                    Text(
                                                        text = line,
                                                        style = MaterialTheme.typography.bodySmall,
                                                        color = statusColor,
                                                    )
                                                }
                                                Text(
                                                    text = stringResource(
                                                        R.string.sync_storage_encryption_line,
                                                        encLabel,
                                                    ),
                                                    style = MaterialTheme.typography.bodySmall,
                                                )
                                            }
                                            IconButton(
                                                onClick = { pendingRemoveStorage = group.id to storageUuid },
                                                enabled = !groupEditLocked,
                                            ) {
                                                Icon(
                                                    imageVector = Icons.Rounded.Delete,
                                                    contentDescription = stringResource(id = R.string.sync_remove_storage),
                                                )
                                            }
                                        }
                                    }
                                }
                            }

                            HorizontalDivider(modifier = Modifier.padding(top = 4.dp))

                            Row(
                                modifier = Modifier.fillMaxWidth(),
                                horizontalArrangement = Arrangement.End,
                            ) {
                                IconButton(
                                    onClick = { viewModel.openPicker(group.id) },
                                    enabled = !groupEditLocked,
                                ) {
                                    Icon(
                                        imageVector = Icons.Rounded.Add,
                                        contentDescription = stringResource(id = R.string.sync_add_storage),
                                    )
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    if (pendingRemoveGroupId != null) {
        AlertDialog(
            onDismissRequest = { pendingRemoveGroupId = null },
            title = { Text(text = stringResource(id = R.string.sync_remove_group_confirm_title)) },
            text = {
                Text(
                    text = stringResource(
                        id = R.string.sync_remove_group_confirm_message,
                        pendingRemoveGroupId.orEmpty(),
                    ),
                )
            },
            confirmButton = {
                Button(
                    onClick = {
                        val groupId = pendingRemoveGroupId ?: return@Button
                        viewModel.removeGroup(groupId)
                        pendingRemoveGroupId = null
                    },
                ) {
                    Text(text = stringResource(id = R.string.sync_confirm_delete))
                }
            },
            dismissButton = {
                Button(onClick = { pendingRemoveGroupId = null }) {
                    Text(text = stringResource(id = R.string.sync_cancel))
                }
            },
        )
    }

    if (pendingRemoveStorage != null) {
        AlertDialog(
            onDismissRequest = { pendingRemoveStorage = null },
            title = { Text(text = stringResource(id = R.string.sync_remove_storage_confirm_title)) },
            text = {
                Text(
                    text = stringResource(
                        id = R.string.sync_remove_storage_confirm_message,
                        pendingRemoveStorage?.second.toString(),
                    ),
                )
            },
            confirmButton = {
                Button(
                    onClick = {
                        val payload = pendingRemoveStorage ?: return@Button
                        viewModel.removeStorageFromGroup(payload.first, payload.second)
                        pendingRemoveStorage = null
                    },
                ) {
                    Text(text = stringResource(id = R.string.sync_confirm_delete))
                }
            },
            dismissButton = {
                Button(onClick = { pendingRemoveStorage = null }) {
                    Text(text = stringResource(id = R.string.sync_cancel))
                }
            },
        )
    }
}

@Composable
private fun encryptionKindLabel(kind: StorageSyncEncryptionKind?): String {
    return when (kind) {
        null -> stringResource(R.string.sync_encryption_unknown)
        StorageSyncEncryptionKind.NotEncrypted -> stringResource(R.string.enc_status_not_encrypted)
        StorageSyncEncryptionKind.EncryptedOpened -> stringResource(R.string.enc_status_encrypted_open)
        StorageSyncEncryptionKind.EncryptedClosed -> stringResource(R.string.enc_status_encrypted)
    }
}

@Composable
private fun StoragePickerScreen(
    modifier: Modifier,
    state: StorageSyncScreenState,
    groupId: String,
    onBack: () -> Unit,
    onAddStorage: (UUID) -> Unit,
    onToggleVault: (UUID) -> Unit,
    snackbarHostState: SnackbarHostState,
) {
    val selected = state.groups.firstOrNull { it.id == groupId }?.storageUuids ?: emptySet()
    val groupEditLocked = state.isBusy || state.isStorageSyncRunning
    Scaffold(
        modifier = modifier,
        contentWindowInsets = WindowInsets(0.dp),
        snackbarHost = { SnackbarHost(snackbarHostState) },
    ) { inner ->
        Box(
            modifier = Modifier
                .padding(inner)
                .fillMaxSize(),
        ) {
            Column(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(horizontal = 16.dp, vertical = 12.dp),
                verticalArrangement = Arrangement.spacedBy(12.dp),
            ) {
                Text(
                    text = stringResource(id = R.string.sync_picker_title, groupId),
                    style = MaterialTheme.typography.titleLarge,
                )

            LazyColumn(
                modifier = Modifier.fillMaxSize(),
                verticalArrangement = Arrangement.spacedBy(10.dp),
            ) {
                items(state.vaults, key = { it.uuid }) { vault ->
                    Card(
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(vertical = 2.dp),
                        colors = CardDefaults.cardColors(
                            containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
                        ),
                    ) {
                        Column(
                            modifier = Modifier
                                .fillMaxWidth()
                                .padding(10.dp),
                            verticalArrangement = Arrangement.spacedBy(6.dp),
                        ) {
                            val expanded = vault.uuid in state.expandedVaultUuids
                            Row(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .clickable { onToggleVault(vault.uuid) },
                                horizontalArrangement = Arrangement.SpaceBetween,
                                verticalAlignment = Alignment.CenterVertically,
                            ) {
                            Text(
                                text = vault.title,
                                style = MaterialTheme.typography.titleSmall,
                                modifier = Modifier.weight(1f),
                            )
                            Icon(
                                imageVector = if (expanded) Icons.Rounded.ExpandLess else Icons.Rounded.ExpandMore,
                                contentDescription = if (expanded) {
                                    stringResource(R.string.sync_picker_collapse)
                                } else {
                                    stringResource(R.string.sync_picker_expand)
                                },
                            )
                        }
                        Text(
                            text = vault.type,
                            style = MaterialTheme.typography.bodySmall,
                            color = MaterialTheme.colorScheme.onSurfaceVariant,
                        )
                        Text(
                            text = vault.uuid.toString(),
                            style = MaterialTheme.typography.bodySmall,
                            fontFamily = FontFamily.Monospace,
                            color = MaterialTheme.colorScheme.onSurfaceVariant,
                        )

                        if (expanded) {
                                if (vault.storages.isEmpty()) {
                                    Text(
                                        text = stringResource(id = R.string.sync_picker_no_storages),
                                        style = MaterialTheme.typography.bodySmall,
                                    )
                                } else {
                                    vault.storages.forEach { storage ->
                                        StoragePickerNode(
                                            node = storage,
                                            depth = 0,
                                            selected = selected,
                                            isBusy = groupEditLocked,
                                            onAddStorage = onAddStorage,
                                        )
                                    }
                                }
                            }
                        }
                    }
                }
            }
            }
            AnimatedFloatingBackButton(
                visible = true,
                onClick = onBack,
                modifier = Modifier
                    .align(Alignment.BottomStart)
                    .navigationBarsPadding()
                    .padding(start = 12.dp, bottom = 12.dp),
            )
        }
    }
}

@Composable
private fun StoragePickerNode(
    node: StorageSyncStorageUi,
    depth: Int,
    selected: Set<UUID>,
    isBusy: Boolean,
    onAddStorage: (UUID) -> Unit,
) {
    val isSelected = node.uuid in selected
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(start = (depth * 14).dp),
        colors = CardDefaults.cardColors(
            containerColor = MaterialTheme.colorScheme.surfaceContainer,
        ),
    ) {
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(8.dp),
            horizontalArrangement = Arrangement.SpaceBetween,
            verticalAlignment = Alignment.Top,
        ) {
            Column(
                modifier = Modifier.weight(1f),
                verticalArrangement = Arrangement.spacedBy(4.dp),
            ) {
                Text(
                    text = node.name,
                    style = MaterialTheme.typography.bodyMedium,
                )
                Text(
                    text = node.uuid.toString(),
                    style = MaterialTheme.typography.bodySmall,
                    fontFamily = FontFamily.Monospace,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                )
                Text(
                    text = stringResource(
                        R.string.sync_storage_encryption_line,
                        encryptionKindLabel(node.encryptionKind),
                    ),
                    style = MaterialTheme.typography.bodySmall,
                )
                if (!node.isReachable) {
                    Text(
                        text = stringResource(R.string.sync_storage_unreachable),
                        style = MaterialTheme.typography.bodySmall,
                        color = MaterialTheme.colorScheme.error,
                    )
                }
            }
            IconButton(
                enabled = !isSelected && !isBusy,
                onClick = { onAddStorage(node.uuid) },
            ) {
                Icon(
                    imageVector = Icons.Rounded.Add,
                    contentDescription = stringResource(
                        if (isSelected) R.string.sync_picker_added else R.string.sync_picker_cd_add,
                    ),
                )
            }
        }
    }
    node.children.forEach { child ->
        StoragePickerNode(
            node = child,
            depth = depth + 1,
            selected = selected,
            isBusy = isBusy,
            onAddStorage = onAddStorage,
        )
    }
}

private fun flattenStorageTree(nodes: List<StorageSyncStorageUi>): List<StorageSyncStorageUi> {
    return nodes.flatMap { node ->
        listOf(node) + flattenStorageTree(node.children)
    }
}

@Composable
private fun groupPolicyLabel(kind: StorageSyncGroupEncryptionKind): String {
    return when (kind) {
        StorageSyncGroupEncryptionKind.UNSET -> stringResource(R.string.sync_group_policy_unset)
        StorageSyncGroupEncryptionKind.NONE -> stringResource(R.string.sync_group_policy_plain)
        StorageSyncGroupEncryptionKind.PASSWORD -> stringResource(R.string.sync_group_policy_password)
    }
}
