/*
 * SPDX-License-Identifier: GPL-3.0-or-later
 */
package com.runicgateway.app.ui.auth

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.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.Checkbox
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
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.platform.LocalContext
import androidx.compose.ui.res.stringResource
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.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.runicgateway.app.R
import com.runicgateway.app.core.web.WebHandoff
import com.runicgateway.app.ui.auth.LoginViewModel.LoginError

/**
 * Native username/password (+TOTP) login (PLAN.md §4.1) — the app's only native
 * credential screen. Registration, forgot-password, and SSO are website hand-offs
 * opened in a Custom Tab (§4.2); the user completes them in the browser and
 * returns here to sign in.
 */
@Composable
fun LoginScreen(
    onSignedIn: () -> Unit,
    modifier: Modifier = Modifier,
    viewModel: LoginViewModel = hiltViewModel(),
) {
    val state by viewModel.state.collectAsStateWithLifecycle()
    val context = LocalContext.current

    LaunchedEffect(state.signedIn) {
        if (state.signedIn) onSignedIn()
    }

    // Open a freshly-minted SSO /start URL in a Custom Tab, exactly once (§4.2).
    LaunchedEffect(state.ssoLaunchUrl) {
        val url = state.ssoLaunchUrl ?: return@LaunchedEffect
        WebHandoff.open(context, url)
        viewModel.onSsoLaunchConsumed()
    }

    Column(
        modifier = modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState())
            .padding(24.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
    ) {
        Text(
            text = stringResource(R.string.login_title),
            style = MaterialTheme.typography.headlineSmall,
            textAlign = TextAlign.Center,
        )
        Text(
            text = stringResource(R.string.login_subtitle),
            style = MaterialTheme.typography.bodyMedium,
            textAlign = TextAlign.Center,
            modifier = Modifier.padding(top = 8.dp, bottom = 24.dp),
        )

        OutlinedTextField(
            value = state.username,
            onValueChange = viewModel::onUsernameChange,
            singleLine = true,
            enabled = !state.submitting,
            label = { Text(stringResource(R.string.login_username)) },
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Text,
                imeAction = ImeAction.Next,
            ),
            modifier = Modifier.fillMaxWidth(),
        )

        OutlinedTextField(
            value = state.password,
            onValueChange = viewModel::onPasswordChange,
            singleLine = true,
            enabled = !state.submitting,
            label = { Text(stringResource(R.string.login_password)) },
            visualTransformation = PasswordVisualTransformation(),
            keyboardOptions = KeyboardOptions(
                keyboardType = KeyboardType.Password,
                imeAction = if (state.totpRequired) ImeAction.Next else ImeAction.Go,
            ),
            keyboardActions = KeyboardActions(onGo = { viewModel.submit() }),
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = 12.dp),
        )

        if (state.totpRequired) {
            if (state.useRecoveryCode) {
                OutlinedTextField(
                    value = state.recoveryCode,
                    onValueChange = viewModel::onRecoveryCodeChange,
                    singleLine = true,
                    enabled = !state.submitting,
                    label = { Text(stringResource(R.string.login_recovery_code)) },
                    supportingText = { Text(stringResource(R.string.login_recovery_hint)) },
                    keyboardOptions = KeyboardOptions(
                        keyboardType = KeyboardType.Password,
                        imeAction = ImeAction.Go,
                    ),
                    keyboardActions = KeyboardActions(onGo = { viewModel.submit() }),
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(top = 12.dp),
                )
            } else {
                OutlinedTextField(
                    value = state.code,
                    onValueChange = viewModel::onCodeChange,
                    singleLine = true,
                    enabled = !state.submitting,
                    label = { Text(stringResource(R.string.login_totp_code)) },
                    supportingText = { Text(stringResource(R.string.login_totp_hint)) },
                    keyboardOptions = KeyboardOptions(
                        keyboardType = KeyboardType.NumberPassword,
                        imeAction = ImeAction.Go,
                    ),
                    keyboardActions = KeyboardActions(onGo = { viewModel.submit() }),
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(top = 12.dp),
                )
            }

            // Toggle between authenticator code and a single-use recovery code.
            TextButton(
                onClick = { viewModel.onUseRecoveryCodeChange(!state.useRecoveryCode) },
                enabled = !state.submitting,
                modifier = Modifier.align(Alignment.Start),
            ) {
                Text(
                    stringResource(
                        if (state.useRecoveryCode) R.string.login_use_totp_instead
                        else R.string.login_use_recovery_instead,
                    ),
                )
            }

            // "Trust this device" → skip the 2FA step on future logins here.
            Row(
                verticalAlignment = Alignment.CenterVertically,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 4.dp),
            ) {
                Checkbox(
                    checked = state.trustDevice,
                    onCheckedChange = viewModel::onTrustDeviceChange,
                    enabled = !state.submitting,
                )
                Text(
                    text = stringResource(R.string.login_trust_device),
                    style = MaterialTheme.typography.bodyMedium,
                )
            }
        }

        state.error?.let { err ->
            Text(
                text = stringResource(loginErrorRes(err)),
                color = MaterialTheme.colorScheme.error,
                style = MaterialTheme.typography.bodyMedium,
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 12.dp),
            )
        }

        Button(
            onClick = viewModel::submit,
            enabled = !state.submitting,
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = 20.dp),
        ) {
            if (state.submitting) {
                CircularProgressIndicator(
                    strokeWidth = 2.dp,
                    modifier = Modifier.size(20.dp),
                    color = MaterialTheme.colorScheme.onPrimary,
                )
            } else {
                Text(stringResource(R.string.login_button))
            }
        }

        // ── Native SSO (§4.2, M9): a single "Sign in with SSO" button that opens the
        // Custom-Tab bridge. With one provider it launches straight through; with
        // several it presents a native picker (below). No website-login fallback —
        // that page can't deep-link the session back; a failed discovery offers a retry.
        var showSsoPicker by remember { mutableStateOf(false) }
        when {
            state.ssoProviders.isNotEmpty() -> {
                OutlinedButton(
                    onClick = {
                        val providers = state.ssoProviders
                        if (providers.size == 1) viewModel.onSsoProviderClick(providers.first())
                        else showSsoPicker = true
                    },
                    enabled = !state.submitting,
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(top = 12.dp),
                ) {
                    Text(stringResource(R.string.login_sso_button))
                }
            }

            state.ssoDiscovering -> {
                Text(
                    text = stringResource(R.string.login_sso_loading),
                    style = MaterialTheme.typography.bodySmall,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                    modifier = Modifier.padding(top = 12.dp),
                )
            }

            state.ssoUnavailable -> {
                TextButton(
                    onClick = { viewModel.discoverSsoProviders() },
                    modifier = Modifier.padding(top = 4.dp),
                ) {
                    Text(stringResource(R.string.login_sso_retry))
                }
            }
            // else: discovery succeeded with no providers — this shard offers no SSO.
        }

        // ── Website hand-offs (§4.2): open the site's own pages in a Custom Tab ──
        viewModel.registerUrl?.let { url ->
            TextButton(
                onClick = { WebHandoff.open(context, url) },
                modifier = Modifier.padding(top = 16.dp),
            ) { Text(stringResource(R.string.login_register)) }
        }
        viewModel.forgotPasswordUrl?.let { url ->
            TextButton(onClick = { WebHandoff.open(context, url) }) {
                Text(stringResource(R.string.login_forgot))
            }
        }

        if (showSsoPicker) {
            SsoProviderPicker(
                providers = state.ssoProviders,
                onDismiss = { showSsoPicker = false },
                onPick = { provider ->
                    showSsoPicker = false
                    viewModel.onSsoProviderClick(provider)
                },
            )
        }
    }
}

