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

import android.graphics.BitmapFactory
import android.util.Base64
import androidx.annotation.StringRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
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.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.graphics.ImageBitmap
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.runicgateway.app.R
import com.runicgateway.app.core.auth.Role
import com.runicgateway.app.data.api.dto.LinkedIdentityDto
import com.runicgateway.app.data.api.dto.PlayerAccountDto
import com.runicgateway.app.ui.UiState
import com.runicgateway.app.ui.auth.AccountViewModel.Feedback
import com.runicgateway.app.ui.auth.AccountViewModel.Section
import com.runicgateway.app.ui.components.ErrorView
import com.runicgateway.app.ui.components.LoadingView
import com.runicgateway.app.ui.components.PillTone
import com.runicgateway.app.ui.components.ShardCard
import com.runicgateway.app.ui.components.StatusPill

/**
 * The signed-in account surface (PLAN.md §5, §6.3): identity + role, self-service
 * over `/auth/me/account*` (change username/password, TOTP, linked identities),
 * and the sign-out controls. Credential-provisioning flows (register / reset / SSO
 * link) stay website hand-offs (§4.2) and are not rebuilt here.
 */
@Composable
fun AccountScreen(
    username: String,
    roleLabel: String,
    onSignOut: () -> Unit,
    onSignOutEverywhere: () -> Unit,
    onOpenTrustedDevices: () -> Unit,
    onOpenRecoveryCodes: () -> Unit,
    modifier: Modifier = Modifier,
    viewModel: AccountViewModel = hiltViewModel(),
) {
    val state by viewModel.state.collectAsStateWithLifecycle()

    Column(
        modifier = modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState())
            .padding(16.dp),
    ) {
        IdentityCard(username = username, roleLabel = roleLabel)

        // One-time recovery codes surfaced right after enabling 2FA — save them now.
        state.recoveryCodesOnce?.let { codes ->
            RecoveryCodesShowOnceCard(codes, onDismiss = viewModel::dismissRecoveryCodes)
        }

        when (val account = state.account) {
            is UiState.Loading -> LoadingView(Modifier.padding(top = 32.dp))
            is UiState.Error -> ErrorView(account.kind, onRetry = viewModel::load, modifier = Modifier.padding(top = 32.dp))
            is UiState.Success -> AccountSections(account.data, state, viewModel, onOpenTrustedDevices, onOpenRecoveryCodes)
        }

        HorizontalDivider(Modifier.padding(vertical = 20.dp))

        OutlinedButton(onClick = onSignOut, modifier = Modifier.fillMaxWidth()) {
            Text(stringResource(R.string.account_sign_out))
        }
        TextButton(
            onClick = onSignOutEverywhere,
            modifier = Modifier.fillMaxWidth().padding(top = 4.dp),
        ) {
            Text(stringResource(R.string.account_sign_out_all))
        }
    }
}

@Composable
private fun IdentityCard(username: String, roleLabel: String) {
    ShardCard(Modifier.fillMaxWidth()) {
        Column(Modifier.padding(20.dp)) {
            Text(text = username, style = MaterialTheme.typography.titleLarge)
            StatusPill(
                text = roleLabel,
                tone = PillTone.Info,
                modifier = Modifier.padding(top = 8.dp),
            )
        }
    }
}

@Composable
private fun AccountSections(
    account: PlayerAccountDto,
    state: AccountViewModel.State,
    viewModel: AccountViewModel,
    onOpenTrustedDevices: () -> Unit,
    onOpenRecoveryCodes: () -> Unit,
) {
    UsernameSection(account, state, viewModel)
    PasswordSection(account, state, viewModel)
    TwoFactorSection(account, state, viewModel)
    SecuritySection(onOpenTrustedDevices, onOpenRecoveryCodes)
    IdentitiesSection(state, viewModel)
}

/**
 * Links to the dedicated trusted-device and recovery-code screens
 * (TRUSTED_DEVICES_MFA.md). Kept simple — the management UX lives on those screens.
 */
@Composable
private fun SecuritySection(onOpenTrustedDevices: () -> Unit, onOpenRecoveryCodes: () -> Unit) {
    SectionCard(R.string.account_security_title) {
        OutlinedButton(
            onClick = onOpenTrustedDevices,
            modifier = Modifier.fillMaxWidth().padding(top = 12.dp),
        ) { Text(stringResource(R.string.account_security_trusted_devices)) }
        OutlinedButton(
            onClick = onOpenRecoveryCodes,
            modifier = Modifier.fillMaxWidth().padding(top = 8.dp),
        ) { Text(stringResource(R.string.account_security_recovery_codes)) }
    }
}

