package dev.ulfrx.recipe.ui.screens.recipedetail

import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
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.layout.ContentScale
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.composables.core.BottomSheetScope
import com.composables.core.DragIndication
import com.composables.core.ModalBottomSheet
import com.composables.core.Scrim
import com.composables.core.Sheet
import com.composables.core.SheetDetent
import com.composables.core.rememberModalBottomSheetState
import com.composables.icons.lucide.Calendar
import com.composables.icons.lucide.Clock
import com.composables.icons.lucide.Lucide
import com.composables.icons.lucide.Minus
import com.composables.icons.lucide.Plus
import com.composeunstyled.UnstyledButton
import com.composeunstyled.UnstyledIcon
import dev.ulfrx.recipe.ui.components.glass.GlassBackdropSource
import dev.ulfrx.recipe.ui.components.glass.GlassSurface
import dev.ulfrx.recipe.ui.components.glass.LocalGlassBackdropState
import dev.ulfrx.recipe.ui.components.glass.rememberGlassBackdropState
import dev.ulfrx.recipe.ui.components.recipe.IngredientRow
import dev.ulfrx.recipe.ui.components.recipe.NutritionSummary
import dev.ulfrx.recipe.ui.components.recipe.RecipeIngredientSlotUi
import dev.ulfrx.recipe.ui.components.recipe.RecipeNutritionUi
import dev.ulfrx.recipe.ui.components.recipe.scaledBy
import dev.ulfrx.recipe.ui.theme.RecipeTheme
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
import recipe.composeapp.generated.resources.Res
import recipe.composeapp.generated.resources.nutrition_label
import recipe.composeapp.generated.resources.recipe_card_minutes_format
import recipe.composeapp.generated.resources.recipe_detail_handle_a11y
import recipe.composeapp.generated.resources.recipe_detail_plan_button
import recipe.composeapp.generated.resources.recipe_detail_section_ingredients
import recipe.composeapp.generated.resources.recipe_detail_section_steps
import recipe.composeapp.generated.resources.recipe_detail_servings_decrement_a11y
import recipe.composeapp.generated.resources.recipe_detail_servings_increment_a11y
import recipe.composeapp.generated.resources.recipe_detail_servings_label
import recipe.composeapp.generated.resources.recipe_detail_step_number_format
import recipe.composeapp.generated.resources.sample_recipe

@Composable
fun RecipeDetailSheet(
    viewModel: RecipeDetailViewModel,
    onPlanRecipe: (recipeId: String) -> Unit,
) {
    val state by viewModel.state.collectAsStateWithLifecycle()
    val sheetState =
        rememberModalBottomSheetState(
            initialDetent = SheetDetent.Hidden,
            detents = listOf(SheetDetent.Hidden, SheetDetent.FullyExpanded),
        )
    val ready = state as? RecipeDetailState.Ready

    LaunchedEffect(ready != null) {
        sheetState.targetDetent = if (ready != null) SheetDetent.FullyExpanded else SheetDetent.Hidden
    }

    // Only caller of dismiss(): a drag that settles the sheet at Hidden while the VM still holds
    // the recipe. Programmatic closes must set targetDetent = Hidden and let this fire — calling
    // dismiss() directly would clear the recipe mid-animation and blank the closing sheet.
    LaunchedEffect(sheetState.isIdle, sheetState.currentDetent) {
        if (sheetState.isIdle && sheetState.currentDetent == SheetDetent.Hidden && ready != null) {
            viewModel.dismiss()
        }
    }

    ModalBottomSheet(state = sheetState) {
        Scrim(
            scrimColor = ScrimColor,
            enter = fadeIn(tween(ScrimFadeMillis)),
            exit = fadeOut(tween(ScrimFadeMillis)),
        )
        Sheet(
            modifier = Modifier.fillMaxWidth(),
            backgroundColor = RecipeTheme.colors.background,
            shape = RoundedCornerShape(topStart = SheetCornerRadius, topEnd = SheetCornerRadius),
        ) {
            ready?.let {
                RecipeDetailContent(
                    ready = it,
                    onServingsChange = viewModel::setServings,
                    onSelectSubstitution = viewModel::selectSubstitution,
                    onPlanRecipe = onPlanRecipe,
                )
            }
        }
    }
}

