package wang.yaojia.webterm.screens

import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
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.text.KeyboardOptions
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
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.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
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.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import wang.yaojia.webterm.designsystem.Spacing
import wang.yaojia.webterm.designsystem.WebTermColors
import wang.yaojia.webterm.designsystem.WebTermTheme
import wang.yaojia.webterm.designsystem.WebTermType
import wang.yaojia.webterm.viewmodels.CertError
import wang.yaojia.webterm.viewmodels.CertPhase
import wang.yaojia.webterm.viewmodels.CertSummaryView
import wang.yaojia.webterm.viewmodels.ClientCertUiState
import wang.yaojia.webterm.viewmodels.ClientCertViewModel

/**
 * # ClientCertScreen (A27) — device-certificate (mTLS) management.
 *
 * The Compose shell over [ClientCertViewModel], reached from the session-list host menu "设备证书"
 * (A20). It lets the user **import** a `.p12` device certificate (SAF `ACTION_OPEN_DOCUMENT` + a
 * passphrase field), **rotate** it, or **remove** it, and shows the installed identity's summary
 * (subject-CN / issuer-CN / expiry, with an **EXPIRED** warning). A **distinct screen**, not folded
 * into pairing (plan §1/§5 A27).
 *
 * ### Secrets & trust discipline (plan §8)
 *  - The passphrase is entered in a masked field, handed straight to the ViewModel with the file bytes,
 *    and cleared from UI state the instant the import/rotation is dispatched — it is never logged,
 *    persisted, or echoed. The private key lands non-exportably in AndroidKeyStore inside the
 *    repository (the "one key home"); this screen never sees it.
 *  - A bad passphrase can NOT clobber a previously-installed cert: the import validates before
 *    persisting, so a failure leaves the prior summary on screen and only surfaces an inert error.
 *  - Every cert-derived string (CNs, expiry) is inert [Text] — no autolink / markdown / `AnnotatedString`.
 *    Error copy is app-authored, never a server/exception string.
 *
 * The SAF picker + AndroidKeyStore/Tink I/O it drives are device-QA (plan §7); the state machine is
 * JVM-tested in `ClientCertViewModelTest`.
 *
 * @param viewModel the device-cert presenter (the nav layer builds it over the shared `IdentityRepository`).
 * @param onBack optional up-navigation back to the host menu.
 */
@Composable
public fun ClientCertScreen(
    viewModel: ClientCertViewModel,
    modifier: Modifier = Modifier,
    onBack: (() -> Unit)? = null,
) {
    val state by viewModel.uiState.collectAsStateWithLifecycle()
    // Bind to the LaunchedEffect scope (cancelled when the screen leaves composition), then load.
    LaunchedEffect(viewModel) { viewModel.bind(this) }

    val context = LocalContext.current
    val scope = rememberCoroutineScope()
    var passphrase by remember { mutableStateOf("") }
    // Which action the pending SAF pick feeds: false = first import, true = rotate an existing cert.
    var pendingRotate by remember { mutableStateOf(false) }

    val picker = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
        if (uri == null) return@rememberLauncherForActivityResult
        // Capture then immediately scrub the secret from UI state — the VM now owns it for the handoff.
        val secret = passphrase
        val rotate = pendingRotate
        passphrase = ""
        scope.launch {
            val bytes = withContext(Dispatchers.IO) {
                runCatching { context.contentResolver.openInputStream(uri)?.use { it.readBytes() } }.getOrNull()
            } ?: ByteArray(0) // an unreadable/empty pick decode-fails → surfaces as an INVALID_FILE error
            if (rotate) viewModel.rotate(bytes, secret) else viewModel.import(bytes, secret)
        }
    }

    if (state.confirmingRemove) {
        RemoveConfirmDialog(onConfirm = viewModel::confirmRemove, onDismiss = viewModel::cancelRemove)
    }

    ClientCertScreen(
        state = state,
        passphrase = passphrase,
        onPassphraseChange = { passphrase = it },
        onImport = { pendingRotate = false; picker.launch(P12_MIME_TYPES) },
        onRotate = { pendingRotate = true; picker.launch(P12_MIME_TYPES) },
        onRemove = viewModel::requestRemove,
        onDismissError = viewModel::clearError,
        modifier = modifier,
        onBack = onBack,
    )
}

