package dev.ulfrx.recipe.ui.components.calendar

import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
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.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.composeunstyled.UnstyledButton
import dev.ulfrx.recipe.ui.theme.RecipeTheme
import kotlinx.datetime.LocalDate

@Composable
internal fun CalendarDayCell(
    date: LocalDate,
    state: DayState,
    isSelected: Boolean,
    isToday: Boolean,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    numberStyle: TextStyle = RecipeTheme.typography.label.copy(fontWeight = FontWeight.Light),
    cellHeight: Dp = 36.dp,
    header: String? = null,
    headerStyle: TextStyle =
        RecipeTheme.typography.label.copy(
            fontWeight = FontWeight.Light,
            fontSize = 9.sp,
            lineHeight = 10.sp,
        ),
) {
    val colors = RecipeTheme.colors
    val baseColor = colors.content
    val mutedColor = colors.contentMuted
    val accent = colors.accent

    val background = if (isSelected) accent.copy(alpha = 0.18f) else Color.Transparent
    val textColor =
        when {
            state.disabled -> mutedColor.copy(alpha = 0.45f)
            state.dimmed && !isSelected -> mutedColor.copy(alpha = 0.55f)
            isSelected -> accent
            else -> baseColor
        }
    val headerColor =
        if (isSelected || state.dimmed || state.disabled) textColor else mutedColor
    val ringColor =
        when {
            isSelected -> accent.copy(alpha = 0.55f)
            isToday -> baseColor.copy(alpha = 0.35f)
            else -> Color.Transparent
        }
    val indicatorColor = if (isSelected) accent else mutedColor.copy(alpha = INDICATOR_MUTED_ALPHA)

    val cellModifier = modifier.height(cellHeight).fillMaxWidth()
    val isClickable = LocalCalendarInteractive.current && !state.disabled

    val content: @Composable () -> Unit = {
        DayCellInner(
            date = date,
            textColor = textColor,
            numberStyle = numberStyle,
            header = header,
            headerStyle = headerStyle,
            headerColor = headerColor,
            indicator = state.indicator,
            indicatorColor = indicatorColor,
        )
    }

    if (isClickable) {
        UnstyledButton(
            onClick = onClick,
            backgroundColor = background,
            contentColor = textColor,
            shape = CircleShape,
            borderColor = ringColor,
            borderWidth = if (ringColor == Color.Transparent) 0.dp else 1.dp,
            modifier = cellModifier,
            content = { content() },
        )
    } else {
        Box(
            modifier = cellModifier.dayCellSurface(background, ringColor),
            contentAlignment = Alignment.Center,
            content = { content() },
        )
    }
}

@Composable
private fun DayCellInner(
    date: LocalDate,
    textColor: Color,
    numberStyle: TextStyle,
    header: String?,
    headerStyle: TextStyle,
    headerColor: Color,
    indicator: Boolean,
    indicatorColor: Color,
) {
    if (header == null) {
        CenteredDayNumber(
            date = date,
            textColor = textColor,
            numberStyle = numberStyle,
            indicator = indicator,
            indicatorColor = indicatorColor,
        )
    } else {
        HeaderDayNumber(
            date = date,
            textColor = textColor,
            numberStyle = numberStyle,
            header = header,
            headerStyle = headerStyle,
            headerColor = headerColor,
            indicator = indicator,
            indicatorColor = indicatorColor,
        )
    }
}

@Composable
private fun CenteredDayNumber(
    date: LocalDate,
    textColor: Color,
    numberStyle: TextStyle,
    indicator: Boolean,
    indicatorColor: Color,
) {
    Box(modifier = Modifier.fillMaxSize()) {
        BasicText(
            text = date.dayOfMonth.toString(),
            style = numberStyle.copy(color = textColor),
            modifier = Modifier.align(Alignment.Center),
        )
        if (indicator) {
            IndicatorDot(
                color = indicatorColor,
                modifier = Modifier.align(Alignment.Center).offset(y = 11.dp),
            )
        }
    }
}

@Composable
private fun HeaderDayNumber(
    date: LocalDate,
    textColor: Color,
    numberStyle: TextStyle,
    header: String,
    headerStyle: TextStyle,
    headerColor: Color,
    indicator: Boolean,
    indicatorColor: Color,
) {
    Box(modifier = Modifier.fillMaxSize()) {
        Column(
            modifier =
                Modifier
                    .align(Alignment.TopCenter)
                    .fillMaxWidth()
                    .padding(top = 4.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
        ) {
            BasicText(text = header, style = headerStyle.copy(color = headerColor))
            Spacer(modifier = Modifier.height(1.dp))
            BasicText(
                text = date.dayOfMonth.toString(),
                style = numberStyle.copy(color = textColor),
            )
        }
        if (indicator) {
            IndicatorDot(
                color = indicatorColor,
                modifier = Modifier.align(Alignment.BottomCenter).padding(bottom = 2.dp),
            )
        }
    }
}

@Composable
private fun IndicatorDot(
    color: Color,
    modifier: Modifier = Modifier,
) {
    Box(
        modifier =
            modifier
                .size(4.dp)
                .clip(CircleShape)
                .background(color),
    )
}

private fun Modifier.dayCellSurface(
    backgroundColor: Color,
    ringColor: Color,
): Modifier =
    this
        .background(backgroundColor, CircleShape)
        .then(
            if (ringColor == Color.Transparent) {
                Modifier
            } else {
                Modifier.border(1.dp, ringColor, CircleShape)
            },
        )

private const val INDICATOR_MUTED_ALPHA = 0.6f
