package ru.forbion.f7cloud.mobile.ui

import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
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.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Fingerprint
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
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.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.ProcessLifecycleOwner
import ru.forbion.f7cloud.core.auth.AppLockStore
import ru.forbion.f7cloud.core.designsystem.F7Colors
import ru.forbion.f7cloud.core.designsystem.F7PrimaryButton
import ru.forbion.f7cloud.core.designsystem.F7SecureScreen
import ru.forbion.f7cloud.core.designsystem.F7TextButton
import ru.forbion.f7cloud.mobile.R

private enum class AppLockSetupStage {
    Choose,
    PinCreate,
    PinConfirm,
}

private const val PIN_LENGTH = 4

private fun biometricAuthenticators(): Int =
    BiometricManager.Authenticators.BIOMETRIC_STRONG or
        BiometricManager.Authenticators.BIOMETRIC_WEAK

private fun canUseBiometric(context: android.content.Context): Boolean =
    BiometricManager.from(context).canAuthenticate(biometricAuthenticators()) ==
        BiometricManager.BIOMETRIC_SUCCESS

@Composable
fun AppLockGate(
    lockStore: AppLockStore,
    unlockNonce: Int = 0,
    content: @Composable () -> Unit,
) {
    if (!lockStore.isEnabled()) {
        content()
        return
    }
    var unlocked by remember { mutableStateOf(false) }
    var lockSession by remember { mutableIntStateOf(0) }

    LaunchedEffect(Unit) {
        if (lockStore.consumeColdStart()) {
            unlocked = false
            lockSession++
        }
    }

    val processLifecycle = ProcessLifecycleOwner.get()
    DisposableEffect(processLifecycle, lockStore) {
        val observer = LifecycleEventObserver { _, event ->
            when (event) {
                Lifecycle.Event.ON_STOP -> lockStore.markBackgrounded()
                Lifecycle.Event.ON_START -> {
                    when {
                        lockStore.consumeColdStart() -> {
                            unlocked = false
                            lockSession++
                        }
                        lockStore.shouldRequireUnlock() -> {
                            unlocked = false
                            lockSession++
                        }
                    }
                }
                else -> Unit
            }
        }
        processLifecycle.lifecycle.addObserver(observer)
        onDispose { processLifecycle.lifecycle.removeObserver(observer) }
    }

    LaunchedEffect(unlockNonce) {
        if (unlockNonce > 0) {
            unlocked = true
            lockStore.clearBackgroundMarker()
        }
    }

    if (unlocked) {
        content()
        return
    }
    AppLockUnlockScreen(
        lockStore = lockStore,
        lockSession = lockSession,
        onUnlocked = {
            lockStore.clearBackgroundMarker()
            unlocked = true
        },
    )
}