@Composable
private fun BottomSheetScope.RecipeDetailContent(
    ready: RecipeDetailState.Ready,
    onServingsChange: (Int) -> Unit,
    onSelectSubstitution: (String, String) -> Unit,
    onPlanRecipe: (String) -> Unit,
) {
    val colors = RecipeTheme.colors
    val typography = RecipeTheme.typography
    val spacing = RecipeTheme.spacing
    val scrollState = rememberScrollState()
    val bottomInset = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
    val handleLabel = stringResource(Res.string.recipe_detail_handle_a11y)

    val backdrop = rememberGlassBackdropState()

    val detail = ready.recipe
    val servings = ready.servings

    CompositionLocalProvider(LocalGlassBackdropState provides backdrop) {
        Box(modifier = Modifier.fillMaxWidth().fillMaxHeight(SHEET_HEIGHT_FRACTION)) {
            GlassBackdropSource(state = backdrop, modifier = Modifier.fillMaxSize()) {
                Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) {
                    RecipeHero()

                    Column(modifier = Modifier.fillMaxWidth().padding(horizontal = spacing.lg)) {
                        BasicText(
                            text = detail.title,
                            style =
                                typography.display.copy(
                                    color = colors.content,
                                    fontSize = TitleTextSize,
                                    lineHeight = TitleLineHeight,
                                    fontWeight = FontWeight.Bold,
                                ),
                        )
                        Spacer(Modifier.height(spacing.sm))
                        MetaRow(minutes = detail.cookingMinutes)

                        Spacer(Modifier.height(spacing.xl))
                        NutritionSection(nutrition = detail.nutrition.scaledBy(servings))

                        Spacer(Modifier.height(spacing.xl))
                        ServingsSection(servings = servings, onServingsChange = onServingsChange)

                        Spacer(Modifier.height(spacing.xl))
                        IngredientsSection(
                            ingredients = detail.ingredients,
                            servings = servings,
                            substitutions = ready.substitutions,
                            onSelectSubstitution = onSelectSubstitution,
                        )

                        Spacer(Modifier.height(spacing.xl))
                        StepsSection(steps = detail.steps)

                        Spacer(Modifier.height(bottomInset + spacing.xxl))
                    }
                }
            }

            DragIndication(
                modifier =
                    Modifier
                        .align(Alignment.TopCenter)
                        .padding(top = spacing.sm)
                        .semantics { contentDescription = handleLabel }
                        .clip(RoundedCornerShape(percent = 50))
                        .background(colors.surface.copy(alpha = 0.85f))
                        .width(HandleWidth)
                        .height(HandleHeight),
            )

            PlanButton(
                modifier =
                    Modifier
                        .align(Alignment.TopEnd)
                        .padding(top = spacing.lg, end = spacing.lg),
                onClick = { onPlanRecipe(detail.id) },
            )
        }
    }
}

@Composable
private fun RecipeHero() {
    val colors = RecipeTheme.colors
    Box(modifier = Modifier.fillMaxWidth().height(HeroHeight)) {
        Box(modifier = Modifier.fillMaxSize().background(colors.surfaceGlass))
        Image(
            painter = painterResource(Res.drawable.sample_recipe),
            contentDescription = null,
            contentScale = ContentScale.Crop,
            modifier = Modifier.fillMaxSize(),
        )
        Box(
            modifier =
                Modifier
                    .fillMaxSize()
                    .background(
                        Brush.verticalGradient(
                            colorStops =
                                arrayOf(
                                    0.5f to Color.Transparent,
                                    1f to colors.background,
                                ),
                        ),
                    ),
        )
    }
}

