package com.tangem.feature.wallet.child.organizetokens.ui

import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.FabPosition
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.BottomFade
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig
import com.tangem.core.ui.components.buttons.actions.RoundedActionButton
import com.tangem.core.ui.components.token.TokenItem
import com.tangem.core.ui.components.tokenlist.ExpandedPortfolioHeader
import com.tangem.core.ui.components.tokenlist.internal.DraggableGroupTitleItem
import com.tangem.core.ui.event.EventEffect
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.reordarable.ReorderableItem
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.OrganizeTokensScreenTestTags
import com.tangem.core.ui.utils.WindowInsetsZero
import com.tangem.core.ui.utils.lazyListItemPosition
import com.tangem.feature.wallet.child.organizetokens.entity.DraggableItem
import com.tangem.feature.wallet.child.organizetokens.entity.OrganizeTokensListUM
import com.tangem.feature.wallet.child.organizetokens.entity.OrganizeTokensState
import com.tangem.feature.wallet.child.organizetokens.entity.RoundingModeUM
import com.tangem.feature.wallet.child.organizetokens.ui.preview.OrganizeTokensPreviewLegacy
import com.tangem.feature.wallet.impl.R
import org.burnoutcrew.reorderable.ReorderableLazyListState
import org.burnoutcrew.reorderable.rememberReorderableLazyListState
import org.burnoutcrew.reorderable.reorderable

@Composable
internal fun OrganizeTokensScreen(state: OrganizeTokensState, modifier: Modifier = Modifier) {
    BackHandler(onBack = state.onBackClick)

    val tokensListState = rememberLazyListState()

    Scaffold(
        modifier = modifier,
        topBar = {
            TopBar(
                config = state.header,
                tokensListState = tokensListState,
            )
        },
        contentWindowInsets = WindowInsetsZero,
        content = { paddingValues ->
            TokenList(
                modifier = Modifier
                    .padding(paddingValues)
                    .fillMaxSize(),
                listState = tokensListState,
                tokensListUM = state.tokenListUM,
                dndConfig = state.dndConfig,
                isBalanceHidden = state.isBalanceHidden,
            )
        },
        floatingActionButtonPosition = FabPosition.Center,
        floatingActionButton = {
            Box(modifier = Modifier.navigationBarsPadding()) {
                Actions(state.actions)
            }
        },
        containerColor = TangemTheme.colors.background.secondary,
    )

    EventEffect(state.scrollListToTop) {
        tokensListState.animateScrollToItem(index = 0)
    }
}

@Composable
private fun TokenList(
    listState: LazyListState,
    tokensListUM: OrganizeTokensListUM,
    dndConfig: OrganizeTokensState.DragAndDropConfig,
    isBalanceHidden: Boolean,
    modifier: Modifier = Modifier,
) {
    val hapticFeedback = LocalHapticFeedback.current
    val tokenList = tokensListUM.items
    Box(modifier = modifier) {
        val onDragEnd: (Int, Int) -> Unit = remember {
            { _, _ ->
                dndConfig.onItemDragEnd()
            }
        }
        val reorderableListState = rememberReorderableLazyListState(
            onMove = dndConfig.onItemDragged,
            listState = listState,
            canDragOver = dndConfig.canDragItemOver,
            onDragEnd = onDragEnd,
        )

        val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }

        val listContentPadding = PaddingValues(
            top = TangemTheme.dimens.spacing4,
            bottom = TangemTheme.dimens.spacing92 + bottomBarHeight,
            start = TangemTheme.dimens.spacing16,
            end = TangemTheme.dimens.spacing16,
        )

        // workaround to force recomposition of list items when the list is updated
        // because sometimes items disappear after reordering in 1.7.4+ compose-runtime version
        // check removing after update to compose-runtime 1.8.0+
        val forceRecompose = remember { mutableIntStateOf(0) }
        LaunchedEffect(tokenList) {
            forceRecompose.intValue++
        }

        LazyColumn(
            modifier = Modifier
                .align(Alignment.TopCenter)
                .reorderable(reorderableListState)
                .testTag(OrganizeTokensScreenTestTags.TOKENS_LAZY_LIST),
            state = reorderableListState.listState,
            contentPadding = listContentPadding,
        ) {
            itemsIndexed(
                items = tokenList,
                key = { _, item -> item.id },
            ) { index, item ->

                val onDragStart = remember(item) {
                    {
                        dndConfig.onItemDragStart(item)
                        hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
                    }
                }

                key(forceRecompose.intValue) {
                    DraggableItem(
                        index = index,
                        item = item,
                        reorderableState = reorderableListState,
                        onDragStart = onDragStart,
                        isBalanceHidden = isBalanceHidden,
                    )
                }
            }
        }

        BottomFade(modifier = Modifier.align(Alignment.BottomCenter))
    }
}

