// SPDX-FileCopyrightText: 2026 Reactorcoremeltdown
// SPDX-License-Identifier: GPL-3.0-or-later

package space.rcmd.android.sizzle.ui.theme

import android.app.Activity
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
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.SideEffect
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import androidx.core.view.WindowCompat

/**
 * The retro look-and-feel. Two ideas drive everything:
 *  1. ONE monospace font family for the entire UI (that's the "tracker" feel and
 *     it makes the grid columns line up perfectly).
 *  2. A small, named [RetroPalette] so colour *themes* can be swapped and
 *     imported/exported from the Settings tab without touching any screen code.
 *
 * The palette is exposed through a CompositionLocal ([LocalRetro]) so any
 * composable can read theme colours with `LocalRetro.current`.
 */
data class RetroPalette(
    val name: String,
    val background: Color,
    val surface: Color,      // panel / toolbar background
    val grid: Color,         // faint grid lines
    val text: Color,         // default monospace text
    val textDim: Color,      // secondary text
    val accent: Color,       // cursor / primary highlight (amber)
    val beat: Color,         // every-beat row tint
    val bar: Color,          // every-bar row tint
    val playhead: Color,     // moving playback line
    val danger: Color,       // mute / destructive
) {
    companion object {
        /** The built-in default: near-black with amber + green, classic demoscene. */
        val AMBER = RetroPalette(
            name = "Amber CRT",
            background = Color(0xFF0B0D0E),
            surface = Color(0xFF15181A),
            grid = Color(0xFF23282B),
            text = Color(0xFFCFE8D8),
            textDim = Color(0xFF6E7C74),
            accent = Color(0xFFF2C14E),
            beat = Color(0xFF141C1A),
            bar = Color(0xFF1E2A20),
            playhead = Color(0xFF7CFC9A),
            danger = Color(0xFFE05A4B),
        )

        /** A second built-in so the theme picker has something to switch to. */
        val ICE = RetroPalette(
            name = "Ice Terminal",
            background = Color(0xFF07090F),
            surface = Color(0xFF10151F),
            grid = Color(0xFF1E2633),
            text = Color(0xFFCFE0FF),
            textDim = Color(0xFF63708A),
            accent = Color(0xFF62B6FF),
            beat = Color(0xFF121A26),
            bar = Color(0xFF1B2740),
            playhead = Color(0xFF8AF0FF),
            danger = Color(0xFFE05A7A),
        )

        val ALL = listOf(AMBER, ICE)
    }
}

val LocalRetro = staticCompositionLocalOf { RetroPalette.AMBER }

/** Monospace typography used everywhere — no other font is loaded. */
private val Mono = FontFamily.Monospace
private val retroTypography = Typography(
    bodyLarge = TextStyle(fontFamily = Mono, fontSize = 14.sp, fontWeight = FontWeight.Normal),
    bodyMedium = TextStyle(fontFamily = Mono, fontSize = 12.sp),
    bodySmall = TextStyle(fontFamily = Mono, fontSize = 10.sp),
    labelLarge = TextStyle(fontFamily = Mono, fontSize = 13.sp, fontWeight = FontWeight.Bold),
    titleMedium = TextStyle(fontFamily = Mono, fontSize = 15.sp, fontWeight = FontWeight.Bold),
)

@Composable
fun SizzleTheme(
    palette: RetroPalette = RetroPalette.AMBER,
    @Suppress("UNUSED_PARAMETER") darkTheme: Boolean = isSystemInDarkTheme(), // follows the palette, not the system
    content: @Composable () -> Unit,
) {
    // Whether this palette reads as a light theme drives which Material base scheme
    // we start from. That matters because Material components (Card, ElevatedCard,
    // ListItem, DropdownMenu…) pull their container/variant colours from roles we
    // don't override — and the dark defaults look wrong (dark cards, muddy dividers)
    // on a bright palette. Starting from the matching base keeps those defaults sane;
    // then we paint the palette over every role components actually read.
    val isLight = palette.background.luminance() > 0.5f

    // Text/icon colour to sit *on top of* the accent and danger fills — chosen for
    // contrast against the fill itself, not the page, so buttons stay legible on any
    // palette (a light accent gets near-black ink, a dark accent gets near-white).
    val onAccent = if (palette.accent.luminance() > 0.5f) Color(0xFF0B0D0E) else Color(0xFFF7F7F7)
    val onDanger = if (palette.danger.luminance() > 0.5f) Color(0xFF0B0D0E) else Color(0xFFF7F7F7)

    val base = if (isLight) lightColorScheme() else darkColorScheme()
    val scheme = base.copy(
        primary = palette.accent,
        onPrimary = onAccent,
        primaryContainer = palette.accent,
        onPrimaryContainer = onAccent,
        secondary = palette.accent,
        onSecondary = onAccent,
        background = palette.background,
        onBackground = palette.text,
        surface = palette.surface,
        onSurface = palette.text,
        // Card / sheet / menu container roles all map to the panel colour so every
        // surface matches the toolbar instead of falling back to Material greys.
        surfaceVariant = palette.surface,
        onSurfaceVariant = palette.textDim,
        surfaceContainerLowest = palette.surface,
        surfaceContainerLow = palette.surface,
        surfaceContainer = palette.surface,
        surfaceContainerHigh = palette.surface,
        surfaceContainerHighest = palette.surface,
        surfaceBright = palette.surface,
        surfaceDim = palette.surface,
        outline = palette.grid,
        outlineVariant = palette.grid,
        error = palette.danger,
        onError = onDanger,
        errorContainer = palette.danger,
        onErrorContainer = onDanger,
        // Disable Material's automatic elevation tint so cards keep the exact panel
        // colour rather than being tinted toward the primary at higher elevations.
        surfaceTint = palette.surface,
    )

    // Tint the system navigation bar to match the app's bottom tab bar (also
    // `surface`) so the gesture handle below it looks seamless — and re-tint when the
    // palette changes. The status bar keeps the window background for the same reason.
    val view = LocalView.current
    if (!view.isInEditMode) {
        SideEffect {
            val window = (view.context as Activity).window
            window.navigationBarColor = palette.surface.toArgb()
            window.statusBarColor = palette.background.toArgb()
            val lightSurface = palette.surface.luminance() > 0.5f
            WindowCompat.getInsetsController(window, view).apply {
                isAppearanceLightNavigationBars = lightSurface
                isAppearanceLightStatusBars = palette.background.luminance() > 0.5f
            }
        }
    }

    CompositionLocalProvider(LocalRetro provides palette) {
        MaterialTheme(colorScheme = scheme, typography = retroTypography, content = content)
    }
}