@Composable
private fun SectionCard(@StringRes titleRes: Int, content: @Composable () -> Unit) {
    ShardCard(Modifier.fillMaxWidth().padding(top = 12.dp)) {
        Column(Modifier.padding(16.dp)) {
            Text(stringResource(titleRes), style = MaterialTheme.typography.titleMedium)
            content()
        }
    }
}

@Composable
private fun UsernameSection(
    account: PlayerAccountDto,
    state: AccountViewModel.State,
    viewModel: AccountViewModel,
) {
    var username by rememberSaveable(account.username) { mutableStateOf(account.username) }
    SectionCard(R.string.account_username_title) {
        OutlinedTextField(
            value = username,
            onValueChange = { username = it },
            singleLine = true,
            enabled = !state.busy,
            label = { Text(stringResource(R.string.login_username)) },
            modifier = Modifier.fillMaxWidth().padding(top = 12.dp),
        )
        Button(
            onClick = { viewModel.changeUsername(username) },
            enabled = !state.busy && username.trim() != account.username && username.trim().length >= 3,
            modifier = Modifier.padding(top = 12.dp),
        ) { Text(stringResource(R.string.account_username_action)) }
        SectionFeedback(state.feedback, Section.USERNAME)
    }
}

@Composable
private fun PasswordSection(
    account: PlayerAccountDto,
    state: AccountViewModel.State,
    viewModel: AccountViewModel,
) {
    val hasPassword = account.has_password
    var current by rememberSaveable { mutableStateOf("") }
    var next by rememberSaveable { mutableStateOf("") }
    SectionCard(if (hasPassword) R.string.account_password_title else R.string.account_password_set_title) {
        if (!hasPassword) {
            Text(
                stringResource(R.string.account_password_set_hint),
                style = MaterialTheme.typography.bodySmall,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                modifier = Modifier.padding(top = 8.dp),
            )
        }
        if (hasPassword) {
            OutlinedTextField(
                value = current,
                onValueChange = { current = it },
                singleLine = true,
                enabled = !state.busy,
                label = { Text(stringResource(R.string.account_password_current)) },
                visualTransformation = PasswordVisualTransformation(),
                keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
                modifier = Modifier.fillMaxWidth().padding(top = 12.dp),
            )
        }
        OutlinedTextField(
            value = next,
            onValueChange = { next = it },
            singleLine = true,
            enabled = !state.busy,
            label = { Text(stringResource(R.string.account_password_new)) },
            visualTransformation = PasswordVisualTransformation(),
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
            modifier = Modifier.fillMaxWidth().padding(top = 12.dp),
        )
        Button(
            onClick = {
                viewModel.changePassword(next, if (hasPassword) current else null)
                current = ""
                next = ""
            },
            enabled = !state.busy && next.length >= 8 && (!hasPassword || current.isNotBlank()),
            modifier = Modifier.padding(top = 12.dp),
        ) {
            Text(stringResource(if (hasPassword) R.string.account_password_action else R.string.account_password_set_action))
        }
        SectionFeedback(state.feedback, Section.PASSWORD)
    }
}