@Composable
private fun LazyItemScope.DraggableItem(
    index: Int,
    item: DraggableItem,
    reorderableState: ReorderableLazyListState,
    onDragStart: () -> Unit,
    isBalanceHidden: Boolean,
) {
    var isDragging by remember {
        mutableStateOf(value = false)
    }

    val itemModifier = Modifier.applyShapeAndShadow(item.roundingModeUM, item.showShadow)

    ReorderableItem(
        reorderableState = reorderableState,
        index = index,
        key = item.id,
    ) { isItemDragging ->
        isDragging = isItemDragging

        val modifierWithBackground = itemModifier
            .background(color = TangemTheme.colors.background.primary)
            .semantics { lazyListItemPosition = index }

        when (item) {
            is DraggableItem.GroupHeader -> DraggableGroupTitleItem(
                state = item.groupTitle,
                reorderableTokenListState = reorderableState,
                modifier = modifierWithBackground.testTag(OrganizeTokensScreenTestTags.GROUP_TITLE_ITEM),
            )
            is DraggableItem.Token -> TokenItem(
                modifier = modifierWithBackground.testTag(OrganizeTokensScreenTestTags.TOKEN_LIST_ITEM),
                state = item.tokenItemState,
                reorderableTokenListState = reorderableState,
                isBalanceHidden = isBalanceHidden,
            )
            // Should be presented in the list but remain invisible
            is DraggableItem.Placeholder -> Box(modifier = Modifier.fillMaxWidth())
            is DraggableItem.Portfolio -> ExpandedPortfolioHeader(
                state = item.tokenItemState,
                isCollapsable = false,
                composables = null,
                isBalanceHidden = isBalanceHidden,
                modifier = modifierWithBackground.fillMaxWidth(),
            )
        }
    }

    DisposableEffect(isDragging) {
        onDispose {
            if (isDragging) {
                onDragStart()
            }
        }
    }
}

@Suppress("LongMethod")
@Composable
private fun TopBar(
    config: OrganizeTokensState.HeaderConfig,
    tokensListState: LazyListState,
    modifier: Modifier = Modifier,
) {
    val isElevationEnabled by remember {
        derivedStateOf {
            tokensListState.firstVisibleItemScrollOffset > 0
        }
    }
    val elevation by animateDpAsState(
        targetValue = if (isElevationEnabled) TangemTheme.dimens.elevation4 else TangemTheme.dimens.elevation0,
        label = "top_bar_shadow_elevation",
    )

    Column(
        modifier = modifier
            .shadow(elevation)
            .background(TangemTheme.colors.background.secondary)
            .statusBarsPadding()
            .padding(horizontal = TangemTheme.dimens.spacing16)
            .fillMaxWidth(),
    ) {
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .heightIn(min = TangemTheme.dimens.size56),
            contentAlignment = Alignment.CenterStart,
        ) {
            Text(
                text = stringResourceSafe(id = R.string.organize_tokens_title),
                style = TangemTheme.typography.subtitle1,
                color = TangemTheme.colors.text.primary1,
                maxLines = 1,
            )
        }
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(bottom = TangemTheme.dimens.spacing8),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
        ) {
            RoundedActionButton(
                modifier = Modifier
                    .weight(1f)
                    .testTag(OrganizeTokensScreenTestTags.SORT_BY_BALANCE_BUTTON),
                config = ActionButtonConfig(
                    text = TextReference.Res(id = R.string.organize_tokens_sort_by_balance),
                    iconResId = R.drawable.ic_sort_24,
                    isEnabled = config.isEnabled,
                    onClick = config.onSortClick,
                    shouldDimContent = config.isSortedByBalance,
                ),
                color = TangemTheme.colors.background.primary,
            )
            RoundedActionButton(
                modifier = Modifier
                    .weight(1f)
                    .testTag(OrganizeTokensScreenTestTags.GROUP_BUTTON),
                config = ActionButtonConfig(
                    text = TextReference.Res(
                        id = if (config.isGrouped) {
                            R.string.organize_tokens_ungroup
                        } else {
                            R.string.organize_tokens_group
                        },
                    ),
                    iconResId = R.drawable.ic_group_24,
                    isEnabled = config.isEnabled,
                    onClick = config.onGroupClick,
                ),
                color = TangemTheme.colors.background.primary,
            )
        }
    }
}