/**
 * The native provider picker (§4.2): a bottom sheet listing the shard's enabled SSO
 * providers so a single "Sign in with SSO" button can serve several IdPs without a
 * website chooser page. Each row opens the Custom-Tab bridge for that provider.
 */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun SsoProviderPicker(
    providers: List<com.runicgateway.app.data.api.dto.SsoProviderDto>,
    onDismiss: () -> Unit,
    onPick: (com.runicgateway.app.data.api.dto.SsoProviderDto) -> Unit,
) {
    ModalBottomSheet(onDismissRequest = onDismiss, sheetState = rememberModalBottomSheetState()) {
        Text(
            text = stringResource(R.string.login_sso_pick_title),
            style = MaterialTheme.typography.titleMedium,
            modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp),
        )
        providers.forEach { provider ->
            TextButton(
                onClick = { onPick(provider) },
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(horizontal = 12.dp, vertical = 2.dp),
            ) {
                Text(
                    text = stringResource(R.string.login_sso_provider, provider.name),
                    modifier = Modifier.fillMaxWidth(),
                    textAlign = TextAlign.Start,
                )
            }
        }
        Spacer(Modifier.height(24.dp)) // clears the gesture inset at the sheet's bottom
    }
}

private fun loginErrorRes(error: LoginError): Int = when (error) {
    LoginError.INVALID_CREDENTIALS -> R.string.login_error_credentials
    LoginError.BAD_CODE -> R.string.login_error_code
    LoginError.RATE_LIMITED -> R.string.login_error_rate_limited
    LoginError.SERVER -> R.string.login_error_server
    LoginError.NETWORK -> R.string.login_error_network
    LoginError.SSO -> R.string.login_error_sso
}