@Composable
private fun MetaRow(minutes: Int) {
    val colors = RecipeTheme.colors
    Row(
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(RecipeTheme.spacing.xs),
    ) {
        UnstyledIcon(
            imageVector = Lucide.Clock,
            contentDescription = null,
            tint = colors.contentMuted,
            modifier = Modifier.size(MetaIconSize),
        )
        BasicText(
            text = stringResource(Res.string.recipe_card_minutes_format, minutes),
            style = RecipeTheme.typography.label.copy(color = colors.contentMuted),
        )
    }
}

@Composable
private fun Section(
    title: String,
    content: @Composable () -> Unit,
) {
    SectionTitle(text = title)
    Spacer(Modifier.height(RecipeTheme.spacing.lg))
    content()
}

@Composable
private fun SectionTitle(text: String) {
    BasicText(
        text = text.uppercase(),
        style =
            RecipeTheme.typography.label.copy(
                color = RecipeTheme.colors.contentMuted,
                fontSize = SectionHeaderTextSize,
                letterSpacing = SectionHeaderTracking,
                fontWeight = FontWeight.Bold,
            ),
    )
}

@Composable
private fun NutritionSection(nutrition: RecipeNutritionUi) {
    Section(title = stringResource(Res.string.nutrition_label)) {
        NutritionSummary(nutrition = nutrition)
    }
}

@Composable
private fun ServingsSection(
    servings: Int,
    onServingsChange: (Int) -> Unit,
) {
    Row(
        modifier = Modifier.fillMaxWidth(),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.SpaceBetween,
    ) {
        SectionTitle(text = stringResource(Res.string.recipe_detail_servings_label))
        ServingsStepper(
            servings = servings,
            onDecrement = { onServingsChange(servings - 1) },
            onIncrement = { onServingsChange(servings + 1) },
        )
    }
}

@Composable
private fun IngredientsSection(
    ingredients: List<RecipeIngredientSlotUi>,
    servings: Int,
    substitutions: Map<String, String>,
    onSelectSubstitution: (slotId: String, optionId: String) -> Unit,
) {
    Section(title = stringResource(Res.string.recipe_detail_section_ingredients)) {
        Column(verticalArrangement = Arrangement.spacedBy(IngredientRowGap)) {
            ingredients.forEach { slot ->
                IngredientRow(
                    slot = slot.scaledBy(servings),
                    selectedOptionId = substitutions[slot.id] ?: slot.default.id,
                    onSelect =
                        if (slot.alternatives.isNotEmpty()) {
                            { choice -> onSelectSubstitution(slot.id, choice.id) }
                        } else {
                            null
                        },
                )
            }
        }
    }
}

@Composable
private fun StepsSection(steps: List<String>) {
    Section(title = stringResource(Res.string.recipe_detail_section_steps)) {
        Column(verticalArrangement = Arrangement.spacedBy(RecipeTheme.spacing.sm)) {
            steps.forEachIndexed { index, step ->
                StepRow(number = index + 1, text = step)
            }
        }
    }
}

@Composable
private fun ServingsStepper(
    servings: Int,
    onDecrement: () -> Unit,
    onIncrement: () -> Unit,
) {
    val colors = RecipeTheme.colors
    Row(
        modifier =
            Modifier
                .clip(RoundedCornerShape(percent = 50))
                .background(colors.surface)
                .padding(horizontal = RecipeTheme.spacing.xs),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(RecipeTheme.spacing.xs),
    ) {
        StepperButton(
            icon = Lucide.Minus,
            contentDescription = stringResource(Res.string.recipe_detail_servings_decrement_a11y),
            enabled = servings > MIN_RECIPE_SERVINGS,
            onClick = onDecrement,
        )
        BasicText(
            text = servings.toString(),
            style =
                RecipeTheme.typography.body.copy(
                    color = colors.content,
                    fontWeight = FontWeight.SemiBold,
                ),
            modifier = Modifier.width(ServingsValueWidth),
        )
        StepperButton(
            icon = Lucide.Plus,
            contentDescription = stringResource(Res.string.recipe_detail_servings_increment_a11y),
            enabled = servings < MAX_RECIPE_SERVINGS,
            onClick = onIncrement,
        )
    }
}

