package com.ponzischeme89.memby.ui

import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.clickable
import androidx.compose.foundation.focusable
import androidx.compose.foundation.interaction.MutableInteractionSource
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.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Build
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusProperties
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.tv.material3.Icon
import androidx.tv.material3.Text
import kotlinx.coroutines.delay
import com.ponzischeme89.memby.ui.theme.MembyAccent

private val MaintenanceAccent: Color get() = MembyAccent
private val MaintenanceTitle = Color(0xFFF2F5F7)
private val MaintenanceBody = Color(0xFFAEB7BF)
private val MaintenanceFaint = Color(0xFFA2ADB5)

/** How long between automatic retries while the gateway is down. */
private const val RETRY_SECONDS = 30

/**
 * Fills the content area while the gateway reports a deliberate outage.
 *
 * The navigation rail deliberately stays mounted beside this: Settings and Switch user
 * are local, so there is no reason to strand the viewer just because content is
 * unavailable. Everything here is cheap to draw — a handful of animated floats and plain
 * Canvas geometry — because TV GPUs punish blur and layered transparency.
 */
@Composable
fun MaintenanceScreen(
    message: String,
    contentFocusRequester: FocusRequester,
    navigationFocusRequester: FocusRequester,
    onRetry: () -> Unit,
    modifier: Modifier = Modifier,
) {
    val transition = rememberInfiniteTransition(label = "maintenance")

    // One slow drift drives the background glow; one fast-ish phase drives the rings.
    val glow by transition.animateFloat(
        initialValue = 0f,
        targetValue = 1f,
        animationSpec = infiniteRepeatable(tween(9_000, easing = LinearEasing), RepeatMode.Reverse),
        label = "glow",
    )
    val pulse by transition.animateFloat(
        initialValue = 0f,
        targetValue = 1f,
        animationSpec = infiniteRepeatable(tween(3_200, easing = LinearEasing)),
        label = "pulse",
    )
    val gearRotation by transition.animateFloat(
        initialValue = 0f,
        targetValue = 360f,
        animationSpec = infiniteRepeatable(tween(24_000, easing = LinearEasing)),
        label = "gear",
    )
    val sweep by transition.animateFloat(
        initialValue = 0f,
        targetValue = 1f,
        animationSpec = infiniteRepeatable(tween(1_900, easing = LinearEasing)),
        label = "sweep",
    )

    // Entrance: content settles in rather than snapping, so the switch from rows to this
    // screen reads as intentional.
    var entered by remember { mutableStateOf(false) }
    LaunchedEffect(Unit) { entered = true }
    val entrance by animateFloatAsState(
        targetValue = if (entered) 1f else 0f,
        animationSpec = tween(420),
        label = "maintenance-entrance",
    )

    var secondsLeft by remember { mutableIntStateOf(RETRY_SECONDS) }
    LaunchedEffect(message) {
        // Restarts whenever the message changes, so a failed retry resets the clock.
        secondsLeft = RETRY_SECONDS
        while (true) {
            delay(1_000)
            secondsLeft -= 1
            if (secondsLeft <= 0) {
                onRetry()
                secondsLeft = RETRY_SECONDS
            }
        }
    }

    Box(modifier = modifier.fillMaxSize()) {
        MaintenanceBackdrop(glow = glow)

        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(horizontal = 56.dp)
                .graphicsLayer {
                    alpha = entrance
                    translationY = (1f - entrance) * 26.dp.toPx()
                },
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center,
        ) {
            PulsingEmblem(pulse = pulse, gearRotation = gearRotation)

            Spacer(Modifier.height(30.dp))
            Text(
                "Memby is taking a short break",
                color = MaintenanceTitle,
                fontSize = 34.sp,
                fontWeight = FontWeight.SemiBold,
                textAlign = TextAlign.Center,
            )

            Spacer(Modifier.height(12.dp))
            Text(
                message,
                color = MaintenanceBody,
                fontSize = 17.sp,
                textAlign = TextAlign.Center,
                modifier = Modifier.widthIn(max = 620.dp),
            )

            Spacer(Modifier.height(28.dp))
            SweepBar(progress = sweep)

            Spacer(Modifier.height(28.dp))
            Row(
                verticalAlignment = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.spacedBy(18.dp),
            ) {
                RetryButton(
                    onClick = {
                        secondsLeft = RETRY_SECONDS
                        onRetry()
                    },
                    modifier = Modifier
                        .focusRequester(contentFocusRequester)
                        // Without this the viewer can reach the button but never get
                        // back to the rail, since nothing else here is focusable.
                        .focusProperties { left = navigationFocusRequester },
                )
                Text(
                    "Checking again in ${secondsLeft}s",
                    color = MaintenanceFaint,
                    fontSize = 14.sp,
                )
            }
        }
    }
}

// The animations here are infinite, so a preview catches them mid-phase. That is fine for
// checking layout and colour; the motion itself only reads correctly on a device.
@TvPreview
@Composable
private fun MaintenanceScreenPreview() {
    PreviewSurface {
        MaintenanceScreen(
            message = "Back after dinner — the library is being reorganised.",
            contentFocusRequester = remember { FocusRequester() },
            navigationFocusRequester = remember { FocusRequester() },
            onRetry = {},
        )
    }
}

