package de.softwareapp_hb.privateqrscanner.ui.screens

import android.Manifest
import android.app.Activity
import android.content.Intent
import android.media.AudioManager
import android.media.ToneGenerator
import android.net.Uri
import android.provider.Settings
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
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.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.OpenInNew
import androidx.compose.material.icons.automirrored.filled.ViewList
import androidx.compose.material.icons.filled.ContentCopy
import androidx.compose.material.icons.filled.FlashOff
import androidx.compose.material.icons.filled.FlashOn
import androidx.compose.material.icons.filled.PersonAdd
import androidx.compose.material.icons.filled.Share
import androidx.compose.material.icons.filled.UploadFile
import androidx.compose.material.icons.filled.ViewModule
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
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.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import de.softwareapp_hb.privateqrscanner.R
import de.softwareapp_hb.privateqrscanner.data.scanner.DetectionBox
import de.softwareapp_hb.privateqrscanner.data.scanner.DetectionPoint
import de.softwareapp_hb.privateqrscanner.domain.ScanResult
import de.softwareapp_hb.privateqrscanner.ui.BatchScanRecord
import de.softwareapp_hb.privateqrscanner.ui.EventTicketScanDecision
import de.softwareapp_hb.privateqrscanner.ui.UseCaseView
import de.softwareapp_hb.privateqrscanner.ui.components.CameraPreview
import de.softwareapp_hb.privateqrscanner.ui.capabilities
import de.softwareapp_hb.privateqrscanner.ui.theme.PrivateQrColors
import de.softwareapp_hb.privateqrscanner.util.ClipboardUtil
import de.softwareapp_hb.privateqrscanner.util.Intents
import de.softwareapp_hb.privateqrscanner.util.ScanContentParsers
import de.softwareapp_hb.privateqrscanner.util.UrlRiskScorer
import de.softwareapp_hb.privateqrscanner.util.readableBarcodePayload
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.barcode.BarcodeScannerOptions
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.common.InputImage
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.math.max

