package dev.privacyllc.period.feature.lock

import android.content.res.Configuration
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
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.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
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.semantics.LiveRegionMode
import androidx.compose.ui.semantics.liveRegion
import androidx.compose.ui.semantics.password
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import dev.privacyllc.period.designsystem.PeriodTheme

private enum class Mode { OVERVIEW, SET_FIRST, CONFIRM_TO_REMOVE, CONFIRM_TO_CHANGE, SET_REPLACEMENT }

/**
 * §36's App lock, one level under Privacy & Security.
 *
 * Turning the lock **off** asks for the current PIN, and so does changing it.
 * Without that, the lock protects nothing: whoever picked up the unlocked phone
 * would simply switch it off in Settings — which is the adversary this feature
 * exists for. Setting the first PIN needs no authentication because there is
 * nothing yet to authenticate against.
 */
@Composable
fun LockSettingsScreen(viewModel: LockSettingsViewModel = hiltViewModel()) {
    val state by viewModel.state.collectAsStateWithLifecycle()
    var mode by remember { mutableStateOf(Mode.OVERVIEW) }

    when (mode) {
        Mode.OVERVIEW -> Overview(
            state = state,
            onSetPin = { mode = Mode.SET_FIRST },
            onChangePin = { mode = Mode.CONFIRM_TO_CHANGE },
            onRemovePin = { mode = Mode.CONFIRM_TO_REMOVE },
            onBiometric = viewModel::setBiometricEnabled,
            onDismissMessage = viewModel::clearMessage,
        )

        Mode.SET_FIRST, Mode.SET_REPLACEMENT -> PinSetupScreen(
            busy = state.busy,
            failed = state.message == LockSettings.Message.COULD_NOT_SET,
            onCancel = { mode = Mode.OVERVIEW; viewModel.clearMessage() },
            onConfirmed = { pin -> viewModel.setPin(pin); mode = Mode.OVERVIEW },
        )

        Mode.CONFIRM_TO_REMOVE -> ConfirmPin(
            title = "Enter your PIN to turn the lock off",
            busy = state.busy,
            wrong = state.message == LockSettings.Message.WRONG_PIN,
            onCancel = { mode = Mode.OVERVIEW; viewModel.clearMessage() },
            onSubmit = { pin -> viewModel.removePin(pin); mode = Mode.OVERVIEW },
        )

        Mode.CONFIRM_TO_CHANGE -> ConfirmPin(
            title = "Enter your current PIN",
            busy = state.busy,
            wrong = state.message == LockSettings.Message.WRONG_PIN,
            onCancel = { mode = Mode.OVERVIEW; viewModel.clearMessage() },
            onSubmit = { pin ->
                viewModel.verifyCurrent(pin) { }
                mode = Mode.SET_REPLACEMENT
            },
        )
    }
}

@Composable
private fun Overview(
    state: LockSettings,
    onSetPin: () -> Unit,
    onChangePin: () -> Unit,
    onRemovePin: () -> Unit,
    onBiometric: (Boolean) -> Unit,
    onDismissMessage: () -> Unit,
) {
    Surface(modifier = Modifier.fillMaxSize()) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .verticalScroll(rememberScrollState())
                .padding(vertical = 16.dp),
        ) {
            Text(
                "App lock",
                style = MaterialTheme.typography.headlineSmall,
                modifier = Modifier.padding(horizontal = 24.dp),
            )

            Spacer(Modifier.height(8.dp))

            Text(
                if (state.hasPin) {
                    "The app asks for your PIN before it opens. While the lock is on, it is " +
                        "hidden in the task switcher and screenshots are blocked."
                } else {
                    "Ask for a PIN before the app opens. There is no way to reset a forgotten " +
                        "PIN, so you will be asked to confirm you understand that first."
                },
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                modifier = Modifier.padding(horizontal = 24.dp),
            )

            Spacer(Modifier.height(16.dp))

            if (!state.hasPin) {
                Row(modifier = Modifier.padding(horizontal = 24.dp)) {
                    Button(onClick = onSetPin, enabled = !state.busy) { Text("Set a PIN") }
                }
            } else {
                LockRow("Change PIN", "You will be asked for the current one first", onChangePin)
                LockRow("Turn off app lock", "The app will open without a PIN", onRemovePin)

                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(horizontal = 24.dp, vertical = 12.dp),
                    verticalAlignment = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.SpaceBetween,
                ) {
                    Column(modifier = Modifier.fillMaxWidth(0.75f)) {
                        Text("Unlock with fingerprint", style = MaterialTheme.typography.bodyLarge)
                        Text(
                            // The thing a person cannot otherwise know, said plainly.
                            "Anyone whose fingerprint or face is set up on this phone will be " +
                                "able to open the app.",
                            style = MaterialTheme.typography.bodySmall,
                            color = MaterialTheme.colorScheme.onSurfaceVariant,
                        )
                    }
                    Switch(checked = state.biometricEnabled, onCheckedChange = onBiometric)
                }
            }

            state.message?.let { message ->
                Spacer(Modifier.height(16.dp))
                Text(
                    text = when (message) {
                        LockSettings.Message.PIN_SET -> "App lock is on."
                        LockSettings.Message.PIN_REMOVED -> "App lock is off."
                        LockSettings.Message.WRONG_PIN -> "That is not the PIN."
                        LockSettings.Message.COULD_NOT_SET ->
                            "This device could not store a PIN, so the lock has not been turned on."
                    },
                    style = MaterialTheme.typography.bodyMedium,
                    color = if (message == LockSettings.Message.WRONG_PIN) {
                        MaterialTheme.colorScheme.error
                    } else {
                        MaterialTheme.colorScheme.onSurfaceVariant
                    },
                    modifier = Modifier
                        .padding(horizontal = 24.dp)
                        .semantics { liveRegion = LiveRegionMode.Polite },
                )
                TextButton(
                    onClick = onDismissMessage,
                    modifier = Modifier.padding(horizontal = 16.dp),
                ) { Text("Done") }
            }
        }
    }
}