@Composable
private fun Actions(config: OrganizeTokensState.ActionsConfig, modifier: Modifier = Modifier) {
    Row(
        modifier = modifier
            .padding(horizontal = TangemTheme.dimens.spacing16)
            .fillMaxWidth(),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
    ) {
        SecondaryButton(
            modifier = Modifier
                .weight(1f)
                .testTag(OrganizeTokensScreenTestTags.CANCEL_BUTTON),
            text = stringResourceSafe(id = R.string.common_cancel),
            onClick = config.onCancelClick,
        )
        PrimaryButton(
            modifier = Modifier
                .weight(1f)
                .testTag(OrganizeTokensScreenTestTags.APPLY_BUTTON),
            text = stringResourceSafe(id = R.string.common_apply),
            onClick = config.onApplyClick,
            showProgress = config.showApplyProgress,
            enabled = config.canApply,
        )
    }
}

private fun Modifier.applyShapeAndShadow(roundingMode: RoundingModeUM, showShadow: Boolean): Modifier {
    return composed {
        val radius by animateDpAsState(
            targetValue = when (roundingMode) {
                is RoundingModeUM.None -> TangemTheme.dimens.radius0
                is RoundingModeUM.All -> TangemTheme.dimens.radius12
                is RoundingModeUM.Bottom,
                is RoundingModeUM.Top,
                -> TangemTheme.dimens.radius16
            },
            label = "item_shape_radius",
        )
        val elevation by animateDpAsState(
            targetValue = if (showShadow) {
                TangemTheme.dimens.elevation8
            } else {
                TangemTheme.dimens.elevation0
            },
            label = "item_elevation",
        )

        this
            .padding(paddingValues = getItemGap(roundingMode))
            .shadow(
                elevation = elevation,
                shape = getItemShape(roundingMode, radius),
                clip = true,
            )
    }
}

@Composable
@ReadOnlyComposable
private fun getItemGap(roundingMode: RoundingModeUM): PaddingValues {
    val paddingValue = TangemTheme.dimens.spacing4

    return if (roundingMode.isShowGap) {
        when (roundingMode) {
            is RoundingModeUM.None -> PaddingValues(all = 0.dp)
            is RoundingModeUM.All -> PaddingValues(vertical = paddingValue)
            is RoundingModeUM.Top -> PaddingValues(top = paddingValue)
            is RoundingModeUM.Bottom -> PaddingValues(bottom = paddingValue)
        }
    } else {
        PaddingValues(all = 0.dp)
    }
}

@Stable
private fun getItemShape(roundingMode: RoundingModeUM, radius: Dp): Shape {
    return when (roundingMode) {
        is RoundingModeUM.None -> RectangleShape
        is RoundingModeUM.Top -> RoundedCornerShape(
            topStart = radius,
            topEnd = radius,
        )
        is RoundingModeUM.Bottom -> RoundedCornerShape(
            bottomStart = radius,
            bottomEnd = radius,
        )
        is RoundingModeUM.All -> RoundedCornerShape(
            size = radius,
        )
    }
}

// region Preview

@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun OrganizeTokensScreenPreview(
    @PreviewParameter(OrganizeTokensStateProvider::class) state: OrganizeTokensState,
) {
    TangemThemePreview {
        OrganizeTokensScreen(state)
    }
}

private class OrganizeTokensStateProvider : CollectionPreviewParameterProvider<OrganizeTokensState>(
    collection = listOf(
        OrganizeTokensPreviewLegacy.stateAccounts,
        OrganizeTokensPreviewLegacy.state,
    ),
)
// endregion Preview