package com.tangem.features.staking.impl.presentation.ui

import android.content.res.Configuration
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.Icon
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.Density
import com.tangem.common.ui.navigationButtons.NavigationButtonsState
import com.tangem.common.ui.navigationButtons.NavigationPrimaryButton
import com.tangem.core.ui.components.SpacerH12
import com.tangem.core.ui.components.SpacerW12
import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshContainer
import com.tangem.core.ui.components.inputrow.InputRowDefault
import com.tangem.core.ui.components.list.roundedListWithDividersItems
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.percent
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.StakingDetailsScreenTestTags
import com.tangem.domain.models.staking.BalanceType
import com.tangem.domain.models.staking.RewardBlockType
import com.tangem.domain.staking.model.common.RewardType
import com.tangem.features.staking.impl.R
import com.tangem.features.staking.impl.presentation.model.StakingClickIntents
import com.tangem.features.staking.impl.presentation.state.BalanceState
import com.tangem.features.staking.impl.presentation.state.InnerYieldBalanceState
import com.tangem.features.staking.impl.presentation.state.StakingStates
import com.tangem.features.staking.impl.presentation.state.previewdata.InitialStakingStatePreview
import com.tangem.features.staking.impl.presentation.state.stub.StakingClickIntentsStub
import com.tangem.features.staking.impl.presentation.state.utils.getRewardTypeShortText
import com.tangem.features.staking.impl.presentation.ui.utils.toImageReference
import com.tangem.utils.StringsSigns.DOT
import com.tangem.utils.extensions.orZero

private const val BANNER_BLOCK_KEY = "BannerBlock"
private const val STAKING_REWARD_BLOCK_KEY = "StakingRewardBlock"
private const val ACTIVE_STAKING_BLOCK_KEY = "ActiveStakingBlock"
private const val STAKE_PRIMARY_BUTTON_KEY = "StakePrimaryButton"

@Composable
internal fun StakingInitialInfoContent(
    state: StakingStates.InitialInfoState,
    buttonState: NavigationButtonsState,
    clickIntents: StakingClickIntents,
    isBalanceHidden: Boolean,
) {
    if (state !is StakingStates.InitialInfoState.Data) return

    TangemPullToRefreshContainer(
        config = state.pullToRefreshConfig,
    ) {
        LazyColumn(
            verticalArrangement = alignLastToBottom(),
            modifier = Modifier
                .fillMaxSize()
                .background(TangemTheme.colors.background.secondary)
                .padding(horizontal = TangemTheme.dimens.spacing16),
        ) {
            if (state.isBannerVisible) {
                item(key = BANNER_BLOCK_KEY) {
                    Column(
                        modifier = Modifier.animateItem(),
                    ) {
                        BannerBlock(onClick = clickIntents::onInitialInfoBannerClick)
                        SpacerH12()
                    }
                }
            }

            this.roundedListWithDividersItems(
                rows = state.infoItems,
                footerContent = { SpacerH12() },
                hideEndText = isBalanceHidden,
            )

            activeStakingBlock(
                state = state,
                clickIntents = clickIntents,
                isBalanceHidden = isBalanceHidden,
            )

            item(STAKE_PRIMARY_BUTTON_KEY) {
                SpacerH12()
                StakeButtonBlock(
                    legalUrls = state.legalUrls,
                    buttonState = buttonState,
                )
            }
        }
    }
}