internal data class GalleryScanCandidate(
    val result: ScanResult,
    val box: DetectionBox?
)

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ScannerScreen(
    analysisEnabled: Boolean,
    lastResult: ScanResult?,
    batchMode: Boolean,
    batchResults: List<BatchScanRecord>,
    eventTicketWhitelistCount: Int,
    duplicateFeedbackNonce: Int,
    scanFeedbackNonce: Int,
    warningsEnabled: Boolean,
    scanFeedbackEnabled: Boolean,
    useCaseView: UseCaseView,
    onScan: (ScanResult) -> Unit,
    onEvaluateEventTicketScan: (ScanResult) -> EventTicketScanDecision,
    onAuditDuplicateTicket: (ScanResult) -> Unit,
    onAuditUnregisteredTicket: (ScanResult) -> Unit,
    onReplaceEventTicketWhitelist: (Set<String>) -> Unit,
    onScanAgain: () -> Unit,
    onBatchModeChange: (Boolean) -> Unit,
    onClearBatchResults: () -> Unit,
    onOpenHistory: () -> Unit
) {
    val context = LocalContext.current
    val haptic = LocalHapticFeedback.current
    val capabilities = remember(useCaseView) { useCaseView.capabilities() }
    val forceBatchMode = useCaseView == UseCaseView.EventTicketing
    val showBatchModeToggle = capabilities.allowBatchMode && !forceBatchMode
    val isBatchModeActive = forceBatchMode || batchMode
    val duplicateSnackbarHostState = remember { SnackbarHostState() }
    val toneGenerator = remember { ToneGenerator(AudioManager.STREAM_NOTIFICATION, 70) }

    LaunchedEffect(forceBatchMode, showBatchModeToggle, batchMode) {
        when {
            forceBatchMode && !batchMode -> onBatchModeChange(true)
            !forceBatchMode && !showBatchModeToggle && batchMode -> onBatchModeChange(false)
        }
    }

    var cameraGranted by remember {
        mutableStateOf(
            ContextCompat.checkSelfPermission(
                context,
                Manifest.permission.CAMERA
            ) == android.content.pm.PackageManager.PERMISSION_GRANTED
        )
    }
    var showSettingsHint by remember { mutableStateOf(false) }
    var torchEnabled by remember { mutableStateOf(false) }
    var torchAvailable by remember { mutableStateOf(false) }
    var showRiskWarning by remember { mutableStateOf(false) }
    var pendingOpenUrl by remember { mutableStateOf<String?>(null) }
    var showDuplicateTicketAlert by remember { mutableStateOf(false) }
    var showUnregisteredTicketAlert by remember { mutableStateOf(false) }
    var duplicateTicketAlertContent by remember { mutableStateOf<String?>(null) }
    var unregisteredTicketAlertContent by remember { mutableStateOf<String?>(null) }
    var showImageScanFailed by remember { mutableStateOf(false) }
    var imageScanCandidates by remember { mutableStateOf<List<GalleryScanCandidate>>(emptyList()) }
    var imageScanPreviewUri by remember { mutableStateOf<Uri?>(null) }
    var hasPotentialInView by remember { mutableStateOf(false) }
    var hasReadableInView by remember { mutableStateOf(false) }
    var detectionBoxes by remember { mutableStateOf<List<DetectionBox>>(emptyList()) }
    var detectionSourceWidth by remember { mutableIntStateOf(0) }
    var detectionSourceHeight by remember { mutableIntStateOf(0) }
    var lastHandledScanFeedbackNonce by remember { mutableIntStateOf(scanFeedbackNonce) }
    val scope = rememberCoroutineScope()
    val activity = context as? Activity
    val imageScanner = remember {
        BarcodeScanning.getClient(
            BarcodeScannerOptions.Builder()
                .setBarcodeFormats(Barcode.FORMAT_ALL_FORMATS)
                .enableAllPotentialBarcodes()
                .build()
        )
    }
    fun ScanResult.visibleAlertContent(): String {
        return if (isBase64Encoded) {
            context.getString(R.string.base64_encoded_inline, content)
        } else {
            content
        }
    }

    fun requestOpenUrl(url: String) {
        val risk = UrlRiskScorer.score(url)
        val risky = warningsEnabled && risk.score >= 3
        if (risky) {
            pendingOpenUrl = url
            showRiskWarning = true
        } else {
            Intents.openUrl(context, url)
        }
    }

    val permissionLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.RequestPermission()
    ) { granted ->
        cameraGranted = granted
        if (!granted && activity != null) {
            showSettingsHint = !ActivityCompat.shouldShowRequestPermissionRationale(
                activity,
                Manifest.permission.CAMERA
            )
        }
    }

    val imagePicker = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.GetContent()
    ) { uri ->
        if (uri == null) return@rememberLauncherForActivityResult
        val image = try {
            InputImage.fromFilePath(context, uri)
        } catch (_: Exception) {
            showImageScanFailed = true
            return@rememberLauncherForActivityResult
        }
        imageScanPreviewUri = uri
        imageScanCandidates = emptyList()

        imageScanner.process(image)
            .addOnSuccessListener { barcodes ->
                val candidates = barcodes.mapNotNull { barcode ->
                    val payload = barcode.readableBarcodePayload() ?: return@mapNotNull null
                    val normalizedBox = barcode.boundingBox?.let { bounds ->
                        val corners = barcode.cornerPoints?.map { p ->
                            DetectionPoint(
                                x = (p.x / image.width.toFloat()).coerceIn(0f, 1f),
                                y = (p.y / image.height.toFloat()).coerceIn(0f, 1f)
                            )
                        } ?: emptyList()
                        DetectionBox(
                            left = (bounds.left / image.width.toFloat()).coerceIn(0f, 1f),
                            top = (bounds.top / image.height.toFloat()).coerceIn(0f, 1f),
                            right = (bounds.right / image.width.toFloat()).coerceIn(0f, 1f),
                            bottom = (bounds.bottom / image.height.toFloat()).coerceIn(0f, 1f),
                            corners = corners
                        )
                    }
                    GalleryScanCandidate(
                        result = ScanResult(
                            content = payload.content,
                            type = barcode.valueType.toHumanType(),
                            isBase64Encoded = payload.isBase64Encoded
                        ),
                        box = normalizedBox
                    )
                }.distinctBy { "${it.result.displayType}|${it.result.content}" }
                imageScanCandidates = candidates
            }
            .addOnFailureListener {
                showImageScanFailed = true
            }
    }
    val whitelistPicker = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.GetContent()
    ) { uri ->
        if (uri == null) return@rememberLauncherForActivityResult
        scope.launch {
            val ids = withContext(Dispatchers.IO) {
                runCatching {
                    context.contentResolver.openInputStream(uri)?.bufferedReader()?.use { reader ->
                        parseWhitelistIds(reader.readText())
                    } ?: emptySet()
                }.getOrElse { emptySet() }
            }
            if (ids.isEmpty()) {
                Toast.makeText(
                    context,
                    context.getString(R.string.whitelist_import_empty),
                    Toast.LENGTH_SHORT
                ).show()
            } else {
                onReplaceEventTicketWhitelist(ids)
                Toast.makeText(
                    context,
                    context.getString(R.string.whitelist_import_success, ids.size),
                    Toast.LENGTH_SHORT
                ).show()
            }
        }
    }

    LaunchedEffect(Unit) {
        if (!cameraGranted) permissionLauncher.launch(Manifest.permission.CAMERA)
    }

    LaunchedEffect(duplicateFeedbackNonce) {
        if (duplicateFeedbackNonce > 0) {
            if (useCaseView == UseCaseView.EventTicketing) return@LaunchedEffect

            Toast.makeText(
                context,
                context.getString(R.string.already_scanned),
                Toast.LENGTH_SHORT
            ).show()
            val result = duplicateSnackbarHostState.showSnackbar(
                message = context.getString(R.string.already_scanned),
                actionLabel = context.getString(R.string.view_history),
                withDismissAction = true
            )
            if (result == SnackbarResult.ActionPerformed) {
                onOpenHistory()
            }
        }
    }

    LaunchedEffect(scanFeedbackNonce, scanFeedbackEnabled) {
        if (scanFeedbackEnabled && scanFeedbackNonce > lastHandledScanFeedbackNonce) {
            haptic.performHapticFeedback(HapticFeedbackType.LongPress)
            toneGenerator.startTone(ToneGenerator.TONE_PROP_BEEP, 120)
        }
        if (scanFeedbackNonce > lastHandledScanFeedbackNonce) {
            lastHandledScanFeedbackNonce = scanFeedbackNonce
        }
    }

    DisposableEffect(Unit) {
        onDispose {
            imageScanner.close()
            toneGenerator.release()
        }
    }

    var containerSize by remember { mutableStateOf(IntSize.Zero) }

    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(PrivateQrColors.Deep)
            .onSizeChanged { containerSize = it }
    ) {
        val density = LocalDensity.current
        val viewW = containerSize.width.toFloat()
        val viewH = containerSize.height.toFloat()
        val galleryOpen = imageScanPreviewUri != null
        val aimW = viewW * 0.70f
        val aimH = with(density) { 230.dp.toPx() }
        val aimLeft = (viewW - aimW) / 2f
        val aimTop = (viewH - aimH) / 2f
        val aimRight = aimLeft + aimW
        val aimBottom = aimTop + aimH

        if (cameraGranted && !galleryOpen) {
            CameraPreview(
                modifier = Modifier.fillMaxSize(),
                analysisEnabled = analysisEnabled,
                torchEnabled = torchEnabled,
                onTorchAvailabilityChanged = { torchAvailable = it },
                onDetectionStateChanged = { hasPotential, hasReadable, boxes, sourceWidth, sourceHeight ->
                    hasPotentialInView = hasPotential
                    hasReadableInView = hasReadable
                    detectionBoxes = boxes
                    detectionSourceWidth = sourceWidth
                    detectionSourceHeight = sourceHeight
                },
                onScan = { scanResult, readableBox, sourceWidth, sourceHeight ->
                    val box = readableBox ?: return@CameraPreview
                    if (sourceWidth <= 0 || sourceHeight <= 0 || viewW <= 0f || viewH <= 0f) {
                        return@CameraPreview
                    }

                    val srcW = sourceWidth.toFloat()
                    val srcH = sourceHeight.toFloat()
                    val scale = max(viewW / srcW, viewH / srcH)
                    val scaledW = srcW * scale
                    val scaledH = srcH * scale
                    val offsetX = (viewW - scaledW) / 2f
                    val offsetY = (viewH - scaledH) / 2f
                    val centerX = offsetX + (((box.left + box.right) * 0.5f) * srcW * scale)
                    val centerY = offsetY + (((box.top + box.bottom) * 0.5f) * srcH * scale)

                    val insideAim = centerX in aimLeft..aimRight && centerY in aimTop..aimBottom
                    if (!insideAim) return@CameraPreview

                    if (forceBatchMode) {
                        when (onEvaluateEventTicketScan(scanResult)) {
                            EventTicketScanDecision.Accept -> Unit
                            EventTicketScanDecision.Unregistered -> {
                                onAuditUnregisteredTicket(scanResult)
                                unregisteredTicketAlertContent = scanResult.visibleAlertContent()
                                showUnregisteredTicketAlert = true
                                return@CameraPreview
                            }
                            EventTicketScanDecision.DuplicateAlert -> {
                                onAuditDuplicateTicket(scanResult)
                                duplicateTicketAlertContent = scanResult.visibleAlertContent()
                                showDuplicateTicketAlert = true
                                return@CameraPreview
                            }
                            EventTicketScanDecision.Ignore -> return@CameraPreview
                        }
                    }

                    onScan(scanResult)
                }
            )

            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .background(
                        Brush.verticalGradient(
                            colors = listOf(
                                PrivateQrColors.Deep.copy(alpha = 0.72f),
                                PrivateQrColors.Teal900.copy(alpha = 0.34f),
                                PrivateQrColors.Deep.copy(alpha = 0.78f)
                            )
                        )
                    )
            )

            if (detectionBoxes.isNotEmpty()) {
                Canvas(
                    modifier = Modifier
                        .fillMaxSize()
                ) {
                    val sourceW = detectionSourceWidth.toFloat()
                    val sourceH = detectionSourceHeight.toFloat()
                    if (sourceW <= 0f || sourceH <= 0f) return@Canvas

                    val scale = max(size.width / sourceW, size.height / sourceH)
                    val scaledW = sourceW * scale
                    val scaledH = sourceH * scale
                    val offsetX = (size.width - scaledW) / 2f
                    val offsetY = (size.height - scaledH) / 2f

                    val boxColor = if (hasReadableInView) Color(0xFF4AE3A3) else Color(0xFFFFC857)
                    detectionBoxes.forEach { box ->
                        val left = offsetX + (box.left * sourceW * scale)
                        val top = offsetY + (box.top * sourceH * scale)
                        val right = offsetX + (box.right * sourceW * scale)
                        val bottom = offsetY + (box.bottom * sourceH * scale)
                        val mappedCorners = box.corners.map { p ->
                            Offset(
                                x = offsetX + (p.x * sourceW * scale),
                                y = offsetY + (p.y * sourceH * scale)
                            )
                        }
                        if (mappedCorners.size >= 4) {
                            val outline = Path().apply {
                                moveTo(mappedCorners.first().x, mappedCorners.first().y)
                                mappedCorners.drop(1).forEach { pt -> lineTo(pt.x, pt.y) }
                                close()
                            }
                            drawPath(
                                path = outline,
                                color = boxColor.copy(alpha = 0.96f),
                                style = Stroke(width = 5.5f)
                            )
                        } else if (right > left && bottom > top) {
                            drawRoundRect(
                                color = boxColor.copy(alpha = 0.95f),
                                topLeft = Offset(left, top),
                                size = Size(right - left, bottom - top),
                                cornerRadius = CornerRadius(22f, 22f),
                                style = Stroke(width = 5.5f)
                            )
                        }
                    }
                }
            }

            Canvas(
                modifier = Modifier
                    .align(Alignment.Center)
                    .fillMaxWidth(0.70f)
                    .height(230.dp)
            ) {
                val guideColor = when {
                    hasReadableInView -> Color(0xFF4AE3A3)
                    hasPotentialInView -> PrivateQrColors.Warning
                    else -> PrivateQrColors.Teal300
                }
                drawRoundRect(
                    color = guideColor.copy(alpha = 0.12f),
                    cornerRadius = CornerRadius(32f, 32f)
                )
                drawRoundRect(
                    color = guideColor.copy(alpha = 0.90f),
                    cornerRadius = CornerRadius(32f, 32f),
                    style = Stroke(width = 5f)
                )
            }

            Text(
                text = when {
                    hasReadableInView -> stringResource(R.string.live_readable_detected)
                    hasPotentialInView -> stringResource(R.string.live_potential_detected)
                    else -> stringResource(R.string.aim_center_hint)
                },
                color = Color.White,
                style = MaterialTheme.typography.labelLarge,
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .align(Alignment.BottomCenter)
                    .padding(bottom = if (isBatchModeActive) 190.dp else 56.dp)
                    .background(
                        color = Color.Black.copy(alpha = 0.45f),
                        shape = RoundedCornerShape(24.dp)
                    )
                    .padding(horizontal = 18.dp, vertical = 10.dp)
            )

            if (capabilities.allowScanFromImage) {
                IconButton(
                    onClick = { imagePicker.launch("image/*") },
                    modifier = Modifier
                        .align(Alignment.TopEnd)
                        .padding(top = 12.dp, end = 12.dp)
                        .background(
                            color = Color.Black.copy(alpha = 0.38f),
                            shape = RoundedCornerShape(18.dp)
                        )
                ) {
                    Icon(
                        painter = painterResource(id = android.R.drawable.ic_menu_gallery),
                        contentDescription = stringResource(R.string.scan_from_image),
                        tint = Color.White
                    )
                }
            }
            if (useCaseView == UseCaseView.EventTicketing) {
                IconButton(
                    onClick = { whitelistPicker.launch("*/*") },
                    modifier = Modifier
                        .align(Alignment.TopEnd)
                        .padding(top = 12.dp, end = if (capabilities.allowScanFromImage) 64.dp else 12.dp)
                        .background(
                            color = Color.Black.copy(alpha = 0.38f),
                            shape = RoundedCornerShape(18.dp)
                        )
                ) {
                    Icon(
                        imageVector = Icons.Default.UploadFile,
                        contentDescription = stringResource(R.string.import_whitelist),
                        tint = Color.White
                    )
                }
            }

            Text(
                text = stringResource(useCaseView.titleRes),
                color = Color.White,
                style = MaterialTheme.typography.titleMedium,
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .align(Alignment.TopCenter)
                    .padding(top = 16.dp, start = 64.dp, end = 64.dp)
                    .background(
                        color = Color.Black.copy(alpha = 0.52f),
                        shape = RoundedCornerShape(22.dp)
                    )
                    .padding(horizontal = 18.dp, vertical = 10.dp)
            )
            if (useCaseView == UseCaseView.EventTicketing) {
                Text(
                    text = stringResource(R.string.whitelist_loaded_count, eventTicketWhitelistCount),
                    color = Color.White,
                    textAlign = TextAlign.Center,
                    modifier = Modifier
                        .align(Alignment.TopCenter)
                        .padding(top = 56.dp, start = 64.dp, end = 64.dp)
                        .background(
                            color = Color.Black.copy(alpha = 0.35f),
                            shape = RoundedCornerShape(12.dp)
                        )
                        .padding(horizontal = 10.dp, vertical = 4.dp)
                )
            }

            Column(
                modifier = Modifier
                    .align(Alignment.TopStart)
                    .padding(12.dp),
                verticalArrangement = Arrangement.spacedBy(8.dp)
            ) {
                if (torchAvailable) {
                    OverlayIconToggle(
                        checked = torchEnabled,
                        onCheckedChange = { torchEnabled = it },
                        label = stringResource(R.string.flashlight),
                        checkedImageVector = Icons.Default.FlashOn,
                        uncheckedImageVector = Icons.Default.FlashOff,
                        showLabel = false
                    )
                }
                if (showBatchModeToggle) {
                    OverlayIconToggle(
                        checked = batchMode,
                        onCheckedChange = onBatchModeChange,
                        label = stringResource(R.string.batch_mode),
                        checkedImageVector = Icons.Default.ViewModule,
                        uncheckedImageVector = Icons.AutoMirrored.Filled.ViewList
                    )
                }
            }

            if (isBatchModeActive && (showBatchModeToggle || forceBatchMode)) {
                Box(modifier = Modifier.align(Alignment.BottomCenter)) {
                    BatchResultsPanel(
                        results = batchResults,
                        onClear = onClearBatchResults,
                        allowShare = capabilities.allowBatchShare
                    )
                }
            }

            SnackbarHost(
                hostState = duplicateSnackbarHostState,
                modifier = Modifier
                    .align(Alignment.BottomCenter)
                    .padding(bottom = if (isBatchModeActive) 12.dp else 80.dp)
            )
        } else if (!galleryOpen) {
            PermissionContent(
                showSettingsHint = showSettingsHint,
                onRequestPermission = { permissionLauncher.launch(Manifest.permission.CAMERA) },
                onOpenSettings = {
                    val intent = Intent(
                        Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                        Uri.fromParts("package", context.packageName, null)
                    )
                    context.startActivity(intent)
                }
            )
        } else {
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .background(Color.Black)
            )
        }

        if (lastResult != null && !isBatchModeActive) {
            val parsedContact = remember(lastResult) {
                if (lastResult.isBase64Encoded) null else ScanContentParsers.parseContact(lastResult.content)
            }
            val parsedEvent = remember(lastResult) {
                if (lastResult.isBase64Encoded) null else ScanContentParsers.parseCalendarEvent(lastResult.content)
            }

            ModalBottomSheet(
                onDismissRequest = onScanAgain,
                containerColor = PrivateQrColors.Surface,
                shape = RoundedCornerShape(topStart = 32.dp, topEnd = 32.dp)
            ) {
                Column(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(start = 20.dp, top = 4.dp, end = 20.dp, bottom = 28.dp),
                    verticalArrangement = Arrangement.spacedBy(14.dp)
                ) {
                    ResultVisualCard(
                        result = lastResult,
                        onOpenUrl = ::requestOpenUrl
                    )
                    val hasQuickActions = capabilities.allowCopy ||
                        capabilities.allowShare ||
                        (capabilities.allowAddContact && parsedContact != null) ||
                        (capabilities.allowOpenUrl && !lastResult.isBase64Encoded && lastResult.type == "URL")

                    if (hasQuickActions) {
                        Row(
                            modifier = Modifier
                                .fillMaxWidth()
                                .horizontalScroll(rememberScrollState()),
                            horizontalArrangement = Arrangement.spacedBy(12.dp)
                        ) {
                            if (capabilities.allowAddContact && parsedContact != null) {
                                ResultActionPill(
                                    text = stringResource(R.string.add_contact),
                                    imageVector = Icons.Default.PersonAdd,
                                    onClick = { Intents.addContact(context, parsedContact, lastResult.content) },
                                    primary = true
                                )
                            }
                            if (capabilities.allowCopy) {
                                ResultActionPill(
                                    text = stringResource(R.string.copy),
                                    imageVector = Icons.Default.ContentCopy,
                                    onClick = { ClipboardUtil.copy(context, lastResult.content) }
                                )
                            }
                            if (capabilities.allowShare) {
                                ResultActionPill(
                                    text = stringResource(R.string.share),
                                    imageVector = Icons.Default.Share,
                                    onClick = { Intents.shareText(context, lastResult.content) }
                                )
                            }
                            if (capabilities.allowOpenUrl && !lastResult.isBase64Encoded && lastResult.type == "URL") {
                                ResultActionPill(
                                    text = stringResource(R.string.open),
                                    imageVector = Icons.AutoMirrored.Filled.OpenInNew,
                                    onClick = { requestOpenUrl(lastResult.content) },
                                    primary = true
                                )
                            }
                        }
                    }

                    if (!lastResult.isBase64Encoded) {
                        when (lastResult.type) {
                            "Phone" -> {
                                if (capabilities.allowDialPhone) {
                                    Button(
                                        onClick = {
                                            Intents.dialPhone(context, ScanContentParsers.extractPhoneNumber(lastResult.content))
                                        },
                                        colors = ButtonDefaults.buttonColors(
                                            containerColor = PrivateQrColors.Teal700
                                        )
                                    ) {
                                        Text(stringResource(R.string.call_number))
                                    }
                                }
                            }

                            "SMS" -> {
                                if (capabilities.allowSendSms) {
                                    Button(
                                        onClick = {
                                            val smsData = ScanContentParsers.parseSms(lastResult.content)
                                            Intents.sendSms(context, smsData.first, smsData.second)
                                        },
                                        colors = ButtonDefaults.buttonColors(
                                            containerColor = PrivateQrColors.Teal700
                                        )
                                    ) {
                                        Text(stringResource(R.string.send_sms))
                                    }
                                }
                            }

                            "Email" -> {
                                if (capabilities.allowSendEmail) {
                                    Button(
                                        onClick = {
                                            Intents.sendEmail(context, ScanContentParsers.extractEmail(lastResult.content), null)
                                        },
                                        colors = ButtonDefaults.buttonColors(
                                            containerColor = PrivateQrColors.Teal700
                                        )
                                    ) {
                                        Text(stringResource(R.string.send_email))
                                    }
                                }
                            }

                            "WiFi" -> {
                                if (capabilities.allowOpenWifiSettings) {
                                    Button(
                                        onClick = { Intents.openWifiSettings(context) },
                                        colors = ButtonDefaults.buttonColors(
                                            containerColor = PrivateQrColors.Teal700
                                        )
                                    ) {
                                        Text(stringResource(R.string.open_wifi_settings))
                                    }
                                }
                            }

                            "Calendar" -> {
                                if (capabilities.allowAddCalendarEvent) {
                                    Button(
                                        onClick = {
                                            Intents.addCalendarEvent(context, parsedEvent, lastResult.content)
                                        },
                                        colors = ButtonDefaults.buttonColors(
                                            containerColor = PrivateQrColors.Teal700
                                        )
                                    ) {
                                        Text(stringResource(R.string.add_calendar_event))
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        if (showRiskWarning && pendingOpenUrl != null) {
            AlertDialog(
                onDismissRequest = { showRiskWarning = false },
                containerColor = PrivateQrColors.Surface,
                shape = RoundedCornerShape(28.dp),
                title = {
                    Text(
                        text = stringResource(R.string.risk_warning_title),
                        color = PrivateQrColors.TextPrimary,
                        style = MaterialTheme.typography.headlineSmall
                    )
                },
                text = {
                    Text(
                        text = stringResource(R.string.risk_warning),
                        color = PrivateQrColors.TextSecondary,
                        style = MaterialTheme.typography.bodyLarge
                    )
                },
                confirmButton = {
                    TextButton(
                        onClick = {
                            Intents.openUrl(context, pendingOpenUrl!!)
                            showRiskWarning = false
                        },
                        colors = ButtonDefaults.textButtonColors(
                            containerColor = PrivateQrColors.Teal700,
                            contentColor = PrivateQrColors.Surface
                        ),
                        shape = RoundedCornerShape(18.dp)
                    ) {
                        Text(stringResource(R.string.open_anyway))
                    }
                },
                dismissButton = {
                    TextButton(
                        onClick = { showRiskWarning = false },
                        colors = ButtonDefaults.textButtonColors(
                            contentColor = PrivateQrColors.Teal700
                        )
                    ) {
                        Text(stringResource(R.string.cancel))
                    }
                }
            )
        }

        if (showDuplicateTicketAlert && useCaseView == UseCaseView.EventTicketing) {
            AlertDialog(
                onDismissRequest = { showDuplicateTicketAlert = false },
                title = { Text(text = stringResource(R.string.duplicate_ticket_alert_title), color = Color(0xFFB00020)) },
                text = {
                    Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                        Text(text = stringResource(R.string.duplicate_ticket_alert_message))
                        if (!duplicateTicketAlertContent.isNullOrBlank()) {
                            Text(
                                text = stringResource(R.string.duplicate_ticket_alert_code, duplicateTicketAlertContent!!),
                                color = Color(0xFFB00020)
                            )
                        }
                    }
                },
                confirmButton = {
                    TextButton(onClick = { showDuplicateTicketAlert = false }) {
                        Text(stringResource(R.string.confirm))
                    }
                }
            )
        }
        if (showUnregisteredTicketAlert && useCaseView == UseCaseView.EventTicketing) {
            AlertDialog(
                onDismissRequest = { showUnregisteredTicketAlert = false },
                title = { Text(text = stringResource(R.string.unregistered_ticket_alert_title), color = Color(0xFFB00020)) },
                text = {
                    Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
                        Text(text = stringResource(R.string.unregistered_ticket_alert_message))
                        if (!unregisteredTicketAlertContent.isNullOrBlank()) {
                            Text(
                                text = stringResource(R.string.unregistered_ticket_alert_code, unregisteredTicketAlertContent!!),
                                color = Color(0xFFB00020)
                            )
                        }
                    }
                },
                confirmButton = {
                    TextButton(onClick = { showUnregisteredTicketAlert = false }) {
                        Text(stringResource(R.string.confirm))
                    }
                }
            )
        }

        if (imageScanPreviewUri != null) {
            GalleryScanPreviewDialog(
                imageUri = imageScanPreviewUri,
                candidates = imageScanCandidates,
                onPick = { candidate ->
                    onScan(candidate.result)
                    imageScanCandidates = emptyList()
                    imageScanPreviewUri = null
                },
                onDismiss = {
                    imageScanCandidates = emptyList()
                    imageScanPreviewUri = null
                }
            )
        }

        if (showImageScanFailed) {
            AlertDialog(
                onDismissRequest = { showImageScanFailed = false },
                text = { Text(stringResource(R.string.image_scan_failed)) },
                confirmButton = {
                    TextButton(onClick = { showImageScanFailed = false }) {
                        Text(stringResource(R.string.confirm))
                    }
                }
            )
        }
    }
}

private fun parseWhitelistIds(raw: String): Set<String> {
    if (raw.isBlank()) return emptySet()
    return raw
        .split('\n', '\r', ',', ';', '\t')
        .asSequence()
        .map { it.trim().lowercase() }
        .filter { it.isNotBlank() }
        .toSet()
}

@Composable
private fun ResultActionPill(
    text: String,
    imageVector: ImageVector,
    onClick: () -> Unit,
    primary: Boolean = false
) {
    TextButton(
        onClick = onClick,
        colors = ButtonDefaults.textButtonColors(
            containerColor = if (primary) PrivateQrColors.Teal700 else PrivateQrColors.AppBackground,
            contentColor = if (primary) PrivateQrColors.Surface else PrivateQrColors.TextPrimary
        ),
        shape = RoundedCornerShape(18.dp)
    ) {
        Icon(
            imageVector = imageVector,
            contentDescription = null,
            modifier = Modifier.size(18.dp)
        )
        Text(
            text = text,
            fontWeight = FontWeight.ExtraBold,
            modifier = Modifier.padding(start = 8.dp)
        )
    }
}