/** Vertical wash plus a radial glow that drifts, so the screen is never quite static. */
@Composable
private fun MaintenanceBackdrop(glow: Float) {
    Canvas(Modifier.fillMaxSize()) {
        drawRect(
            brush = Brush.verticalGradient(
                listOf(Color(0xFF0B0F14), Color(0xFF121A22), Color(0xFF0A0D11)),
            ),
        )
        val centre = Offset(
            x = size.width * (0.42f + 0.16f * glow),
            y = size.height * (0.38f + 0.10f * (1f - glow)),
        )
        val radius = size.minDimension * (0.55f + 0.08f * glow)
        drawCircle(
            brush = Brush.radialGradient(
                colors = listOf(MaintenanceAccent.copy(alpha = 0.13f), Color.Transparent),
                center = centre,
                radius = radius,
            ),
            radius = radius,
            center = centre,
        )
    }
}

/**
 * Three rings expanding outward on staggered phases, with a slowly turning gear at the
 * centre. Drawn in one Canvas: three composables with their own animations would cost
 * three recompositions per frame for the same picture.
 */
@Composable
private fun PulsingEmblem(pulse: Float, gearRotation: Float) {
    Box(contentAlignment = Alignment.Center) {
        Canvas(Modifier.size(210.dp)) {
            val base = size.minDimension * 0.22f
            repeat(3) { index ->
                // Stagger the phases so the rings never bunch up together.
                val phase = (pulse + index / 3f) % 1f
                val radius = base * (1f + phase * 1.6f)
                drawCircle(
                    color = MaintenanceAccent.copy(alpha = 0.34f * (1f - phase)),
                    radius = radius,
                    center = center,
                    style = androidx.compose.ui.graphics.drawscope.Stroke(width = 2.dp.toPx()),
                )
            }
            drawCircle(
                brush = Brush.radialGradient(
                    colors = listOf(MaintenanceAccent.copy(alpha = 0.22f), Color.Transparent),
                    center = center,
                    radius = base * 1.35f,
                ),
                radius = base * 1.35f,
                center = center,
            )
        }

        Box(
            modifier = Modifier
                .size(96.dp)
                .clip(CircleShape)
                .background(Color(0xFF16202A))
                .border(1.dp, MaintenanceAccent.copy(alpha = 0.35f), CircleShape),
            contentAlignment = Alignment.Center,
        ) {
            Icon(
                imageVector = Icons.Default.Build,
                contentDescription = null,
                tint = MaintenanceAccent,
                modifier = Modifier
                    .size(40.dp)
                    .graphicsLayer { rotationZ = gearRotation },
            )
        }
    }
}

/** An indeterminate bar: a highlight sweeping a dim track, looping. */
@Composable
private fun SweepBar(progress: Float) {
    Canvas(
        Modifier
            .width(300.dp)
            .height(3.dp),
    ) {
        val corner = androidx.compose.ui.geometry.CornerRadius(size.height / 2f)
        drawRoundRect(color = Color.White.copy(alpha = 0.07f), cornerRadius = corner)

        // The highlight starts off-screen left and exits right, so the loop point is
        // invisible rather than a visible jump back to the start.
        val bandWidth = size.width * 0.32f
        val x = -bandWidth + (size.width + bandWidth) * progress
        drawRoundRect(
            brush = Brush.horizontalGradient(
                colors = listOf(Color.Transparent, MaintenanceAccent.copy(alpha = 0.85f), Color.Transparent),
                startX = x,
                endX = x + bandWidth,
            ),
            topLeft = Offset(x.coerceAtLeast(0f), 0f),
            size = Size(
                width = (x + bandWidth).coerceAtMost(size.width) - x.coerceAtLeast(0f),
                height = size.height,
            ),
            cornerRadius = corner,
        )
    }
}

@Composable
private fun RetryButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
    var focused by remember { mutableStateOf(false) }
    val scale by animateFloatAsState(
        targetValue = if (focused) 1.06f else 1f,
        animationSpec = tween(120),
        label = "retry-scale",
    )

    Box(
        modifier = modifier
            .graphicsLayer { scaleX = scale; scaleY = scale }
            .clip(RoundedCornerShape(10.dp))
            .background(if (focused) MaintenanceAccent else Color(0xFF1E2833))
            .border(
                width = if (focused) 0.dp else 1.dp,
                color = Color.White.copy(alpha = 0.12f),
                shape = RoundedCornerShape(10.dp),
            )
            .onFocusChanged { focused = it.isFocused }
            .focusable(interactionSource = remember { MutableInteractionSource() })
            .clickable(onClick = onClick)
            .padding(horizontal = 26.dp, vertical = 12.dp),
    ) {
        Text(
            "Try again",
            color = if (focused) Color(0xFF06240A) else MaintenanceTitle,
            fontSize = 16.sp,
            fontWeight = FontWeight.SemiBold,
        )
    }
}
