package dev.privacyllc.period.feature.lock

import android.content.res.Configuration
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
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.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.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 dev.privacyllc.period.designsystem.PeriodTheme

/**
 * Consent first, then the PIN.
 *
 * ## Why this is a screen and not a checkbox
 *
 * "There is no way to reset this" is only fair if it is said **before** the
 * decision, in words, on its own. A checkbox under a PIN field is a thing people
 * tick; a sentence they have to read and press past is a thing they know. The
 * owner ruled out any recovery path, and that decision is only defensible if the
 * person setting the PIN was told plainly at the time rather than on the day
 * they forget.
 *
 * ## Why it also says what the lock does *not* do
 *
 * The second half is the part a product would normally leave out. This lock
 * stops the app opening; it does not encrypt anything. The records sit in
 * app-private storage, and somebody who unlocks the phone and connects it to a
 * computer, or who has rooted it, is not stopped by a PIN. `SECURITY.md` already
 * lists both as out of scope, and a lock screen that implied otherwise would be
 * this product's one dishonest moment.
 */
@Composable
internal fun PinSetupScreen(
    busy: Boolean,
    failed: Boolean,
    onCancel: () -> Unit,
    onConfirmed: (CharArray) -> Unit,
) {
    var consented by remember { mutableStateOf(false) }
    if (!consented) {
        Consent(onCancel = onCancel, onAccept = { consented = true })
    } else {
        PinEntry(busy = busy, failed = failed, onCancel = onCancel, onConfirmed = onConfirmed)
    }
}

@Composable
private fun Consent(onCancel: () -> Unit, onAccept: () -> Unit) {
    Surface(modifier = Modifier.fillMaxSize()) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .verticalScroll(rememberScrollState())
                .padding(24.dp),
            verticalArrangement = Arrangement.Center,
        ) {
            Text("Before you set a PIN", style = MaterialTheme.typography.headlineMedium)

            Spacer(Modifier.height(20.dp))

            Text("There is no way to reset it", style = MaterialTheme.typography.titleMedium)
            Spacer(Modifier.height(6.dp))
            Text(
                "Nobody can reset this PIN — not us, not a support email, not a security " +
                    "question. If you forget it, the only way back into the app is to erase " +
                    "everything you have recorded and start again.",
                style = MaterialTheme.typography.bodyLarge,
            )

            Spacer(Modifier.height(20.dp))

            Text("What the lock does, and what it does not", style = MaterialTheme.typography.titleMedium)
            Spacer(Modifier.height(6.dp))
            Text(
                "It stops the app opening without your PIN. That is the thing it is for: " +
                    "somebody picking up your unlocked phone.",
                style = MaterialTheme.typography.bodyLarge,
            )
            Spacer(Modifier.height(6.dp))
            Text(
                "It does not encrypt what you have recorded. Somebody who unlocks this " +
                    "phone and connects it to a computer, or who has rooted it, is not " +
                    "stopped by this PIN. We would rather tell you that than let a padlock " +
                    "imply otherwise.",
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
            )

            Spacer(Modifier.height(20.dp))

            Text(
                "While the lock is on, the app is hidden in the task switcher and " +
                    "screenshots are blocked.",
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
            )

            Spacer(Modifier.height(28.dp))

            Button(onClick = onAccept, modifier = Modifier.fillMaxWidth()) {
                Text("I understand — choose a PIN")
            }
            TextButton(onClick = onCancel, modifier = Modifier.fillMaxWidth()) {
                Text("Not now")
            }
        }
    }
}

@Composable
private fun PinEntry(
    busy: Boolean,
    failed: Boolean,
    onCancel: () -> Unit,
    onConfirmed: (CharArray) -> Unit,
) {
    var first by remember { mutableStateOf("") }
    var second by remember { mutableStateOf("") }
    var mismatch by remember { mutableStateOf(false) }

    Surface(modifier = Modifier.fillMaxSize()) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .verticalScroll(rememberScrollState())
                .padding(24.dp),
            verticalArrangement = Arrangement.Center,
        ) {
            Text("Choose a PIN", style = MaterialTheme.typography.headlineMedium)
            Spacer(Modifier.height(8.dp))
            Text(
                "Between $MIN_PIN and $MAX_PIN digits.",
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
            )

            Spacer(Modifier.height(20.dp))

            PinField(value = first, label = "PIN", enabled = !busy) {
                first = it; mismatch = false
            }
            Spacer(Modifier.height(12.dp))
            PinField(value = second, label = "Repeat PIN", enabled = !busy) {
                second = it; mismatch = false
            }

            if (mismatch || failed) {
                Spacer(Modifier.height(8.dp))
                Text(
                    if (mismatch) {
                        "Those do not match."
                    } else {
                        "This device could not store a PIN, so the lock has not been turned on."
                    },
                    style = MaterialTheme.typography.bodyMedium,
                    color = MaterialTheme.colorScheme.error,
                    modifier = Modifier.semantics { liveRegion = LiveRegionMode.Polite },
                )
            }

            Spacer(Modifier.height(20.dp))

            Button(
                onClick = {
                    if (first == second) onConfirmed(first.toCharArray()) else mismatch = true
                },
                enabled = !busy && first.length >= MIN_PIN && second.isNotEmpty(),
                modifier = Modifier.fillMaxWidth(),
            ) {
                Text("Turn on app lock")
            }
            TextButton(onClick = onCancel, modifier = Modifier.fillMaxWidth()) { Text("Cancel") }
        }
    }
}

@Composable
private fun PinField(value: String, label: String, enabled: Boolean, onChange: (String) -> Unit) {
    OutlinedTextField(
        value = value,
        onValueChange = { onChange(it.filter(Char::isDigit).take(MAX_PIN)) },
        singleLine = true,
        enabled = enabled,
        label = { Text(label) },
        visualTransformation = PasswordVisualTransformation(),
        keyboardOptions = KeyboardOptions(
            keyboardType = KeyboardType.NumberPassword,
            imeAction = ImeAction.Next,
        ),
        modifier = Modifier
            .fillMaxWidth()
            .semantics { password() },
    )
}

private const val MIN_PIN = 4
private const val MAX_PIN = 12

@Preview(name = "Consent · light", showBackground = true, heightDp = 900)
@Preview(
    name = "Consent · dark",
    showBackground = true,
    heightDp = 900,
    uiMode = Configuration.UI_MODE_NIGHT_YES,
)
@Preview(name = "Consent · font 2.0", showBackground = true, heightDp = 1800, fontScale = 2.0f)
@Composable
private fun ConsentPreview() {
    PeriodTheme { PinSetupScreen(busy = false, failed = false, onCancel = {}, onConfirmed = {}) }
}