/** Stateless body — pure inputs so a [Preview] and any host can drive it without the SAF/VM machinery. */
@Composable
public fun ClientCertScreen(
    state: ClientCertUiState,
    passphrase: String,
    onPassphraseChange: (String) -> Unit,
    onImport: () -> Unit,
    onRotate: () -> Unit,
    onRemove: () -> Unit,
    onDismissError: () -> Unit,
    modifier: Modifier = Modifier,
    onBack: (() -> Unit)? = null,
) {
    Surface(modifier = modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(Spacing.lg16),
            verticalArrangement = Arrangement.spacedBy(Spacing.md12),
        ) {
            Header(onBack = onBack)

            when (state.phase) {
                CertPhase.LOADING -> LoadingRow()
                CertPhase.READY, CertPhase.BUSY -> {
                    val busy = state.phase == CertPhase.BUSY
                    val summary = state.summary
                    if (summary != null) {
                        InstalledCertCard(summary)
                    } else {
                        Text(
                            text = "尚未安装设备证书。隧道主机（mTLS）需要先在此导入 .p12 证书。",
                            style = MaterialTheme.typography.bodyMedium,
                            color = MaterialTheme.colorScheme.onSurfaceVariant,
                        )
                    }

                    state.error?.let { ErrorCard(error = it, onDismiss = onDismissError) }

                    PassphraseField(
                        passphrase = passphrase,
                        onPassphraseChange = onPassphraseChange,
                        enabled = !busy,
                    )
                    CertActions(
                        hasCert = summary != null,
                        enabled = !busy,
                        onImport = onImport,
                        onRotate = onRotate,
                        onRemove = onRemove,
                    )
                    if (busy) LoadingRow()
                }
            }
        }
    }
}

@Composable
private fun Header(onBack: (() -> Unit)?) {
    Row(
        modifier = Modifier.fillMaxWidth(),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(Spacing.sm8),
    ) {
        if (onBack != null) {
            TextButton(onClick = onBack) { Text("返回") }
        }
        Text(text = "设备证书", style = MaterialTheme.typography.headlineSmall)
    }
}

/** The installed identity summary: subject/issuer CN, expiry, and a prominent EXPIRED warning. */
@Composable
private fun InstalledCertCard(summary: CertSummaryView) {
    Card(
        modifier = Modifier.fillMaxWidth(),
        colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant),
    ) {
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .padding(Spacing.lg16),
            verticalArrangement = Arrangement.spacedBy(Spacing.sm8),
        ) {
            SummaryRow(label = "设备（CN）", value = summary.subjectCommonName)
            SummaryRow(label = "签发方（CN）", value = summary.issuerCommonName)
            SummaryRow(label = "到期", value = summary.expiry)
            if (summary.isExpired) {
                HorizontalDivider(color = MaterialTheme.colorScheme.outline)
                Text(
                    text = "⚠ 证书已过期，无法用于连接。请更换一份有效证书。",
                    style = MaterialTheme.typography.bodyMedium,
                    color = WebTermColors.statusStuck,
                )
            }
        }
    }
}

@Composable
private fun SummaryRow(label: String, value: String) {
    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.spacedBy(Spacing.md12),
    ) {
        Text(
            text = label,
            style = MaterialTheme.typography.labelMedium,
            color = MaterialTheme.colorScheme.onSurfaceVariant,
            modifier = Modifier.weight(1f),
        )
        // Cert-derived value: inert monospaced Text — no linkify/markdown (plan §8).
        Text(text = value, style = WebTermType.monoTabular(13), color = MaterialTheme.colorScheme.onSurface)
    }
}

@Composable
private fun ErrorCard(error: CertError, onDismiss: () -> Unit) {
    Card(
        modifier = Modifier.fillMaxWidth(),
        colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.errorContainer),
    ) {
        Column(
            modifier = Modifier
                .fillMaxWidth()
                .padding(Spacing.md12),
            verticalArrangement = Arrangement.spacedBy(Spacing.xs4),
        ) {
            Text(
                text = errorCopy(error),
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.onErrorContainer,
            )
            TextButton(onClick = onDismiss, modifier = Modifier.align(Alignment.End)) { Text("知道了") }
        }
    }
}