@Composable
private fun LockRow(title: String, subtitle: String, onClick: () -> Unit) {
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .clickable(onClick = onClick)
            .padding(horizontal = 24.dp, vertical = 12.dp),
    ) {
        Text(title, style = MaterialTheme.typography.bodyLarge)
        Text(
            subtitle,
            style = MaterialTheme.typography.bodySmall,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
        )
    }
}

@Composable
private fun ConfirmPin(
    title: String,
    busy: Boolean,
    wrong: Boolean,
    onCancel: () -> Unit,
    onSubmit: (CharArray) -> Unit,
) {
    var pin by remember { mutableStateOf("") }
    Surface(modifier = Modifier.fillMaxSize()) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .verticalScroll(rememberScrollState())
                .padding(24.dp),
            verticalArrangement = Arrangement.Center,
        ) {
            Text(title, style = MaterialTheme.typography.headlineSmall)
            Spacer(Modifier.height(16.dp))
            OutlinedTextField(
                value = pin,
                onValueChange = { pin = it.filter(Char::isDigit).take(12) },
                singleLine = true,
                enabled = !busy,
                label = { Text("PIN") },
                visualTransformation = PasswordVisualTransformation(),
                keyboardOptions = KeyboardOptions(
                    keyboardType = KeyboardType.NumberPassword,
                    imeAction = ImeAction.Done,
                ),
                modifier = Modifier
                    .fillMaxWidth()
                    .semantics { password() },
            )
            if (wrong) {
                Spacer(Modifier.height(8.dp))
                Text(
                    "That is not the PIN.",
                    color = MaterialTheme.colorScheme.error,
                    style = MaterialTheme.typography.bodyMedium,
                    modifier = Modifier.semantics { liveRegion = LiveRegionMode.Polite },
                )
            }
            Spacer(Modifier.height(20.dp))
            Button(
                onClick = { onSubmit(pin.toCharArray()); pin = "" },
                enabled = !busy && pin.length >= 4,
                modifier = Modifier.fillMaxWidth(),
            ) { Text("Continue") }
            TextButton(onClick = onCancel, modifier = Modifier.fillMaxWidth()) { Text("Cancel") }
        }
    }
}

@Preview(name = "App lock · off", showBackground = true)
@Preview(name = "App lock · off, dark", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Preview(name = "App lock · off, font 2.0", showBackground = true, heightDp = 1000, fontScale = 2.0f)
@Composable
private fun LockSettingsOffPreview() {
    PeriodTheme {
        Overview(LockSettings(hasPin = false), {}, {}, {}, {}, {})
    }
}

@Preview(name = "App lock · on", showBackground = true)
@Preview(name = "App lock · on, font 2.0", showBackground = true, heightDp = 1200, fontScale = 2.0f)
@Composable
private fun LockSettingsOnPreview() {
    PeriodTheme {
        Overview(LockSettings(hasPin = true, biometricEnabled = true), {}, {}, {}, {}, {})
    }
}