@Composable
private fun StepperButton(
    icon: ImageVector,
    contentDescription: String,
    enabled: Boolean,
    onClick: () -> Unit,
) {
    val colors = RecipeTheme.colors
    UnstyledButton(
        onClick = onClick,
        enabled = enabled,
        modifier = Modifier.size(StepperButtonSize),
    ) {
        Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
            UnstyledIcon(
                imageVector = icon,
                contentDescription = contentDescription,
                tint = if (enabled) colors.content else colors.contentMuted.copy(alpha = 0.45f),
                modifier = Modifier.size(StepperIconSize),
            )
        }
    }
}

@Composable
private fun StepRow(
    number: Int,
    text: String,
) {
    val colors = RecipeTheme.colors
    Row(horizontalArrangement = Arrangement.spacedBy(RecipeTheme.spacing.sm)) {
        BasicText(
            text = stringResource(Res.string.recipe_detail_step_number_format, number),
            style =
                RecipeTheme.typography.body.copy(
                    color = colors.contentMuted,
                    fontWeight = FontWeight.Medium,
                    fontSize = StepTextSize,
                ),
            modifier = Modifier.width(StepNumberWidth),
        )
        BasicText(
            text = text,
            style =
                RecipeTheme.typography.body.copy(
                    color = colors.content,
                    fontWeight = FontWeight.Normal,
                    fontSize = StepTextSize,
                    lineHeight = StepLineHeight,
                ),
            modifier = Modifier.weight(1f),
        )
    }
}

@Composable
private fun PlanButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
) {
    val colors = RecipeTheme.colors
    GlassSurface(
        modifier = modifier.height(PlanButtonHeight),
        cornerRadius = PlanButtonHeight / 2,
        tint = colors.surfaceGlass,
    ) {
        UnstyledButton(
            onClick = onClick,
            backgroundColor = Color.Transparent,
            contentColor = colors.content,
            contentPadding = PaddingValues(horizontal = RecipeTheme.spacing.lg),
            modifier = Modifier.fillMaxHeight(),
        ) {
            Row(
                verticalAlignment = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.spacedBy(RecipeTheme.spacing.xs),
            ) {
                UnstyledIcon(
                    imageVector = Lucide.Calendar,
                    contentDescription = null,
                    tint = colors.content,
                    modifier = Modifier.size(PlanButtonIconSize),
                )
                BasicText(
                    text = stringResource(Res.string.recipe_detail_plan_button),
                    style =
                        RecipeTheme.typography.label.copy(
                            color = colors.content,
                            fontWeight = FontWeight.SemiBold,
                        ),
                )
            }
        }
    }
}

private const val SHEET_HEIGHT_FRACTION = 0.92f
private const val ScrimFadeMillis = 250

private val ScrimColor = Color.Black.copy(alpha = 0.45f)
private val SheetCornerRadius = 28.dp
private val HeroHeight = 200.dp
private val HandleWidth = 36.dp
private val HandleHeight = 5.dp
private val IngredientRowGap = 6.dp
private val MetaIconSize = 14.dp
private val StepperButtonSize = 30.dp
private val StepperIconSize = 14.dp
private val ServingsValueWidth = 28.dp
private val StepNumberWidth = 20.dp
private val PlanButtonHeight = 36.dp
private val PlanButtonIconSize = 14.dp

private val TitleTextSize = 24.sp
private val TitleLineHeight = 28.sp
private val SectionHeaderTextSize = 11.sp
private val SectionHeaderTracking = 1.sp
private val StepTextSize = 14.sp
private val StepLineHeight = 20.sp