@Composable
private fun TwoFactorSection(
    account: PlayerAccountDto,
    state: AccountViewModel.State,
    viewModel: AccountViewModel,
) {
    var code by rememberSaveable { mutableStateOf("") }
    SectionCard(R.string.account_totp_title) {
        StatusPill(
            text = stringResource(if (account.totp_enabled) R.string.account_totp_on else R.string.account_totp_off),
            tone = if (account.totp_enabled) PillTone.Success else PillTone.Neutral,
            modifier = Modifier.padding(top = 8.dp),
        )

        when {
            // Enabled → offer disable via a current code.
            account.totp_enabled -> {
                CodeField(code, { code = it }, !state.busy)
                Button(
                    onClick = { viewModel.disableTotp(code); code = "" },
                    enabled = !state.busy && code.isNotBlank(),
                    modifier = Modifier.padding(top = 12.dp),
                ) { Text(stringResource(R.string.account_totp_disable)) }
            }
            // Mid-enrollment → show QR + confirm.
            state.totpSetup != null -> {
                Text(
                    stringResource(R.string.account_totp_scan),
                    style = MaterialTheme.typography.bodySmall,
                    color = MaterialTheme.colorScheme.onSurfaceVariant,
                    modifier = Modifier.padding(top = 12.dp),
                )
                rememberQrBitmap(state.totpSetup.qr)?.let { bmp ->
                    Image(
                        bitmap = bmp,
                        contentDescription = stringResource(R.string.account_totp_qr_desc),
                        modifier = Modifier.padding(top = 12.dp).size(180.dp),
                    )
                }
                CodeField(code, { code = it }, !state.busy)
                Row(Modifier.padding(top = 12.dp), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
                    Button(
                        onClick = { viewModel.enableTotp(code); code = "" },
                        enabled = !state.busy && code.isNotBlank(),
                    ) { Text(stringResource(R.string.account_totp_confirm)) }
                    OutlinedButton(onClick = { viewModel.cancelTotp(); code = "" }, enabled = !state.busy) {
                        Text(stringResource(R.string.account_totp_cancel))
                    }
                }
            }
            // Not enabled, not enrolling → start.
            else -> {
                Button(
                    onClick = viewModel::beginTotp,
                    enabled = !state.busy,
                    modifier = Modifier.padding(top = 12.dp),
                ) { Text(stringResource(R.string.account_totp_setup)) }
            }
        }
        SectionFeedback(state.feedback, Section.TOTP)
    }
}

@Composable
private fun IdentitiesSection(state: AccountViewModel.State, viewModel: AccountViewModel) {
    SectionCard(R.string.account_identities_title) {
        if (state.identities.isEmpty()) {
            Text(
                stringResource(R.string.account_identities_empty),
                style = MaterialTheme.typography.bodySmall,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                modifier = Modifier.padding(top = 8.dp),
            )
        } else {
            state.identities.forEach { identity -> IdentityRow(identity, state.busy, viewModel) }
        }
        SectionFeedback(state.feedback, Section.IDENTITY)
    }
}

@Composable
private fun IdentityRow(identity: LinkedIdentityDto, busy: Boolean, viewModel: AccountViewModel) {
    Row(
        Modifier.fillMaxWidth().padding(top = 12.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Column(Modifier.weight(1f)) {
            Text(
                identity.provider.replaceFirstChar { it.uppercase() },
                style = MaterialTheme.typography.bodyLarge,
            )
            identity.email?.let {
                Text(it, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant)
            }
        }
        TextButton(onClick = { viewModel.unlinkIdentity(identity.provider) }, enabled = !busy) {
            Text(stringResource(R.string.account_identity_unlink))
        }
    }
}

@Composable
private fun CodeField(code: String, onChange: (String) -> Unit, enabled: Boolean) {
    OutlinedTextField(
        value = code,
        onValueChange = { onChange(it.filter(Char::isDigit).take(8)) },
        singleLine = true,
        enabled = enabled,
        label = { Text(stringResource(R.string.login_totp_code)) },
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword),
        modifier = Modifier.fillMaxWidth().padding(top = 12.dp),
    )
}

@Composable
private fun SectionFeedback(feedback: Feedback?, section: Section) {
    if (feedback == null || feedback.section != section) return
    Text(
        text = stringResource(feedback.messageRes),
        style = MaterialTheme.typography.bodySmall,
        color = if (feedback.ok) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.error,
        modifier = Modifier.padding(top = 10.dp),
    )
}

/** Decode a `data:image/png;base64,…` URL (the TOTP QR) into an [ImageBitmap]. */
@Composable
private fun rememberQrBitmap(dataUrl: String?): ImageBitmap? = remember(dataUrl) {
    if (dataUrl.isNullOrBlank()) return@remember null
    val comma = dataUrl.indexOf(',')
    if (comma < 0) return@remember null
    runCatching {
        val bytes = Base64.decode(dataUrl.substring(comma + 1), Base64.DEFAULT)
        BitmapFactory.decodeByteArray(bytes, 0, bytes.size)?.asImageBitmap()
    }.getOrNull()
}

/** Human label for a role (advisory display only — §4.3). */
@StringRes
fun roleLabelRes(role: Role): Int = when (role) {
    Role.PLAYER -> R.string.role_player
    Role.MODERATOR -> R.string.role_moderator
    Role.EDITOR -> R.string.role_editor
    Role.ADMIN -> R.string.role_admin
    Role.UNKNOWN -> R.string.role_unknown
}