private fun LazyListScope.activeStakingBlock(
    state: StakingStates.InitialInfoState.Data,
    clickIntents: StakingClickIntents,
    isBalanceHidden: Boolean,
) {
    val innerYieldBalanceState = state.yieldBalance as? InnerYieldBalanceState.Data ?: return

    item(key = STAKING_REWARD_BLOCK_KEY) {
        Column(modifier = Modifier.animateItem()) {
            StakingRewardBlock(
                yieldBalanceState = state.yieldBalance,
                onRewardsClick = clickIntents::openRewardsValidators,
                isBalanceHidden = isBalanceHidden,
            )
            SpacerH12()
        }
    }

    if (innerYieldBalanceState.balances.isNotEmpty()) {
        item(ACTIVE_STAKING_BLOCK_KEY) {
            Text(
                text = stringResourceSafe(id = R.string.staking_your_stakes),
                style = TangemTheme.typography.subtitle2,
                color = TangemTheme.colors.text.tertiary,
                modifier = Modifier
                    .roundedShapeItemDecoration(
                        currentIndex = 0,
                        lastIndex = 1 + state.yieldBalance.balances.lastIndex,
                        addDefaultPadding = false,
                    )
                    .fillMaxWidth()
                    .background(TangemTheme.colors.background.action)
                    .padding(
                        top = TangemTheme.dimens.spacing12,
                        start = TangemTheme.dimens.spacing12,
                        end = TangemTheme.dimens.spacing12,
                        bottom = TangemTheme.dimens.spacing4,
                    ),
            )
        }
        itemsIndexed(
            items = state.yieldBalance.balances,
            key = { index, balance ->
                // Staked balance does not have unique identifier.
                buildString {
                    append(balance.hashCode())
                    append("_")
                    append(index)
                }
            },
        ) { index, balance ->
            ActiveStakingBlock(
                balance = balance,
                isBalanceHidden = isBalanceHidden,
                onClick = { clickIntents.onActiveStake(balance) },
                onAnalytic = clickIntents::onActiveStakeAnalytic,
                modifier = Modifier
                    .animateItem()
                    .roundedShapeItemDecoration(
                        currentIndex = index + 1,
                        lastIndex = state.yieldBalance.balances.lastIndex + 1,
                        addDefaultPadding = false,
                    )
                    .testTag(StakingDetailsScreenTestTags.ACTIVE_STAKING_BLOCK),
            )
        }
    }
}

@Composable
private fun BannerBlock(onClick: () -> Unit) {
    Box(
        modifier = Modifier
            .clip(RoundedCornerShape(size = TangemTheme.dimens.radius14))
            .clickable(
                interactionSource = remember { MutableInteractionSource() },
                indication = ripple(),
                onClick = onClick,
            ),
    ) {
        Image(
            modifier = Modifier
                .fillMaxSize()
                .testTag(StakingDetailsScreenTestTags.BANNER_IMAGE),
            contentScale = ContentScale.FillWidth,
            painter = painterResource(R.drawable.img_staking_banner),
            contentDescription = null,
        )
        Text(
            modifier = Modifier
                .align(Alignment.CenterStart)
                .padding(TangemTheme.dimens.spacing16)
                .testTag(StakingDetailsScreenTestTags.BANNER_TEXT),
            text = buildAnnotatedString {
                withStyle(SpanStyle(Brush.linearGradient(textGradientColors))) {
                    append(stringResourceSafe(R.string.staking_details_banner_text))
                }
            },
            style = TangemTheme.typography.h2,
        )
    }
}

@Composable
private fun StakingRewardBlock(
    yieldBalanceState: InnerYieldBalanceState.Data,
    onRewardsClick: () -> Unit,
    isBalanceHidden: Boolean,
) {
    val reward = yieldBalanceState.reward
    val (text, textColor) = when (yieldBalanceState.reward.rewardBlockType) {
        RewardBlockType.RewardsRequirementsError,
        RewardBlockType.Rewards,
        -> {
            annotatedReference {
                append(reward.rewardsFiat.orMaskWithStars(isBalanceHidden))
                appendSpace()
                append(DOT)
                appendSpace()
                append(reward.rewardsCrypto.orMaskWithStars(isBalanceHidden))
            } to TangemTheme.colors.text.primary1
        }
        RewardBlockType.EthereumEarnedRewards -> {
            resourceReference(
                id = R.string.staking_details_autocompound_rewards_earned,
                formatArgs = wrappedList(reward.rewardsCrypto.orMaskWithStars(isBalanceHidden)),
            ) to TangemTheme.colors.text.primary1
        }
        RewardBlockType.RewardUnavailable.DefaultRewardUnavailable -> {
            resourceReference(R.string.staking_details_auto_claiming_rewards_daily_text) to
                TangemTheme.colors.text.tertiary
        }
        RewardBlockType.RewardUnavailable.SolanaRewardUnavailable -> {
            resourceReference(R.string.staking_solana_details_auto_claiming_rewards_daily_text) to
                TangemTheme.colors.text.tertiary
        }
        RewardBlockType.CardanoNoRewards -> {
            resourceReference(R.string.staking_cardano_details_rewards_info_text) to
                TangemTheme.colors.text.tertiary
        }
        RewardBlockType.NoRewards -> {
            resourceReference(R.string.staking_details_no_rewards_to_claim) to TangemTheme.colors.text.tertiary
        }
    }
    val isShowIcon = reward.rewardBlockType == RewardBlockType.Rewards && yieldBalanceState.isActionable
    InputRowDefault(
        title = resourceReference(R.string.staking_rewards),
        text = text,
        iconRes = R.drawable.ic_chevron_right_24.takeIf { isShowIcon },
        onIconClick = onRewardsClick,
        textColor = textColor,
        modifier = Modifier
            .clip(TangemTheme.shapes.roundedCornersXMedium)
            .background(TangemTheme.colors.background.action)
            .clickable(
                interactionSource = remember { MutableInteractionSource() },
                indication = ripple(),
                enabled = yieldBalanceState.isActionable,
                onClick = onRewardsClick,
            ),
    )
}

