package dev.privacyllc.period.designsystem

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Typography
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp

private val LightColors = lightColorScheme(
    primary = PeriodPalette.DeepPlum,
    onPrimary = Color.White,
    primaryContainer = PeriodPalette.LightLavender,
    onPrimaryContainer = PeriodPalette.DeepPlum,
    secondary = PeriodPalette.RichPurple,
    onSecondary = Color.White,
    secondaryContainer = PeriodPalette.LavenderMist,
    onSecondaryContainer = PeriodPalette.DeepPlum,
    tertiary = PeriodPalette.PeriodRose,
    onTertiary = Color.White,
    // "Blush-tinted ivory or lavender mist", per the guide. Never pure white.
    background = PeriodPalette.LightBackground,
    onBackground = PeriodPalette.OnLight,
    surface = PeriodPalette.LightSurface,
    onSurface = PeriodPalette.OnLight,
    surfaceVariant = PeriodPalette.LavenderMist,
    onSurfaceVariant = PeriodPalette.DarkMauve,
    outline = PeriodPalette.SoftLavender,
    error = PeriodPalette.Error,
    onError = Color.White,
)

private val DarkColors = darkColorScheme(
    primary = PeriodPalette.GlowPink,
    onPrimary = PeriodPalette.MidnightPlum,
    primaryContainer = PeriodPalette.DeepPurple,
    onPrimaryContainer = PeriodPalette.GlowPink,
    secondary = PeriodPalette.LavenderGlow,
    onSecondary = PeriodPalette.MidnightPlum,
    secondaryContainer = PeriodPalette.RaisedSurface,
    onSecondaryContainer = PeriodPalette.LavenderGlow,
    tertiary = PeriodPalette.RoseNeon,
    onTertiary = PeriodPalette.MidnightPlum,
    // "Quiet nighttime + plum velvet + soft rose glow" — never flat black.
    background = PeriodPalette.MidnightPlum,
    onBackground = PeriodPalette.OnDark,
    surface = PeriodPalette.DarkViolet,
    onSurface = PeriodPalette.OnDark,
    surfaceVariant = PeriodPalette.RaisedSurface,
    onSurfaceVariant = PeriodPalette.LavenderGlow,
    outline = PeriodPalette.MauveGlow,
    error = PeriodPalette.ErrorDark,
    onError = PeriodPalette.MidnightPlum,
)

/**
 * Semantic cycle colours.
 *
 * Deliberately not squeezed into Material's roles: "the colour a predicted
 * period is drawn in" is not `secondary`, and mapping it onto one means the next
 * person to adjust `secondary` silently changes the calendar. They travel as
 * their own token set instead.
 *
 * **Colour is never the only signal.** These pair with shape — solid fill,
 * dotted outline, ring, marker — per docs/design/README.md rule 3.
 */
data class CycleColors(
    val periodConfirmed: Color,
    val periodPredicted: Color,
    val fertileWindow: Color,
    val ovulation: Color,
)

val LocalCycleColors = staticCompositionLocalOf {
    CycleColors(
        periodConfirmed = PeriodPalette.DayPeriod,
        periodPredicted = PeriodPalette.DayPredicted,
        fertileWindow = PeriodPalette.DayFertility,
        ovulation = PeriodPalette.FertilityPink,
    )
}

private val PeriodTypography = Typography().let { base ->
    base.copy(
        // The forecast number is the visual hero — PRODUCT_PLAN.md §38.
        displayLarge = base.displayLarge.copy(fontWeight = FontWeight.Bold, fontSize = 72.sp),
    )
}

@Composable
fun PeriodTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit,
) {
    // §5 of the brand guide names a colour per cycle state. They are close
    // relatives on purpose — the identity is plum and rose throughout — which
    // is exactly why the calendar's markers differ in SHAPE. Colour is the
    // second signal here and never the only one.
    val cycleColors = if (darkTheme) {
        CycleColors(
            periodConfirmed = PeriodPalette.RoseNeon,
            periodPredicted = PeriodPalette.LavenderGlow,
            fertileWindow = PeriodPalette.GlowPink,
            ovulation = PeriodPalette.FertilityPink,
        )
    } else {
        CycleColors(
            periodConfirmed = PeriodPalette.DayPeriod,
            periodPredicted = PeriodPalette.DayPredicted,
            fertileWindow = PeriodPalette.DayFertility,
            ovulation = PeriodPalette.DayStrongPeriod,
        )
    }

    CompositionLocalProvider(LocalCycleColors provides cycleColors) {
        MaterialTheme(
            colorScheme = if (darkTheme) DarkColors else LightColors,
            typography = PeriodTypography,
        ) {
            // The Surface is not decoration, and leaving it out was a real bug.
            //
            // Material 3 sets `LocalContentColor` from a Surface. Without one,
            // any Text that does not name a colour inherits the default —
            // BLACK — and the app's own background never paints at all. In
            // light mode that looked correct by accident, because dark text on
            // cream is what was wanted anyway. In dark mode the onboarding
            // headings rendered near-black on charcoal and were barely
            // readable, which is how it was found: by opening it.
            //
            // Putting it here rather than in each screen means no screen can
            // forget, including the ones without a Scaffold.
            Surface(
                modifier = Modifier.fillMaxSize(),
                color = MaterialTheme.colorScheme.background,
                contentColor = MaterialTheme.colorScheme.onBackground,
                content = content,
            )
        }
    }
}

object PeriodThemeDefaults {
    val cycleColors: CycleColors
        @Composable @ReadOnlyComposable get() = LocalCycleColors.current
}