@Composable
fun AppLockSetupDialog(
    visible: Boolean,
    lockStore: AppLockStore,
    onDismiss: () -> Unit,
    onLockConfigured: () -> Unit = {},
) {
    if (!visible) return
    F7SecureScreen() // создание/подтверждение PIN — не в скриншотах/recents

    val context = LocalContext.current
    val activity = context.findFragmentActivity()
    var stage by remember { mutableStateOf(AppLockSetupStage.Choose) }
    var firstPin by remember { mutableStateOf("") }
    var currentPin by remember { mutableStateOf("") }
    var error by remember { mutableStateOf<String?>(null) }

    val biometricAvailable = remember { canUseBiometric(context) }

    fun finishSkip() {
        lockStore.markSetupOffered()
        onDismiss()
    }

    fun finishPinSetup(pin: String) {
        lockStore.enable(pin, biometric = false)
        lockStore.markSetupOffered()
        onLockConfigured()
        onDismiss()
    }

    fun finishBiometricSetup() {
        lockStore.enableBiometricOnly()
        lockStore.markSetupOffered()
        onLockConfigured()
        onDismiss()
    }

    fun launchBiometricSetup() {
        val host = activity ?: run {
            error = "Биометрия недоступна на этом устройстве"
            return
        }
        val executor = ContextCompat.getMainExecutor(context)
        val prompt = BiometricPrompt(
            host,
            executor,
            object : BiometricPrompt.AuthenticationCallback() {
                override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                    finishBiometricSetup()
                }

                override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
                    if (errorCode != BiometricPrompt.ERROR_USER_CANCELED &&
                        errorCode != BiometricPrompt.ERROR_NEGATIVE_BUTTON
                    ) {
                        error = errString.toString()
                    }
                }
            },
        )
        prompt.authenticate(
            BiometricPrompt.PromptInfo.Builder()
                .setTitle("Биометрия")
                .setSubtitle("Подтвердите отпечаток пальца для защиты приложения")
                .setNegativeButtonText("Отмена")
                .setAllowedAuthenticators(biometricAuthenticators())
                .build(),
        )
    }

    fun appendDigit(digit: String) {
        if (currentPin.length >= PIN_LENGTH) return
        currentPin += digit
        error = null
        if (currentPin.length == PIN_LENGTH) {
            when (stage) {
                AppLockSetupStage.PinCreate -> {
                    firstPin = currentPin
                    currentPin = ""
                    stage = AppLockSetupStage.PinConfirm
                }
                AppLockSetupStage.PinConfirm -> {
                    if (currentPin == firstPin) {
                        finishPinSetup(currentPin)
                    } else {
                        error = "PIN-коды не совпадают"
                        currentPin = ""
                        firstPin = ""
                        stage = AppLockSetupStage.PinCreate
                    }
                }
                else -> Unit
            }
        }
    }

    fun removeDigit() {
        if (currentPin.isNotEmpty()) {
            currentPin = currentPin.dropLast(1)
            error = null
        }
    }

    Dialog(
        onDismissRequest = {},
        properties = DialogProperties(
            dismissOnBackPress = false,
            dismissOnClickOutside = false,
            usePlatformDefaultWidth = false,
        ),
    ) {
        Surface(
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = 20.dp),
            shape = RoundedCornerShape(16.dp),
            color = Color.White,
        ) {
            when (stage) {
                AppLockSetupStage.Choose -> AppLockSetupChoiceContent(
                    canUseBiometric = biometricAvailable,
                    error = error,
                    onChoosePin = {
                        error = null
                        currentPin = ""
                        firstPin = ""
                        stage = AppLockSetupStage.PinCreate
                    },
                    onChooseBiometric = {
                        error = null
                        launchBiometricSetup()
                    },
                    onSkip = ::finishSkip,
                )
                AppLockSetupStage.PinCreate,
                AppLockSetupStage.PinConfirm,
                -> AppLockPinKeypadContent(
                    title = if (stage == AppLockSetupStage.PinCreate) {
                        "Придумайте PIN-код"
                    } else {
                        "Повторите PIN-код"
                    },
                    subtitle = if (stage == AppLockSetupStage.PinCreate) {
                        "Введите $PIN_LENGTH цифры на клавиатуре ниже"
                    } else {
                        "Подтвердите PIN-код ещё раз"
                    },
                    pinLength = currentPin.length,
                    maxPinLength = PIN_LENGTH,
                    error = error,
                    onDigit = ::appendDigit,
                    onBackspace = ::removeDigit,
                    onBack = {
                        error = null
                        currentPin = ""
                        firstPin = ""
                        stage = AppLockSetupStage.Choose
                    },
                )
            }
        }
    }
}

@Composable
private fun AppLockSetupChoiceContent(
    canUseBiometric: Boolean,
    error: String?,
    onChoosePin: () -> Unit,
    onChooseBiometric: () -> Unit,
    onSkip: () -> Unit,
) {
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(horizontal = 24.dp, vertical = 28.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.spacedBy(12.dp),
    ) {
        Text(
            text = "Защита приложения",
            style = MaterialTheme.typography.titleLarge,
            fontWeight = FontWeight.SemiBold,
            color = F7Colors.TextPrimary,
            textAlign = TextAlign.Center,
        )
        Text(
            text = "Вы хотите использовать PIN-код или отпечаток пальца?",
            style = MaterialTheme.typography.bodyLarge,
            color = F7Colors.TextSecondary,
            textAlign = TextAlign.Center,
            modifier = Modifier.padding(bottom = 8.dp),
        )
        if (!error.isNullOrBlank()) {
            Text(
                text = error,
                color = F7Colors.Error,
                style = MaterialTheme.typography.bodyMedium,
                textAlign = TextAlign.Center,
            )
        }
        F7PrimaryButton(
            text = "PIN-код",
            onClick = onChoosePin,
            modifier = Modifier.fillMaxWidth(),
        )
        if (canUseBiometric) {
            F7PrimaryButton(
                text = "Отпечаток пальца",
                onClick = onChooseBiometric,
                modifier = Modifier.fillMaxWidth(),
            )
        }
        Spacer(modifier = Modifier.height(4.dp))
        F7TextButton(
            text = "Пропустить",
            onClick = onSkip,
            modifier = Modifier.fillMaxWidth(),
        )
    }
}