@Composable
private fun ActiveStakingBlock(
    balance: BalanceState,
    isBalanceHidden: Boolean,
    onClick: () -> Unit,
    onAnalytic: () -> Unit,
    modifier: Modifier = Modifier,
) {
    val (icon, iconTint) = balance.type.getIcon()

    Row(
        verticalAlignment = Alignment.CenterVertically,
        modifier = modifier
            .background(TangemTheme.colors.background.action)
            .clickable(
                interactionSource = remember { MutableInteractionSource() },
                indication = ripple(),
                enabled = balance.isClickable,
                onClick = {
                    onAnalytic()
                    onClick()
                },
            )
            .padding(TangemTheme.dimens.spacing12),
    ) {
        StakingBalanceIcon(
            balance = balance,
            icon = icon,
            iconTint = iconTint,
        )

        Column(modifier = Modifier.weight(1f)) {
            Row {
                Text(
                    text = balance.title.resolveReference(),
                    style = TangemTheme.typography.subtitle2,
                    color = TangemTheme.colors.text.primary1,
                )
                if (balance.isPending) {
                    Spacer(modifier = Modifier.width(TangemTheme.dimens.spacing4))
                    Icon(
                        painter = rememberVectorPainter(
                            image = ImageVector.vectorResource(id = R.drawable.ic_staking_pending_transaction),
                        ),
                        tint = TangemTheme.colors.icon.informative,
                        contentDescription = null,
                        modifier = Modifier.size(TangemTheme.dimens.size18),
                    )
                }
            }
            val caption = balance.subtitle ?: balance.getAprTextNeutral()
            Text(
                text = caption.resolveAnnotatedReference(),
                style = TangemTheme.typography.caption2,
                color = TangemTheme.colors.text.tertiary,
                modifier = Modifier.padding(top = TangemTheme.dimens.spacing2),
            )
        }

        SpacerW12()

        Column(horizontalAlignment = Alignment.End) {
            Text(
                text = balance.formattedFiatAmount.orMaskWithStars(isBalanceHidden).resolveReference(),
                style = TangemTheme.typography.body1,
                color = TangemTheme.colors.text.primary1,
            )
            Text(
                text = balance.formattedCryptoAmount.orMaskWithStars(isBalanceHidden).resolveReference(),
                style = TangemTheme.typography.caption2,
                color = TangemTheme.colors.text.tertiary,
                modifier = Modifier.padding(top = TangemTheme.dimens.spacing2),
            )
        }
    }
}