@Composable
private fun PassphraseField(passphrase: String, onPassphraseChange: (String) -> Unit, enabled: Boolean) {
    OutlinedTextField(
        value = passphrase,
        onValueChange = onPassphraseChange,
        label = { Text("证书口令（.p12 密码）") },
        singleLine = true,
        enabled = enabled,
        visualTransformation = PasswordVisualTransformation(),
        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
        modifier = Modifier.fillMaxWidth(),
    )
}

@Composable
private fun CertActions(
    hasCert: Boolean,
    enabled: Boolean,
    onImport: () -> Unit,
    onRotate: () -> Unit,
    onRemove: () -> Unit,
) {
    if (hasCert) {
        Button(onClick = onRotate, enabled = enabled, modifier = Modifier.fillMaxWidth()) {
            Text("更换证书…")
        }
        OutlinedButton(onClick = onRemove, enabled = enabled, modifier = Modifier.fillMaxWidth()) {
            Text("移除证书")
        }
    } else {
        Button(onClick = onImport, enabled = enabled, modifier = Modifier.fillMaxWidth()) {
            Text("导入设备证书…")
        }
    }
}

@Composable
private fun RemoveConfirmDialog(onConfirm: () -> Unit, onDismiss: () -> Unit) {
    AlertDialog(
        onDismissRequest = onDismiss,
        title = { Text("移除设备证书？") },
        text = { Text("移除后，需要 mTLS 的隧道主机将无法连接，直到你重新导入证书。") },
        confirmButton = { TextButton(onClick = onConfirm) { Text("移除") } },
        dismissButton = { TextButton(onClick = onDismiss) { Text("取消") } },
    )
}

@Composable
private fun LoadingRow() {
    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.Center,
    ) {
        CircularProgressIndicator()
    }
}

/** Maps the coarse [CertError] to inert, app-authored copy (never an exception/server string, §8). */
private fun errorCopy(error: CertError): String = when (error) {
    CertError.BAD_PASSPHRASE -> "证书口令不正确。原有证书保持不变，请检查口令后重试。"
    CertError.INVALID_FILE -> "无法读取该文件：它不是有效的 .p12 证书。"
    CertError.NO_IDENTITY -> "该 .p12 里没有可用的私钥（可能只含证书）。请选择包含私钥的证书文件。"
    CertError.IMPORT_FAILED -> "导入证书失败。请重试。"
    CertError.REMOVE_FAILED -> "移除证书失败。请重试。"
}

/** SAF filter for PKCS#12 material; some providers tag `.p12`/`.pfx` as octet-stream, so include it. */
private val P12_MIME_TYPES = arrayOf(
    "application/x-pkcs12",
    "application/pkcs12",
    "application/octet-stream",
)

@Preview(name = "ClientCertScreen — installed")
@Composable
private fun ClientCertScreenInstalledPreview() {
    WebTermTheme {
        ClientCertScreen(
            state = ClientCertUiState(
                summary = CertSummaryView(
                    subjectCommonName = "t1-android",
                    issuerCommonName = "webterm-device-ca",
                    expiry = "2027年6月15日",
                    isExpired = false,
                ),
                phase = CertPhase.READY,
            ),
            passphrase = "",
            onPassphraseChange = {},
            onImport = {},
            onRotate = {},
            onRemove = {},
            onDismissError = {},
        )
    }
}

@Preview(name = "ClientCertScreen — expired + none")
@Composable
private fun ClientCertScreenExpiredPreview() {
    WebTermTheme {
        ClientCertScreen(
            state = ClientCertUiState(
                summary = CertSummaryView("t1-android", "webterm-device-ca", "2020年1月1日", isExpired = true),
                phase = CertPhase.READY,
                error = CertError.BAD_PASSPHRASE,
            ),
            passphrase = "",
            onPassphraseChange = {},
            onImport = {},
            onRotate = {},
            onRemove = {},
            onDismissError = {},
        )
    }
}
