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.background
import androidx.compose.foundation.border
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.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.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.Color
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.ArrowLeft
import com.composables.icons.lucide.Lucide
import com.composables.icons.lucide.Plus
import dev.ulfrx.recipe.ui.components.glass.CircleGlassButton
import dev.ulfrx.recipe.ui.components.glass.GlassBackdropSource
import dev.ulfrx.recipe.ui.components.glass.LocalGlassBackdropState
import dev.ulfrx.recipe.ui.components.glass.rememberGlassBackdropState
import dev.ulfrx.recipe.ui.components.recipe.IngredientCard
import dev.ulfrx.recipe.ui.components.recipe.IngredientDivider
import dev.ulfrx.recipe.ui.components.recipe.IngredientRow
import dev.ulfrx.recipe.ui.components.recipe.MealSlot
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.section.Section
import dev.ulfrx.recipe.ui.components.section.SectionTitle
import dev.ulfrx.recipe.ui.components.recipe.RecipeServingsStepper
import dev.ulfrx.recipe.ui.components.recipe.scaledBy
import dev.ulfrx.recipe.ui.screens.mealplaneditor.AddableIngredientUi
import dev.ulfrx.recipe.ui.screens.mealplaneditor.MealPlanEditorContent
import dev.ulfrx.recipe.ui.screens.mealplaneditor.MealPlanEditorState
import dev.ulfrx.recipe.ui.screens.mealplaneditor.MealPlanEditorViewModel
import dev.ulfrx.recipe.ui.screens.mealplaneditor.PlannedMealUi
import dev.ulfrx.recipe.ui.screens.mealplaneditor.sampleAddableIngredients
import kotlinx.datetime.LocalDate
import dev.ulfrx.recipe.ui.theme.RecipeTheme
import org.jetbrains.compose.resources.stringResource
import recipe.composeapp.generated.resources.Res
import recipe.composeapp.generated.resources.meal_plan_editor_back_a11y
import recipe.composeapp.generated.resources.meal_plan_editor_confirm_a11y
import recipe.composeapp.generated.resources.nutrition_label
import recipe.composeapp.generated.resources.recipe_detail_handle_a11y
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

@Composable
fun RecipeDetailSheet(
    detailViewModel: RecipeDetailViewModel,
    editorViewModel: MealPlanEditorViewModel,
    onPlanConfirmed: (PlannedMealUi) -> Unit,
) {
    val detailState by detailViewModel.state.collectAsStateWithLifecycle()
    val editorState by editorViewModel.state.collectAsStateWithLifecycle()

    val ready = detailState as? RecipeDetailState.Ready
    val editing = editorState as? MealPlanEditorState.Editing
    val anyOpen = ready != null || editing != null

    val sheetState =
        rememberModalBottomSheetState(
            initialDetent = SheetDetent.Hidden,
            detents = listOf(SheetDetent.Hidden, SheetDetent.FullyExpanded),
        )

    LaunchedEffect(anyOpen) {
        sheetState.targetDetent = if (anyOpen) SheetDetent.FullyExpanded else SheetDetent.Hidden
    }

    // Only caller of the dismiss path: a drag that settles the sheet at Hidden
    // while either VM still holds state. 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 && anyOpen) {
            editorViewModel.close()
            detailViewModel.dismiss()
        }
    }

    ModalBottomSheet(state = sheetState) {
        Scrim(
            scrimColor = SCRIM_COLOR,
            enter = fadeIn(tween(SCRIM_FADE_MILLIS)),
            exit = fadeOut(tween(SCRIM_FADE_MILLIS)),
        )
        Sheet(
            modifier = Modifier.fillMaxWidth(),
            backgroundColor = RecipeTheme.colors.background,
            shape = RoundedCornerShape(topStart = SHEET_CORNER_RADIUS, topEnd = SHEET_CORNER_RADIUS),
        ) {
            SheetBody(
                editing = editing,
                ready = ready,
                onOpenEditor = {
                    val current = ready ?: return@SheetBody
                    editorViewModel.open(
                        recipe = current.recipe,
                        initialSubstitutions = current.substitutions,
                        initialServings = current.servings,
                    )
                },
                onCloseEditor = editorViewModel::close,
                onConfirmEditor = {
                    val planned = editorViewModel.confirm() ?: return@SheetBody
                    onPlanConfirmed(planned)
                    sheetState.targetDetent = SheetDetent.Hidden
                },
                detailActions =
                    RecipeDetailActions(
                        onServingsChange = detailViewModel::setServings,
                        onSelectSubstitution = detailViewModel::selectSubstitution,
                    ),
                editorActions =
                    EditorActions(
                        onSelectDate = editorViewModel::selectDate,
                        onSetCalendarExpanded = editorViewModel::setCalendarExpanded,
                        onSelectSlot = editorViewModel::selectSlot,
                        onSetServings = editorViewModel::setServings,
                        onSelectSubstitution = editorViewModel::selectSubstitution,
                        onRemoveRecipeIngredient = editorViewModel::removeRecipeIngredient,
                        onRemoveAddedIngredient = editorViewModel::removeAddedIngredient,
                        onRestoreRemoved = editorViewModel::restoreRemovedIngredients,
                        onAddIngredient = editorViewModel::addIngredient,
                    ),
            )
        }
    }
}