@Composable
private fun StakingBalanceIcon(balance: BalanceState, icon: Int?, iconTint: Color) {
    if (balance.hasImage() || icon != null) {
        StakingTargetIcon(
            image = if (balance.hasImage()) balance.target?.image.toImageReference() else null,
            onImageError = if (icon != null) {
                {
                    Box(
                        contentAlignment = Alignment.Center,
                        modifier = Modifier
                            .size(TangemTheme.dimens.spacing36)
                            .clip(TangemTheme.shapes.roundedCornersXLarge)
                            .background(iconTint.copy(alpha = 0.08f)),
                    ) {
                        Icon(
                            painter = rememberVectorPainter(
                                image = ImageVector.vectorResource(id = icon),
                            ),
                            tint = iconTint,
                            contentDescription = null,
                            modifier = Modifier.size(TangemTheme.dimens.size18),
                        )
                    }
                }
            } else {
                { ValidatorImagePlaceholder() }
            },
            modifier = Modifier
                .size(TangemTheme.dimens.spacing36)
                .clip(TangemTheme.shapes.roundedCornersXLarge),
        )
        SpacerW12()
    }
}

@Composable
private fun StakeButtonBlock(
    legalUrls: StakingStates.InitialInfoState.LegalUrls,
    buttonState: NavigationButtonsState,
) {
    val state = buttonState as? NavigationButtonsState.Data
    val primaryButton = state?.primaryButton

    Column(
        modifier = Modifier.fillMaxWidth(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
    ) {
        state?.let { buttonsState ->
            StakingTosText(
                onTermsOfServiceClick = { buttonsState.onTextClick(legalUrls.termsOfServiceUrl) },
                onPrivacyPolicyClick = { buttonsState.onTextClick(legalUrls.privacyPolicyUrl) },
            )
        }
        NavigationPrimaryButton(primaryButton = primaryButton)
    }
}

@Suppress("UnusedPrivateMember")
@Composable
private fun BalanceState.getAprTextColored() = combinedReference(
    getRewardTypeShortText(target?.rewardInfo?.type ?: RewardType.UNKNOWN),
    annotatedReference {
        appendSpace()
        appendColored(
            text = target?.rewardInfo?.rate?.orZero().format { percent() },
            color = TangemTheme.colors.text.accent,
        )
    },
)

@Composable
private fun BalanceState.getAprTextNeutral() = combinedReference(
    getRewardTypeShortText(target?.rewardInfo?.type ?: RewardType.UNKNOWN),
    stringReference(" " + target?.rewardInfo?.rate?.orZero().format { percent() }),
)

@Composable
private fun BalanceType.getIcon() = when (this) {
    BalanceType.UNSTAKING -> R.drawable.ic_connection_18 to TangemTheme.colors.icon.accent
    BalanceType.UNSTAKED -> R.drawable.ic_connection_18 to TangemTheme.colors.icon.informative
    BalanceType.LOCKED -> R.drawable.ic_lock_24 to TangemTheme.colors.icon.informative
    else -> null to TangemTheme.colors.icon.informative
}

private fun BalanceState.hasImage() = when (type) {
    BalanceType.UNSTAKING,
    BalanceType.UNSTAKED,
    BalanceType.LOCKED,
    -> false
    else -> target?.image != null
}

private val textGradientColors = listOf(
    TangemColorPalette.White,
    Color(0xff8fb4df),
)

@Composable
private fun alignLastToBottom() = remember {
    object : Arrangement.Vertical {
        override fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) {
            var currentOffset = 0

            sizes.forEachIndexed { index, size ->
                if (index == sizes.lastIndex) {
                    outPositions[index] = totalSize - size
                } else {
                    outPositions[index] = currentOffset
                    currentOffset += size
                }
            }
        }
    }
}

// region preview

@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun StakingInitialInfoContent_Preview(
    @PreviewParameter(StakingInitialInfoContentPreviewProvider::class) feeState: StakingStates.InitialInfoState.Data,
) {
    TangemThemePreview {
        StakingInitialInfoContent(
            state = feeState,
            buttonState = NavigationButtonsState.Empty,
            clickIntents = StakingClickIntentsStub,
            isBalanceHidden = false,
        )
    }
}

private class StakingInitialInfoContentPreviewProvider : PreviewParameterProvider<StakingStates.InitialInfoState.Data> {
    override val values: Sequence<StakingStates.InitialInfoState.Data>
        get() = sequenceOf(
            InitialStakingStatePreview.defaultState,
            InitialStakingStatePreview.stateWithYield,
        )
}
// endregion