@Composable
private fun AppLockPinKeypadContent(
    title: String,
    subtitle: String,
    pinLength: Int,
    maxPinLength: Int,
    error: String?,
    onDigit: (String) -> Unit,
    onBackspace: () -> Unit,
    onBack: (() -> Unit)? = null,
    extraAction: (@Composable () -> Unit)? = null,
    header: (@Composable () -> Unit)? = null,
) {
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(horizontal = 20.dp, vertical = 24.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        if (onBack != null) {
            F7TextButton(
                text = "← Назад",
                onClick = onBack,
                modifier = Modifier.align(Alignment.Start),
            )
        }
        header?.invoke()
        Text(
            text = title,
            style = MaterialTheme.typography.titleMedium,
            fontWeight = FontWeight.SemiBold,
            color = F7Colors.TextPrimary,
            textAlign = TextAlign.Center,
            modifier = Modifier.padding(top = 4.dp),
        )
        Text(
            text = subtitle,
            style = MaterialTheme.typography.bodyMedium,
            color = F7Colors.TextSecondary,
            textAlign = TextAlign.Center,
            modifier = Modifier.padding(top = 8.dp, bottom = 20.dp),
        )
        PinDots(
            filledCount = pinLength,
            totalCount = maxPinLength,
        )
        if (!error.isNullOrBlank()) {
            Text(
                text = error,
                color = F7Colors.Error,
                style = MaterialTheme.typography.bodyMedium,
                textAlign = TextAlign.Center,
                modifier = Modifier.padding(top = 12.dp),
            )
        }
        Spacer(modifier = Modifier.height(20.dp))
        PinNumericKeypad(
            onDigit = onDigit,
            onBackspace = onBackspace,
        )
        extraAction?.invoke()
    }
}

@Composable
private fun PinDots(filledCount: Int, totalCount: Int) {
    Row(
        horizontalArrangement = Arrangement.spacedBy(14.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        repeat(totalCount) { index ->
            Box(
                modifier = Modifier
                    .size(14.dp)
                    .clip(CircleShape)
                    .background(
                        if (index < filledCount) {
                            F7Colors.Primary
                        } else {
                            Color(0xFFE6E6E6)
                        },
                    ),
            )
        }
    }
}

@Composable
private fun PinNumericKeypad(
    onDigit: (String) -> Unit,
    onBackspace: () -> Unit,
) {
    val rows = listOf(
        listOf("1", "2", "3"),
        listOf("4", "5", "6"),
        listOf("7", "8", "9"),
        listOf("", "0", "⌫"),
    )
    Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
        rows.forEach { row ->
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceEvenly,
            ) {
                row.forEach { key ->
                    when (key) {
                        "" -> Spacer(modifier = Modifier.size(72.dp))
                        "⌫" -> PinKey(
                            label = "⌫",
                            onClick = onBackspace,
                        )
                        else -> PinKey(
                            label = key,
                            onClick = { onDigit(key) },
                        )
                    }
                }
            }
        }
    }
}

@Composable
private fun PinKey(
    label: String,
    onClick: () -> Unit,
) {
    Box(
        modifier = Modifier
            .size(72.dp)
            .clip(CircleShape)
            .background(Color(0xFFF3F3F3))
            .clickable(onClick = onClick),
        contentAlignment = Alignment.Center,
    ) {
        Text(
            text = label,
            fontSize = if (label == "⌫") 24.sp else 28.sp,
            fontWeight = FontWeight.Medium,
            color = F7Colors.TextPrimary,
        )
    }
}

