package com.ponzischeme89.memby.ui

import androidx.compose.foundation.background
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.height
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
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.focus.FocusRequester
import androidx.compose.ui.focus.focusProperties
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.LocalContext
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.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.tv.material3.Text
import com.ponzischeme89.memby.ui.theme.MembyAccent
import com.ponzischeme89.memby.ui.theme.MembyMutedText
import com.ponzischeme89.memby.ui.theme.MembyOnSurface
import com.ponzischeme89.memby.ui.theme.MembyQuietText
import com.ponzischeme89.memby.ui.theme.MembySurface
import com.ponzischeme89.memby.update.InstallPermission

/**
 * The setup step that asks for permission to install updates.
 *
 * Every TV running Memby was sideloaded, which means the app that installed it holds
 * Android's per-app install permission and Memby does not. Without this step nobody is ever
 * asked: the permission is discovered to be missing months later, in the middle of an update
 * that then cannot proceed, and the household's answer becomes "reinstall it by hand" — once
 * per release, forever.
 *
 * It is deliberately **skippable**. A fresh install must never be blocked by a permission
 * that only matters later, and on a TV with no permission screen to open there would be
 * nothing the viewer could do to satisfy it. Skipping costs them the same prompt at update
 * time, which is where it used to live.
 */
@Composable
fun InstallPermissionScreen(onContinue: () -> Unit) {
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current
    var noScreenAvailable by remember { mutableStateOf(false) }
    var asked by remember { mutableStateOf(false) }

    // Granting happens in Android's settings, which pauses Memby. Coming back with the
    // permission in hand should simply carry on rather than leave the viewer looking at a
    // step they have already completed.
    DisposableEffect(lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_RESUME && asked && InstallPermission.granted(context)) {
                onContinue()
            }
        }
        lifecycleOwner.lifecycle.addObserver(observer)
        onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
    }

    InstallPermissionContent(
        noScreenAvailable = noScreenAvailable,
        onOpenSettings = {
            asked = true
            noScreenAvailable = !InstallPermission.requestScreen(context)
        },
        onSkip = onContinue,
    )
}

/**
 * The screen itself, stateless so it can be screenshotted in both of its states
 * (`OnboardingScreenshotTest`).
 *
 * The instructions are the substance here. Android's own screen is a bare list of app names
 * with switches and no explanation of why anyone was sent there, and on a television the
 * viewer has a remote, no back button they trust, and no idea what "unknown sources" means.
 * Naming the steps — *find Memby, turn it on, press Back* — is the difference between a
 * permission that gets granted and one that gets abandoned halfway.
 */
@Composable
fun InstallPermissionContent(
    noScreenAvailable: Boolean,
    onOpenSettings: () -> Unit,
    onSkip: () -> Unit,
    modifier: Modifier = Modifier,
) {
    val allowFocus = remember { FocusRequester() }
    val skipFocus = remember { FocusRequester() }
    LaunchedEffect(Unit) { runCatching { allowFocus.requestFocus() } }

    Box(
        modifier.fillMaxSize().background(MembySurface),
        contentAlignment = Alignment.Center,
    ) {
        Column(
            modifier = Modifier.widthIn(max = 780.dp).padding(horizontal = 56.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
        ) {
            Text(
                "Let Memby update itself",
                color = MembyOnSurface,
                fontSize = 34.sp,
                fontWeight = FontWeight.SemiBold,
                textAlign = TextAlign.Center,
            )
            Spacer(Modifier.height(14.dp))
            Text(
                "Memby installs its own updates. Because it was installed from a file rather " +
                    "than a store, Android needs your permission once before it will let it.",
                color = MembyMutedText,
                fontSize = 17.sp,
                textAlign = TextAlign.Center,
            )

            Spacer(Modifier.height(26.dp))
            if (noScreenAvailable) {
                PermissionStep(1, "Open ${InstallPermission.MANUAL_PATH} on this TV.")
                PermissionStep(2, "Find Memby in the list and turn it on.")
                PermissionStep(3, "Come back here and carry on.")
            } else {
                PermissionStep(1, "Choose Open settings below.")
                PermissionStep(2, "Turn on the switch for Memby.")
                PermissionStep(3, "Press Back on your remote — Memby carries on by itself.")
            }

            Spacer(Modifier.height(18.dp))
            Text(
                "You can skip this. Memby will ask again the first time an update needs it, " +
                    "and until then nothing changes.",
                color = MembyQuietText,
                fontSize = 14.sp,
                textAlign = TextAlign.Center,
            )

            Spacer(Modifier.height(30.dp))
            Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
                UpdateButton(
                    label = if (noScreenAvailable) "Try again" else "Open settings",
                    primary = true,
                    enabled = true,
                    onClick = onOpenSettings,
                    modifier = Modifier
                        .focusRequester(allowFocus)
                        .focusProperties { right = skipFocus },
                )
                UpdateButton(
                    label = "Skip for now",
                    primary = false,
                    enabled = true,
                    onClick = onSkip,
                    modifier = Modifier
                        .focusRequester(skipFocus)
                        .focusProperties { left = allowFocus },
                )
            }
        }
    }
}

/** One numbered instruction. Never focusable — reading matter, not a control. */
@Composable
private fun PermissionStep(number: Int, text: String) {
    Row(
        modifier = Modifier.fillMaxWidth().padding(vertical = 5.dp),
        horizontalArrangement = Arrangement.spacedBy(14.dp),
    ) {
        Text(
            "$number",
            color = MembyAccent,
            fontSize = 16.sp,
            fontWeight = FontWeight.Bold,
            modifier = Modifier.width(20.dp),
            textAlign = TextAlign.End,
        )
        Text(text, color = MembyOnSurface.copy(alpha = 0.88f), fontSize = 16.sp)
    }
}