@Composable
private fun BottomSheetScope.SheetBody(
    editing: MealPlanEditorState.Editing?,
    ready: RecipeDetailState.Ready?,
    onOpenEditor: () -> Unit,
    onCloseEditor: () -> Unit,
    onConfirmEditor: () -> Unit,
    detailActions: RecipeDetailActions,
    editorActions: EditorActions,
) {
    val backdrop = rememberGlassBackdropState()
    val handleLabel = stringResource(Res.string.recipe_detail_handle_a11y)
    val spacing = RecipeTheme.spacing

    CompositionLocalProvider(LocalGlassBackdropState provides backdrop) {
        Box(modifier = Modifier.fillMaxWidth().fillMaxHeight(SHEET_HEIGHT_FRACTION)) {
            GlassBackdropSource(state = backdrop, modifier = Modifier.fillMaxSize()) {
                when {
                    editing != null ->
                        MealPlanEditorContent(
                            editing = editing,
                            catalog = sampleAddableIngredients,
                            topChromeInset = TopActionsTopInset,
                            topChromeHeight = TopPillHeight,
                            onSelectDate = editorActions.onSelectDate,
                            onSetCalendarExpanded = editorActions.onSetCalendarExpanded,
                            onSelectSlot = editorActions.onSelectSlot,
                            onSetServings = editorActions.onSetServings,
                            onSelectSubstitution = editorActions.onSelectSubstitution,
                            onRemoveRecipeIngredient = editorActions.onRemoveRecipeIngredient,
                            onRemoveAddedIngredient = editorActions.onRemoveAddedIngredient,
                            onRestoreRemoved = editorActions.onRestoreRemoved,
                            onAddIngredient = editorActions.onAddIngredient,
                        )

                    ready != null ->
                        RecipeDetailBody(
                            ready = ready,
                            onPlanClick = onOpenEditor,
                            onServingsChange = detailActions.onServingsChange,
                            onSelectSubstitution = detailActions.onSelectSubstitution,
                        )
                }
            }

            SheetHandle(
                contentDescription = handleLabel,
                modifier = Modifier.align(Alignment.TopCenter).padding(top = spacing.sm),
            )

            if (editing != null) {
                EditorTopActions(
                    onBack = onCloseEditor,
                    onConfirm = onConfirmEditor,
                    modifier =
                        Modifier
                            .align(Alignment.TopCenter)
                            .fillMaxWidth()
                            .padding(top = TopActionsTopInset, start = spacing.lg, end = spacing.lg),
                )
            }

        }
    }
}