@Composable
private fun AppLockBrandedBackdrop(
    modifier: Modifier = Modifier,
    content: @Composable ColumnScope.() -> Unit = {},
) {
    Column(
        modifier = modifier
            .fillMaxSize()
            .background(Color.White)
            .padding(horizontal = 32.dp, vertical = 48.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
    ) {
        Image(
            painter = painterResource(R.drawable.f7_app_lock_logo),
            contentDescription = "F7cloud",
            modifier = Modifier
                .fillMaxWidth(0.88f)
                .padding(bottom = 28.dp),
            contentScale = ContentScale.Fit,
        )
        content()
    }
}

@Composable
private fun AppLockUnlockScreen(
    lockStore: AppLockStore,
    lockSession: Int,
    onUnlocked: () -> Unit,
) {
    F7SecureScreen() // экран разблокировки (PIN) — не в скриншотах/recents
    val context = LocalContext.current
    val activity = context.findFragmentActivity()
    var pin by remember { mutableStateOf("") }
    var error by remember { mutableStateOf<String?>(null) }
    var showPinEntry by remember(lockSession) { mutableStateOf(!lockStore.useBiometric()) }
    val biometricOnly = lockStore.isBiometricOnly()
    val biometricAvailable = remember { canUseBiometric(context) }

    fun verifyPinInput() {
        if (lockStore.verifyPin(pin)) {
            onUnlocked()
        } else {
            error = "Неверный PIN"
            pin = ""
        }
    }

    fun appendDigit(digit: String) {
        if (pin.length >= PIN_LENGTH) return
        pin += digit
        error = null
        if (pin.length == PIN_LENGTH) {
            verifyPinInput()
        }
    }

    fun removeDigit() {
        if (pin.isNotEmpty()) {
            pin = pin.dropLast(1)
            error = null
        }
    }

    fun launchBiometricUnlock() {
        val host = activity ?: run {
            error = "Биометрия недоступна"
            return
        }
        val executor = ContextCompat.getMainExecutor(context)
        val prompt = BiometricPrompt(
            host,
            executor,
            object : BiometricPrompt.AuthenticationCallback() {
                override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                    onUnlocked()
                }

                override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
                    when (errorCode) {
                        BiometricPrompt.ERROR_NEGATIVE_BUTTON -> {
                            if (!biometricOnly) {
                                showPinEntry = true
                            }
                        }
                        BiometricPrompt.ERROR_USER_CANCELED -> Unit
                        else -> error = errString.toString()
                    }
                }
            },
        )
        prompt.authenticate(
            BiometricPrompt.PromptInfo.Builder()
                .setTitle("Разблокировка F7cloud")
                .setSubtitle("Прикоснитесь к сканеру отпечатка")
                .setAllowedAuthenticators(biometricAuthenticators())
                .setNegativeButtonText(if (biometricOnly) "Отмена" else "Ввести PIN")
                .build(),
        )
    }

    LaunchedEffect(lockSession, lockStore.useBiometric(), activity) {
        if (lockStore.useBiometric() && biometricAvailable && activity != null && !showPinEntry) {
            launchBiometricUnlock()
        }
    }

    if (showPinEntry && !biometricOnly) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .background(Color.White),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center,
        ) {
            AppLockPinKeypadContent(
                title = "Введите PIN-код",
                subtitle = "Для доступа к приложению",
                pinLength = pin.length,
                maxPinLength = PIN_LENGTH,
                error = error,
                onDigit = ::appendDigit,
                onBackspace = ::removeDigit,
                onBack = if (lockStore.useBiometric() && biometricAvailable) {
                    {
                        error = null
                        pin = ""
                        showPinEntry = false
                    }
                } else {
                    null
                },
                header = {
                    Image(
                        painter = painterResource(R.drawable.f7_app_lock_logo),
                        contentDescription = "F7cloud",
                        modifier = Modifier
                            .fillMaxWidth(0.75f)
                            .padding(bottom = 16.dp),
                        contentScale = ContentScale.Fit,
                    )
                },
            )
        }
        return
    }

    AppLockBrandedBackdrop {
        if (lockStore.useBiometric() && biometricAvailable) {
            Icon(
                imageVector = Icons.Filled.Fingerprint,
                contentDescription = null,
                tint = F7Colors.Primary,
                modifier = Modifier.size(56.dp),
            )
            Text(
                text = "Прикоснитесь к сканеру отпечатка",
                style = MaterialTheme.typography.bodyLarge,
                color = F7Colors.TextSecondary,
                textAlign = TextAlign.Center,
                modifier = Modifier.padding(top = 16.dp),
            )
            if (!error.isNullOrBlank()) {
                Text(
                    text = error ?: "",
                    color = F7Colors.Error,
                    style = MaterialTheme.typography.bodyMedium,
                    textAlign = TextAlign.Center,
                    modifier = Modifier.padding(top = 12.dp),
                )
            }
            F7PrimaryButton(
                text = "Сканер отпечатка",
                onClick = ::launchBiometricUnlock,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 24.dp),
            )
        }
        if (!biometricOnly && lockStore.useBiometric() && biometricAvailable) {
            F7TextButton(
                text = "Ввести PIN",
                onClick = { showPinEntry = true },
                modifier = Modifier.padding(top = 12.dp),
            )
        }
    }
}

private fun android.content.Context.findFragmentActivity(): FragmentActivity? {
    var ctx: android.content.Context = this
    while (true) {
        when (ctx) {
            is FragmentActivity -> return ctx
            is android.content.ContextWrapper -> ctx = ctx.baseContext
            else -> return null
        }
    }
}