@Composable
private fun EditorTopActions(
    onBack: () -> Unit,
    onConfirm: () -> Unit,
    modifier: Modifier = Modifier,
) {
    Row(
        modifier = modifier,
        horizontalArrangement = Arrangement.SpaceBetween,
        verticalAlignment = Alignment.CenterVertically,
    ) {
        CircleGlassButton(
            onClick = onBack,
            icon = Lucide.ArrowLeft,
            contentDescription = stringResource(Res.string.meal_plan_editor_back_a11y),
            size = TopPillHeight,
            iconSize = TopActionIconSize,
            glassStyle = RecipeTheme.glass.button,
        )
        CircleGlassButton(
            onClick = onConfirm,
            icon = Lucide.Plus,
            contentDescription = stringResource(Res.string.meal_plan_editor_confirm_a11y),
            size = TopPillHeight,
            iconSize = TopActionIconSize,
            glassStyle = RecipeTheme.glass.button,
        )
    }
}

@Composable
private fun BottomSheetScope.SheetHandle(
    contentDescription: String,
    modifier: Modifier = Modifier,
) {
    val colors = RecipeTheme.colors
    DragIndication(
        modifier =
            modifier
                .semantics { this.contentDescription = contentDescription }
                .clip(RoundedCornerShape(percent = 50))
                .background(colors.surface.copy(alpha = HandleAlpha))
                .width(HandleWidth)
                .height(HandleHeight),
    )
}

@Composable
private fun RecipeDetailBody(
    ready: RecipeDetailState.Ready,
    onPlanClick: () -> Unit,
    onServingsChange: (Int) -> Unit,
    onSelectSubstitution: (String, String) -> Unit,
) {
    val spacing = RecipeTheme.spacing
    val scrollState = rememberScrollState()
    val bottomInset = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()

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

    Column(modifier = Modifier.fillMaxSize().verticalScroll(scrollState)) {
        RecipeDetailHero(
            title = detail.title,
            cookingMinutes = detail.cookingMinutes,
            onPlanClick = onPlanClick,
        )

        Column(modifier = Modifier.fillMaxWidth().padding(horizontal = spacing.lg)) {
            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))
        }
    }
}

@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))
        RecipeServingsStepper(
            servings = servings,
            servingsRange = MIN_RECIPE_SERVINGS..MAX_RECIPE_SERVINGS,
            decrementContentDescription = stringResource(Res.string.recipe_detail_servings_decrement_a11y),
            incrementContentDescription = stringResource(Res.string.recipe_detail_servings_increment_a11y),
            onServingsChange = onServingsChange,
        )
    }
}

@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)) {
        IngredientCard {
            ingredients.forEachIndexed { index, slot ->
                if (index > 0) IngredientDivider()
                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 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.Bold,
                    fontSize = StepNumberTextSize,
                ),
            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),
        )
    }
}

private class RecipeDetailActions(
    val onServingsChange: (Int) -> Unit,
    val onSelectSubstitution: (String, String) -> Unit,
)

private class EditorActions(
    val onSelectDate: (LocalDate) -> Unit,
    val onSetCalendarExpanded: (Boolean) -> Unit,
    val onSelectSlot: (MealSlot) -> Unit,
    val onSetServings: (Int) -> Unit,
    val onSelectSubstitution: (String, String) -> Unit,
    val onRemoveRecipeIngredient: (String) -> Unit,
    val onRemoveAddedIngredient: (String) -> Unit,
    val onRestoreRemoved: () -> Unit,
    val onAddIngredient: (AddableIngredientUi) -> Unit,
)

private const val SHEET_HEIGHT_FRACTION = 0.92f
private const val SCRIM_FADE_MILLIS = 250
private const val HandleAlpha = 0.85f

private val SCRIM_COLOR = Color.Black.copy(alpha = 0.45f)
private val SHEET_CORNER_RADIUS = 28.dp
private val HandleWidth = 36.dp
private val HandleHeight = 5.dp
private val StepNumberWidth = 20.dp
private val TopPillHeight = 44.dp
private val TopActionIconSize = 18.dp
private val TopActionsTopInset = 28.dp


private val StepNumberTextSize = 11.sp
private val StepTextSize = 13.sp
private val StepLineHeight = 19.sp
