package pl.firmatpp.kierowca.ui

import android.Manifest
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationManager
import android.net.Uri
import android.os.Build
import android.provider.Settings
import androidx.activity.compose.BackHandler
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
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.IntrinsicSize
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AddPhotoAlternate
import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.material.icons.outlined.CameraAlt
import androidx.compose.material.icons.outlined.CalendarToday
import androidx.compose.material.icons.outlined.CheckCircle
import androidx.compose.material.icons.outlined.CloudUpload
import androidx.compose.material.icons.outlined.Factory
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.outlined.LocationOn
import androidx.compose.material.icons.outlined.LocalShipping
import androidx.compose.material.icons.outlined.Navigation
import androidx.compose.material.icons.outlined.Person
import androidx.compose.material.icons.outlined.Phone
import androidx.compose.material.icons.outlined.Refresh
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.foundation.verticalScroll
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.google.android.gms.auth.api.phone.SmsRetriever
import com.google.android.gms.common.api.CommonStatusCodes
import com.google.android.gms.common.api.Status
import java.io.File
import java.time.Instant
import java.time.LocalDate
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import kotlinx.coroutines.delay
import java.util.Locale
import pl.firmatpp.kierowca.R
import pl.firmatpp.kierowca.data.PhotoUploadMetadata
import pl.firmatpp.kierowca.data.model.DriverRouteDto
import pl.firmatpp.kierowca.data.model.NavigationPointDto
import pl.firmatpp.kierowca.data.model.RoutePhotoDto
import pl.firmatpp.kierowca.data.upload.PhotoUploadEntity
import pl.firmatpp.kierowca.data.upload.PhotoUploadStatus
import pl.firmatpp.kierowca.domain.OtpCodeExtractor
import pl.firmatpp.kierowca.domain.RouteDisplayMapper
import pl.firmatpp.kierowca.ui.theme.TppColors

@Composable
fun DriverApp(viewModel: DriverViewModel) {
    val state by viewModel.state.collectAsState()
    val lifecycleOwner = LocalLifecycleOwner.current
    var appInForeground by remember { mutableStateOf(true) }

    BackHandler(enabled = state.screen != DriverScreen.Initializing && state.screen != DriverScreen.Phone && state.screen != DriverScreen.Routes) {
        viewModel.back()
    }
    DisposableEffect(lifecycleOwner, state.screen) {
        val observer = LifecycleEventObserver { _, event ->
            when (event) {
                Lifecycle.Event.ON_START -> {
                    appInForeground = true
                    if (state.screen == DriverScreen.Routes) viewModel.refreshRoutesSilently()
                }
                Lifecycle.Event.ON_STOP -> appInForeground = false
                else -> Unit
            }
        }
        lifecycleOwner.lifecycle.addObserver(observer)
        onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
    }
    LaunchedEffect(state.screen, state.selectedDate, state.autoRefreshSeconds, appInForeground) {
        if (state.screen != DriverScreen.Routes || !appInForeground) return@LaunchedEffect

        val intervalMillis = state.autoRefreshSeconds.coerceIn(30, 600) * 1000L
        while (true) {
            delay(intervalMillis)
            viewModel.refreshRoutesSilently()
        }
    }
    SmsUserConsentEffect(
        enabled = state.screen == DriverScreen.Phone || state.screen == DriverScreen.Otp,
        onCode = viewModel::updateOtpCode,
    )

    Box(Modifier.fillMaxSize().background(TppColors.Surface)) {
        when (state.screen) {
            DriverScreen.Initializing -> StartupScreen()
            DriverScreen.Phone -> PhoneScreen(state, viewModel::requestOtp)
            DriverScreen.Otp -> OtpScreen(state, viewModel::updateOtpCode, viewModel::verifyOtp, viewModel::back)
            DriverScreen.Routes -> RoutesScreen(
                state = state,
                onRefresh = viewModel::refreshRoutesSilently,
                onDate = viewModel::selectRouteDate,
                onProfile = viewModel::openProfile,
                onPhotoQueue = viewModel::openPhotoQueue,
                onRoute = viewModel::openRoute,
            )
            DriverScreen.Profile -> ProfileScreen(state, viewModel::refreshRoutes, viewModel::openProfile, viewModel::logout)
            DriverScreen.Detail -> DetailScreen(
                state,
                viewModel::back,
                viewModel::uploadPhoto,
                viewModel::openPhoto,
                viewModel::deletePhoto,
                viewModel::deleteConfirmedUpload,
                viewModel::retryPhotoUpload,
                viewModel::refreshSelectedRoute,
                viewModel::completeSelectedRoute,
            )
            DriverScreen.Photo -> PhotoScreen(state, viewModel::back)
            DriverScreen.PhotoQueue -> PhotoQueueScreen(
                state = state,
                onBack = viewModel::back,
                onRetryUpload = viewModel::retryPhotoUpload,
            )
        }

        if (state.loading && state.screen != DriverScreen.Initializing) {
            Box(Modifier.fillMaxSize().background(Color.White.copy(alpha = 0.42f)), contentAlignment = Alignment.Center) {
                CircularProgressIndicator(color = TppColors.Forest)
            }
        }
    }
}

@Composable
private fun StartupScreen() {
    Column(
        Modifier
            .fillMaxSize()
            .navigationBarsPadding()
            .background(TppColors.Surface),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Column(Modifier.fillMaxWidth().statusBarsPadding()) {
            BrandBannerHeader(height = 178.dp)
        }
        Column(
            Modifier
                .weight(1f)
                .fillMaxWidth()
                .padding(horizontal = 32.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center,
        )
        {
            CircularProgressIndicator(color = TppColors.Forest)
            Spacer(Modifier.height(20.dp))
            Text(
                "Sprawdzanie sesji",
                color = TppColors.Muted,
                fontFamily = FontFamily.Monospace,
                fontWeight = FontWeight.SemiBold,
            )
        }
    }
}

@Composable
private fun PhoneScreen(state: DriverUiState, onSubmit: (String) -> Unit) {
    var phoneDigits by remember(state.phone) { mutableStateOf(polishPhoneDigits(state.phone)) }
    Column(
        Modifier
            .fillMaxSize()
            .background(TppColors.Surface)
            .navigationBarsPadding(),
    ) {
        Column(Modifier.fillMaxWidth().statusBarsPadding()) {
            BrandBannerHeader(height = 190.dp)
        }
        Column(
            Modifier
                .fillMaxSize()
                .verticalScroll(rememberScrollState())
                .padding(start = 24.dp, top = 28.dp, end = 24.dp, bottom = 28.dp),
            horizontalAlignment = Alignment.Start,
        ) {
            Text(
                "Firma TPP - Kierowca",
                color = TppColors.Forest,
                fontFamily = FontFamily.Monospace,
                fontWeight = FontWeight.Bold,
                fontSize = 14.sp,
            )
            Spacer(Modifier.height(10.dp))
            Text(
                "Zaloguj się",
                style = MaterialTheme.typography.headlineLarge,
                color = TppColors.Ink,
                fontWeight = FontWeight.Bold,
            )
            Spacer(Modifier.height(8.dp))
            Text(
                "Wpisz numer telefonu kierowcy. Kod OTP wyślemy SMS-em.",
                color = TppColors.Muted,
                fontSize = 17.sp,
                fontWeight = FontWeight.Medium,
            )
            Spacer(Modifier.height(28.dp))
            Card(
                modifier = Modifier.fillMaxWidth(),
                colors = CardDefaults.cardColors(containerColor = Color(0xFFFEFFFC)),
                border = BorderStroke(1.dp, TppColors.Outline.copy(alpha = 0.72f)),
                shape = RoundedCornerShape(8.dp),
            ) {
                Column(Modifier.fillMaxWidth().padding(20.dp), verticalArrangement = Arrangement.spacedBy(18.dp)) {
                    Text(
                        "Numer telefonu",
                        color = TppColors.Ink,
                        fontFamily = FontFamily.Monospace,
                        fontWeight = FontWeight.Bold,
                        fontSize = 18.sp,
                    )
                    PhoneNumberField(
                        digits = phoneDigits,
                        onDigitsChange = { phoneDigits = polishPhoneDigits(it) },
                    )
                    Text(
                        "Dostęp tylko dla upoważnionych kierowców.",
                        color = TppColors.Muted,
                        fontWeight = FontWeight.SemiBold,
                        fontSize = 15.sp,
                    )
                    ErrorText(state.error)
                    Button(
                        onClick = { onSubmit("+48$phoneDigits") },
                        modifier = Modifier
                            .fillMaxWidth()
                            .height(64.dp),
                        colors = ButtonDefaults.buttonColors(containerColor = TppColors.ContainerGreen),
                        shape = RoundedCornerShape(4.dp),
                        contentPadding = PaddingValues(horizontal = 20.dp),
                    ) {
                        Text(
                            "Wyślij kod OTP",
                            fontFamily = FontFamily.Monospace,
                            fontWeight = FontWeight.Bold,
                            fontSize = 20.sp,
                        )
                    }
                }
            }
        }
    }
}

@Composable
private fun BrandBannerHeader(height: Dp) {
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .height(height)
            .background(Color(0xFFFAFCF1)),
    ) {
        Image(
            painter = painterResource(R.drawable.tpp_banner),
            contentDescription = "Firma TPP",
            contentScale = ContentScale.Crop,
            modifier = Modifier.matchParentSize(),
        )
        Box(
            Modifier
                .matchParentSize()
                .background(
                    Brush.horizontalGradient(
                        0f to Color(0xFFFAFCF1).copy(alpha = 0.56f),
                        0.18f to Color.Transparent,
                        0.82f to Color.Transparent,
                        1f to Color(0xFFFAFCF1).copy(alpha = 0.56f),
                    ),
                ),
        )
        Box(
            Modifier
                .matchParentSize()
                .background(
                    Brush.verticalGradient(
                        0f to Color.Transparent,
                        0.72f to Color.Transparent,
                        1f to TppColors.Surface.copy(alpha = 0.88f),
                    ),
                ),
        )
    }
}

@Composable
private fun PhoneNumberField(digits: String, onDigitsChange: (String) -> Unit) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .height(82.dp)
            .background(Color(0xFFFEFFFC), RoundedCornerShape(2.dp))
            .border(BorderStroke(1.dp, TppColors.Outline), RoundedCornerShape(2.dp)),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Box(
            Modifier
                .width(94.dp)
                .fillMaxHeight()
                .background(TppColors.Panel),
            contentAlignment = Alignment.Center,
        ) {
            Text("+48", color = TppColors.Ink, fontSize = 26.sp)
        }
        Box(Modifier.width(1.dp).fillMaxHeight().background(TppColors.Outline))
        BasicTextField(
            value = digits,
            onValueChange = { onDigitsChange(it) },
            singleLine = true,
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone),
            visualTransformation = PolishPhoneNumberVisualTransformation,
            textStyle = TextStyle(
                color = TppColors.Muted,
                fontSize = 28.sp,
                fontWeight = FontWeight.Medium,
            ),
            modifier = Modifier
                .weight(1f)
                .padding(horizontal = 24.dp),
            decorationBox = { innerTextField ->
                Box(contentAlignment = Alignment.CenterStart) {
                    if (digits.isBlank()) {
                        Text(
                            "000 000 000",
                            color = TppColors.Muted.copy(alpha = 0.92f),
                            fontSize = 28.sp,
                            fontWeight = FontWeight.Medium,
                        )
                    }
                    innerTextField()
                }
            },
        )
    }
}

@Composable
private fun OtpScreen(
    state: DriverUiState,
    onCodeChange: (String) -> Unit,
    onSubmit: (String) -> Unit,
    onBack: () -> Unit,
) {
    AuthShell(title = "Kod SMS", subtitle = "Wpisz kod wyslany na ${state.maskedPhone}.", onBack = onBack) {
        OutlinedTextField(
            value = state.otpCode,
            onValueChange = { onCodeChange(it) },
            label = { Text("Kod OTP") },
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword),
            modifier = Modifier.fillMaxWidth(),
            singleLine = true,
        )
        ErrorText(state.error)
        Spacer(Modifier.height(if (state.error.isNullOrBlank()) 18.dp else 8.dp))
        PrimaryButton("Zaloguj") { onSubmit(state.otpCode) }
    }
}

@Composable
private fun AuthShell(title: String, subtitle: String, onBack: (() -> Unit)? = null, content: @Composable ColumnScope.() -> Unit) {
    Column(
        Modifier.fillMaxSize().padding(20.dp),
        verticalArrangement = Arrangement.Center,
    ) {
        if (onBack != null) IconButton(onClick = onBack) { Icon(Icons.Outlined.ArrowBack, contentDescription = "Wstecz") }
        Text(title, style = MaterialTheme.typography.headlineLarge, fontWeight = FontWeight.Bold, color = TppColors.Forest)
        Text(subtitle, color = TppColors.Muted, modifier = Modifier.padding(top = 8.dp, bottom = 24.dp))
        content()
    }
}

@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
@Composable
private fun RoutesScreen(
    state: DriverUiState,
    onRefresh: () -> Unit,
    onDate: (String) -> Unit,
    onProfile: () -> Unit,
    onPhotoQueue: () -> Unit,
    onRoute: (String) -> Unit,
) {
    Scaffold(
        topBar = { StitchHeader() },
        bottomBar = {
            StitchBottomBar(
                activeScreen = DriverScreen.Routes,
                onRoutes = onRefresh,
                onProfile = onProfile,
            )
        },
        containerColor = TppColors.Surface,
    ) { padding ->
        val pullRefreshState = rememberPullRefreshState(state.refreshing, onRefresh)
        Box(Modifier.fillMaxSize().padding(padding).pullRefresh(pullRefreshState)) {
            LazyColumn(
                Modifier.fillMaxSize(),
                contentPadding = PaddingValues(start = 16.dp, top = 24.dp, end = 16.dp, bottom = 20.dp),
                verticalArrangement = Arrangement.spacedBy(18.dp),
            ) {
                item {
                    Row(
                        Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.SpaceBetween,
                        verticalAlignment = Alignment.CenterVertically,
                    ) {
                        Text(
                            routeListTitle(state.selectedDate),
                            style = MaterialTheme.typography.headlineLarge,
                            fontWeight = FontWeight.Bold,
                            color = TppColors.Ink,
                        )
                        DateChip(state.selectedDate)
                    }
                }
                item {
                    RouteDateSelector(
                        selectedDate = state.selectedDate,
                        minDate = state.minRouteDate,
                        maxDate = state.maxRouteDate,
                        onDate = onDate,
                    )
                }
                item { OfflineStaleBanner(state) }
                item { PhotoQueueBanner(state.queuedPhotoUploads, onPhotoQueue) }
                if (state.routes.isEmpty()) {
                    item { EmptyState("Brak kursow na wybrany dzien") }
                } else {
                    items(state.routes, key = { it.id }) { route -> StitchRouteCard(route, onRoute) }
                }
                item { ErrorText(state.error) }
            }
            PullRefreshIndicator(
                refreshing = state.refreshing,
                state = pullRefreshState,
                modifier = Modifier.align(Alignment.TopCenter),
                backgroundColor = Color.White,
                contentColor = TppColors.Forest,
            )
        }
    }
}

@Composable
private fun PhotoQueueBanner(uploads: List<PhotoUploadEntity>, onPhotoQueue: () -> Unit) {
    val count = queuedPhotoUploadCount(uploads.map { it.status })
    if (count <= 0) return

    Card(
        colors = CardDefaults.cardColors(containerColor = Color.White),
        border = BorderStroke(1.dp, TppColors.Outline),
        shape = RoundedCornerShape(8.dp),
        modifier = Modifier.fillMaxWidth(),
    ) {
        Row(
            Modifier.fillMaxWidth().padding(14.dp),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.spacedBy(12.dp),
        ) {
            Box(
                Modifier.size(42.dp).background(TppColors.ContainerGreen, RoundedCornerShape(6.dp)),
                contentAlignment = Alignment.Center,
            ) {
                Icon(Icons.Outlined.CloudUpload, contentDescription = null, tint = TppColors.Forest)
            }
            Text(
                "W kolejce do wysłania jest $count ${photoCountLabel(count)}.",
                color = TppColors.Ink,
                fontWeight = FontWeight.SemiBold,
                modifier = Modifier.weight(1f),
            )
            Button(
                onClick = onPhotoQueue,
                colors = ButtonDefaults.buttonColors(containerColor = TppColors.Forest),
                shape = RoundedCornerShape(4.dp),
                contentPadding = PaddingValues(horizontal = 14.dp, vertical = 10.dp),
            ) {
                Text("Kolejka", color = Color.White, fontWeight = FontWeight.Bold)
            }
        }
    }
}

@Composable
private fun PhotoQueueScreen(
    state: DriverUiState,
    onBack: () -> Unit,
    onRetryUpload: (PhotoUploadEntity) -> Unit,
) {
    val uploads = state.queuedPhotoUploads.sortedBy { it.createdAtEpochMillis }

    Scaffold(
        topBar = { DetailHeader(onBack, title = "Kolejka zdjęć") },
        containerColor = TppColors.Surface,
    ) { padding ->
        LazyColumn(
            Modifier.fillMaxSize().padding(padding),
            contentPadding = PaddingValues(16.dp),
            verticalArrangement = Arrangement.spacedBy(14.dp),
        ) {
            item { OfflineStaleBanner(state) }
            if (uploads.isEmpty()) {
                item { EmptyState("Brak zdjęć oczekujących na przesłanie") }
            } else {
                item {
                    Text(
                        "Zdjęcia do przesłania (${uploads.size})",
                        style = MaterialTheme.typography.headlineSmall,
                        fontWeight = FontWeight.Bold,
                        color = TppColors.Ink,
                    )
                }
                items(uploads, key = { it.clientRequestId }) { upload ->
                    PhotoQueueItem(upload, onRetryUpload)
                }
            }
        }
    }
}

@Composable
private fun PhotoQueueItem(upload: PhotoUploadEntity, onRetryUpload: (PhotoUploadEntity) -> Unit) {
    val status = upload.statusType
    val hasFailureDetails = canShowPhotoUploadFailureDetails(upload.status, upload.lastError)
    var showFailureDetails by remember { mutableStateOf(false) }
    Card(
        colors = CardDefaults.cardColors(containerColor = Color.White),
        border = BorderStroke(1.dp, TppColors.Outline),
        shape = RoundedCornerShape(8.dp),
        modifier = Modifier.fillMaxWidth(),
    ) {
        Row(
            Modifier.fillMaxWidth().padding(12.dp),
            horizontalArrangement = Arrangement.spacedBy(12.dp),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            AsyncImage(
                model = File(upload.localPath),
                contentDescription = "Zdjęcie z kolejki",
                contentScale = ContentScale.Crop,
                modifier = Modifier.size(76.dp).background(TppColors.Panel, RoundedCornerShape(6.dp)),
            )
            Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(6.dp)) {
                Text("Kurs #${upload.routeId}", color = TppColors.Ink, fontWeight = FontWeight.Bold)
                Text(
                    upload.lastError?.lineSequence()?.firstOrNull { it.isNotBlank() }?.takeIf { hasFailureDetails } ?: status.label,
                    color = if (hasFailureDetails) TppColors.Error else TppColors.Muted,
                    fontFamily = FontFamily.Monospace,
                    style = MaterialTheme.typography.labelMedium,
                )
                if (status == PhotoUploadStatus.Uploading || status == PhotoUploadStatus.Verifying) {
                    androidx.compose.material3.LinearProgressIndicator(
                        progress = { (upload.progress.coerceIn(0, 100) / 100f) },
                        modifier = Modifier.fillMaxWidth(),
                        color = TppColors.Forest,
                        trackColor = TppColors.Outline.copy(alpha = 0.4f),
                    )
                }
            }
            if (hasFailureDetails || canRetryPhotoUpload(upload.status)) {
                Row(horizontalArrangement = Arrangement.spacedBy(4.dp), verticalAlignment = Alignment.CenterVertically) {
                    if (hasFailureDetails) {
                        IconButton(
                            onClick = { showFailureDetails = true },
                            modifier = Modifier
                                .size(40.dp)
                                .background(TppColors.Surface, RoundedCornerShape(20.dp))
                                .border(1.dp, TppColors.Error.copy(alpha = 0.35f), RoundedCornerShape(20.dp)),
                        ) {
                            Icon(
                                Icons.Outlined.Info,
                                contentDescription = "Pokaż szczegóły błędu",
                                tint = TppColors.Error,
                                modifier = Modifier.size(20.dp),
                            )
                        }
                    }
                    if (canRetryPhotoUpload(upload.status)) {
                        Button(
                            onClick = { onRetryUpload(upload) },
                            colors = ButtonDefaults.buttonColors(containerColor = TppColors.Forest),
                            shape = RoundedCornerShape(4.dp),
                            contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp),
                        ) {
                            Icon(
                                Icons.Outlined.Refresh,
                                contentDescription = null,
                                tint = Color.White,
                                modifier = Modifier.size(18.dp),
                            )
                            Spacer(Modifier.width(6.dp))
                            Text("Ponów", color = Color.White, fontWeight = FontWeight.Bold)
                        }
                    }
                }
            }
        }
    }
    if (showFailureDetails) {
        PhotoUploadFailureDialog(
            details = upload.lastError.orEmpty(),
            onDismiss = { showFailureDetails = false },
        )
    }
}

@Composable
private fun PhotoUploadFailureDialog(details: String, onDismiss: () -> Unit) {
    AlertDialog(
        onDismissRequest = onDismiss,
        title = {
            Text("Dlaczego nie wysłano zdjęcia?", color = TppColors.Ink, fontWeight = FontWeight.Bold)
        },
        text = {
            Text(
                details,
                color = TppColors.Ink,
                fontFamily = FontFamily.Monospace,
                style = MaterialTheme.typography.bodySmall,
                modifier = Modifier.heightIn(max = 320.dp).verticalScroll(rememberScrollState()),
            )
        },
        confirmButton = {
            TextButton(onClick = onDismiss) {
                Text("Zamknij", color = TppColors.Forest, fontWeight = FontWeight.Bold)
            }
        },
    )
}

@Composable
private fun StitchHeader() {
    Column(Modifier.fillMaxWidth().background(TppColors.Surface).statusBarsPadding()) {
        BrandBannerHeader(height = 124.dp)
        HorizontalDivider(color = TppColors.Outline.copy(alpha = 0.86f), thickness = 1.dp)
    }
}

@Composable
private fun DateChip(date: String) {
    val formatter = remember { DateTimeFormatter.ofPattern("d MMM", Locale("pl", "PL")) }
    val label = remember(date) { LocalDate.parse(date).format(formatter).replace(".", "").replaceFirstChar { it.uppercase() } }
    Row(
        modifier = Modifier.background(TppColors.Panel, RoundedCornerShape(12.dp)).padding(horizontal = 16.dp, vertical = 12.dp),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.spacedBy(10.dp),
    ) {
        Icon(Icons.Outlined.CalendarToday, contentDescription = null, tint = TppColors.Forest)
        Text(label, fontFamily = FontFamily.Monospace, color = TppColors.Muted, fontWeight = FontWeight.SemiBold)
    }
}

@Composable
private fun RouteDateSelector(selectedDate: String, minDate: String, maxDate: String, onDate: (String) -> Unit) {
    val dates = remember(minDate, maxDate) {
        runCatching {
            generateSequence(LocalDate.parse(minDate)) { date ->
                val next = date.plusDays(1)
                if (next <= LocalDate.parse(maxDate)) next else null
            }.toList()
        }.getOrElse { listOf(LocalDate.parse(selectedDate)) }
    }
    val listState = rememberLazyListState()

    LaunchedEffect(dates, selectedDate) {
        val selectedIndex = dates.indexOfFirst { it.toString() == selectedDate }
        if (selectedIndex >= 0) {
            listState.scrollToItem(selectedIndex)
        }
    }

    LazyRow(state = listState, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
        items(dates, key = { it.toString() }) { date ->
            DateSelectorChip(
                date = date,
                selected = date.toString() == selectedDate,
                onClick = { onDate(date.toString()) },
            )
        }
    }
}

@Composable
private fun DateSelectorChip(date: LocalDate, selected: Boolean, onClick: () -> Unit) {
    val formatter = remember { DateTimeFormatter.ofPattern("EEE d MMM", Locale("pl", "PL")) }
    val label = remember(date) { date.format(formatter).replace(".", "").replaceFirstChar { it.uppercase() } }
    val background = if (selected) TppColors.Forest else Color.White
    val content = if (selected) Color.White else TppColors.Muted

    Row(
        modifier = Modifier
            .background(background, RoundedCornerShape(8.dp))
            .clickable(onClick = onClick)
            .padding(horizontal = 14.dp, vertical = 10.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Text(label, color = content, fontFamily = FontFamily.Monospace, fontWeight = FontWeight.SemiBold)
    }
}

private fun routeListTitle(selectedDate: String): String {
    val today = LocalDate.now()
    val date = runCatching { LocalDate.parse(selectedDate) }.getOrDefault(today)

    return when (date) {
        today -> "Dzisiejsze Kursy"
        today.plusDays(1) -> "Jutrzejsze Kursy"
        else -> "Kursy"
    }
}

@Composable
private fun StitchRouteCard(route: DriverRouteDto, onRoute: (String) -> Unit) {
    val display = RouteDisplayMapper.toCard(route)
    val stripColor = routeStatusColor(route.status)
    Card(
        modifier = Modifier.fillMaxWidth(),
        colors = CardDefaults.cardColors(containerColor = Color.White),
        shape = RoundedCornerShape(8.dp),
        border = BorderStroke(1.dp, TppColors.Outline),
    ) {
        Row(Modifier.fillMaxWidth().height(IntrinsicSize.Min)) {
            Box(
                Modifier
                    .fillMaxHeight()
                    .width(8.dp)
                    .background(stripColor, RoundedCornerShape(topStart = 8.dp, bottomStart = 8.dp)),
            )
            Column(Modifier.fillMaxWidth()) {
                Column(Modifier.padding(start = 24.dp, top = 24.dp, end = 24.dp, bottom = 20.dp)) {
                    Row(
                        Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.SpaceBetween,
                        verticalAlignment = Alignment.CenterVertically,
                    ) {
                        StatusChip(route.status, stripColor)
                        Text(
                            "Kontrakt: #${display.contractLabel.trimStart('#')}",
                            fontFamily = FontFamily.Monospace,
                            color = TppColors.Ink,
                            fontWeight = FontWeight.SemiBold,
                        )
                    }
                    Spacer(Modifier.height(26.dp))
                    RouteTimeline(display.origin, display.destination)
                }
                HorizontalDivider(color = TppColors.Outline.copy(alpha = 0.55f))
                Button(
                    onClick = { onRoute(route.id) },
                    modifier = Modifier.fillMaxWidth().padding(16.dp).height(56.dp),
                    colors = ButtonDefaults.buttonColors(containerColor = stripColor),
                    shape = RoundedCornerShape(4.dp),
                ) {
                    Text("Szczegóły  →", fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold)
                }
            }
        }
    }
}

@Composable
private fun StatusChip(status: String, color: Color) {
    Text(
        status.ifBlank { "NOWY" }.uppercase(Locale("pl", "PL")),
        modifier = Modifier
            .background(color.copy(alpha = 0.18f), RoundedCornerShape(3.dp))
            .padding(horizontal = 14.dp, vertical = 8.dp),
        color = color,
        fontFamily = FontFamily.Monospace,
        fontWeight = FontWeight.Bold,
    )
}

@Composable
private fun RouteTimeline(origin: String, destination: String) {
    Row(horizontalArrangement = Arrangement.spacedBy(14.dp)) {
        Column(horizontalAlignment = Alignment.CenterHorizontally) {
            Box(Modifier.size(18.dp).background(Color.White, RoundedCornerShape(50)).padding(3.dp)) {
                Box(Modifier.fillMaxSize().background(TppColors.Forest, RoundedCornerShape(50)))
            }
            Box(Modifier.width(3.dp).height(64.dp).background(TppColors.Outline.copy(alpha = 0.8f)))
            Box(Modifier.size(18.dp).background(TppColors.Navy, RoundedCornerShape(50)))
        }
        Column(verticalArrangement = Arrangement.spacedBy(30.dp)) {
            Column {
                Text("SKĄD", color = TppColors.Muted, fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold)
                Text(origin, style = MaterialTheme.typography.titleLarge, color = TppColors.Ink, fontWeight = FontWeight.Bold)
            }
            Column {
                Text("DOKĄD", color = TppColors.Muted, fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold)
                Text(destination, style = MaterialTheme.typography.titleLarge, color = TppColors.Ink, fontWeight = FontWeight.Bold)
            }
        }
    }
}

@Composable
private fun ProfileScreen(
    state: DriverUiState,
    onRoutes: () -> Unit,
    onProfile: () -> Unit,
    onLogout: () -> Unit,
) {
    Scaffold(
        topBar = { StitchHeader() },
        bottomBar = {
            StitchBottomBar(
                activeScreen = DriverScreen.Profile,
                onRoutes = onRoutes,
                onProfile = onProfile,
            )
        },
        containerColor = TppColors.Surface,
    ) { padding ->
        Column(
            Modifier
                .fillMaxSize()
                .padding(padding)
                .padding(horizontal = 20.dp, vertical = 24.dp),
            verticalArrangement = Arrangement.spacedBy(18.dp),
        ) {
            Card(
                modifier = Modifier.fillMaxWidth(),
                colors = CardDefaults.cardColors(containerColor = Color.White),
                shape = RoundedCornerShape(8.dp),
                border = BorderStroke(1.dp, TppColors.Outline),
            ) {
                Column(Modifier.padding(24.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
                    Icon(Icons.Outlined.Person, contentDescription = null, tint = TppColors.Forest, modifier = Modifier.size(34.dp))
                    Text(
                        "Witaj ${state.driver?.displayName.orEmpty().ifBlank { "Kierowco" }}",
                        style = MaterialTheme.typography.headlineSmall,
                        fontWeight = FontWeight.Bold,
                        color = TppColors.Ink,
                    )
                }
            }
            Button(
                onClick = onLogout,
                modifier = Modifier.fillMaxWidth().height(56.dp),
                colors = ButtonDefaults.buttonColors(containerColor = TppColors.Forest),
                shape = RoundedCornerShape(4.dp),
            ) {
                Text("Wyloguj", fontWeight = FontWeight.Bold)
            }
        }
    }
}

@Composable
private fun StitchBottomBar(activeScreen: DriverScreen, onRoutes: () -> Unit, onProfile: () -> Unit) {
    Column(Modifier.fillMaxWidth().background(Color.White).navigationBarsPadding()) {
        HorizontalDivider(color = TppColors.Outline.copy(alpha = 0.9f), thickness = 2.dp)
        Row(
            Modifier.fillMaxWidth().height(82.dp),
            horizontalArrangement = Arrangement.SpaceEvenly,
            verticalAlignment = Alignment.CenterVertically,
        ) {
            BottomNavItem("Zlecenia", Icons.Outlined.LocalShipping, active = activeScreen == DriverScreen.Routes, onClick = onRoutes)
            BottomNavItem("Profil", Icons.Outlined.Person, active = activeScreen == DriverScreen.Profile, onClick = onProfile)
        }
    }
}

@Composable
private fun BottomNavItem(label: String, icon: ImageVector, active: Boolean, onClick: () -> Unit) {
    Column(
        modifier = Modifier.heightIn(min = 64.dp).clickable(onClick = onClick).padding(horizontal = 18.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
    ) {
        Icon(icon, contentDescription = label, tint = if (active) TppColors.Forest else TppColors.Muted)
        Spacer(Modifier.height(6.dp))
        Text(label, color = if (active) TppColors.Forest else TppColors.Muted, fontFamily = FontFamily.Monospace)
    }
}

private fun routeStatusColor(status: String): Color {
    val normalized = status.uppercase(Locale("pl", "PL"))
    return when {
        "NOW" in normalized -> TppColors.Navy
        "ZAK" in normalized || "GOT" in normalized -> TppColors.Forest
        "TRAK" in normalized || "START" in normalized -> Color(0xFFF5B800)
        else -> TppColors.Forest
    }
}

@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
@Composable
private fun DetailScreen(
    state: DriverUiState,
    onBack: () -> Unit,
    onUpload: (Uri, String, PhotoUploadMetadata) -> Unit,
    onPhoto: (RoutePhotoDto) -> Unit,
    onDeletePhoto: (RoutePhotoDto) -> Unit,
    onDeleteUpload: (PhotoUploadEntity) -> Unit,
    onRetryUpload: (PhotoUploadEntity) -> Unit,
    onRefresh: () -> Unit,
    onCompleteRoute: () -> Unit,
) {
    val context = LocalContext.current
    var cameraUri by remember { mutableStateOf<Uri?>(null) }
    var showCompleteDialog by remember { mutableStateOf(false) }
    var showPreciseLocationPermissionDialog by remember { mutableStateOf(false) }
    val pullRefreshState = rememberPullRefreshState(state.refreshing, onRefresh)
    val cameraLauncher = rememberLauncherForActivityResult(ActivityResultContracts.TakePicture()) { ok ->
        val capturedUri = cameraUri
        if (
            ok &&
            capturedUri != null &&
            canLaunchCameraWithLocationPolicy(
                requirePreciseLocation = state.requirePreciseLocationForPhotos,
                hasFineLocation = hasPermission(context, Manifest.permission.ACCESS_FINE_LOCATION),
            )
        ) {
            onUpload(capturedUri, "camera", cameraPhotoMetadata(context))
        }
        cameraUri = null
    }
    val cameraPermissionLauncher = rememberLauncherForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { grants ->
        val pendingUri = cameraUri
        val cameraGranted = grants[Manifest.permission.CAMERA] == true || hasPermission(context, Manifest.permission.CAMERA)
        val fineLocationGranted = grants[Manifest.permission.ACCESS_FINE_LOCATION] == true ||
            hasPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
        if (!canLaunchCameraWithLocationPolicy(state.requirePreciseLocationForPhotos, fineLocationGranted)) {
            showPreciseLocationPermissionDialog = true
            cameraUri = null
            return@rememberLauncherForActivityResult
        }
        if (cameraGranted && pendingUri != null) cameraLauncher.launch(pendingUri)
    }
    val pickerLauncher = rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
        if (uri != null) onUpload(uri, "gallery", PhotoUploadMetadata())
    }
    val route = state.selectedRoute
    val canManagePhotos = canManageRoutePhotos(state.selectedDate)

    Scaffold(
        topBar = {
            DetailHeader(onBack)
        },
        containerColor = TppColors.Surface,
    ) { padding ->
        if (route == null) return@Scaffold
        Box(Modifier.fillMaxSize().padding(padding).pullRefresh(pullRefreshState)) {
            LazyColumn(
                Modifier.fillMaxSize(),
                contentPadding = PaddingValues(16.dp),
                verticalArrangement = Arrangement.spacedBy(24.dp),
            ) {
                item { ManifestSection(route, onNavigate = { openNavigation(context, it) }) }
                item { OfflineStaleBanner(state) }
                item {
                    RouteCompletionSection(
                        route = route,
                        selectedDate = state.selectedDate,
                        allowRouteCompletion = state.allowRouteCompletion,
                        isOnline = state.isOnline,
                        completing = state.completingRoute,
                        feedback = state.feedback,
                        onComplete = { showCompleteDialog = true },
                    )
                }
                item {
                    CargoDocumentationSection(
                        photos = route.photos,
                        uploads = state.photoUploads,
                        deletingPhotoIds = state.deletingPhotoIds,
                        imageAuthHeader = state.imageAuthHeader,
                        allowGalleryUploads = state.allowGalleryUploads,
                        canManagePhotos = canManagePhotos,
                        onPhoto = onPhoto,
                        onDeletePhoto = onDeletePhoto,
                        onDeleteUpload = onDeleteUpload,
                        onRetryUpload = onRetryUpload,
                        onCamera = {
                            val newUri = createCameraUri(context)
                            cameraUri = newUri
                            val missingPermissions = cameraCapturePermissions(
                                context = context,
                                requirePreciseLocation = state.requirePreciseLocationForPhotos,
                            )
                            if (missingPermissions.isEmpty()) {
                                cameraLauncher.launch(newUri)
                            } else {
                                cameraPermissionLauncher.launch(missingPermissions)
                            }
                        },
                        onGallery = {
                            pickerLauncher.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
                        },
                    )
                }
                item { ErrorText(state.error) }
            }
            PullRefreshIndicator(
                refreshing = state.refreshing,
                state = pullRefreshState,
                modifier = Modifier.align(Alignment.TopCenter),
                backgroundColor = Color.White,
                contentColor = TppColors.Forest,
            )
        }
    }

    if (showCompleteDialog && route != null) {
        AlertDialog(
            onDismissRequest = { if (!state.completingRoute) showCompleteDialog = false },
            title = { Text("Zakończyć kurs?", color = TppColors.Ink, fontWeight = FontWeight.Bold) },
            text = { Text("Status kursu zostanie ustawiony jako zakończony.", color = TppColors.Muted) },
            confirmButton = {
                TextButton(
                    enabled = !state.completingRoute,
                    onClick = {
                        showCompleteDialog = false
                        onCompleteRoute()
                    },
                ) {
                    Text("Zakończ kurs", color = TppColors.Forest, fontWeight = FontWeight.Bold)
                }
            },
            dismissButton = {
                TextButton(
                    enabled = !state.completingRoute,
                    onClick = { showCompleteDialog = false },
                ) {
                    Text("Anuluj", color = TppColors.Muted)
                }
            },
        )
    }

    if (showPreciseLocationPermissionDialog) {
        AlertDialog(
            onDismissRequest = { showPreciseLocationPermissionDialog = false },
            title = { Text("Brak dokładnej lokalizacji", color = TppColors.Ink, fontWeight = FontWeight.Bold) },
            text = {
                Text(
                    "Aby zrobić zdjęcie, nadaj aplikacji uprawnienie do dokładnej lokalizacji.",
                    color = TppColors.Muted,
                )
            },
            confirmButton = {
                TextButton(
                    onClick = {
                        showPreciseLocationPermissionDialog = false
                        openAppSettings(context)
                    },
                ) {
                    Text("Przejdź do ustawień", color = TppColors.Forest, fontWeight = FontWeight.Bold)
                }
            },
            dismissButton = {
                TextButton(onClick = { showPreciseLocationPermissionDialog = false }) {
                    Text("Anuluj", color = TppColors.Muted)
                }
            },
        )
    }
}

@Composable
private fun DetailHeader(onBack: () -> Unit, title: String = "Szczegóły Trasy") {
    Column(Modifier.fillMaxWidth().background(TppColors.Surface).statusBarsPadding()) {
        Row(
            Modifier.fillMaxWidth().height(64.dp).padding(start = 8.dp, end = 16.dp),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            IconButton(onClick = onBack, modifier = Modifier.size(48.dp)) {
                Icon(Icons.Outlined.ArrowBack, contentDescription = "Wstecz", tint = TppColors.Ink)
            }
            Text(
                title,
                style = MaterialTheme.typography.titleLarge,
                fontWeight = FontWeight.SemiBold,
                color = TppColors.Ink,
                modifier = Modifier.padding(start = 4.dp),
            )
        }
        HorizontalDivider(color = TppColors.Outline, thickness = 1.dp)
    }
}

@Composable
private fun RouteCompletionSection(
    route: DriverRouteDto,
    selectedDate: String,
    allowRouteCompletion: Boolean,
    isOnline: Boolean,
    completing: Boolean,
    feedback: String?,
    onComplete: () -> Unit,
) {
    if (!allowRouteCompletion) return

    if (route.status == "ZAKOŃCZONA") {
        if (!feedback.isNullOrBlank()) {
            Card(
                colors = CardDefaults.cardColors(containerColor = Color(0xFFEAF7EF)),
                border = BorderStroke(1.dp, Color(0xFF9BD1AD)),
                shape = RoundedCornerShape(8.dp),
                modifier = Modifier.fillMaxWidth(),
            ) {
                Row(
                    Modifier.fillMaxWidth().padding(14.dp),
                    horizontalArrangement = Arrangement.spacedBy(10.dp),
                    verticalAlignment = Alignment.CenterVertically,
                ) {
                    Icon(Icons.Outlined.CheckCircle, contentDescription = null, tint = TppColors.Forest)
                    Text(feedback, color = TppColors.Forest, fontWeight = FontWeight.Bold)
                }
            }
        }
        return
    }

    if (!canCompleteRouteFromDriverApp(route, selectedDate)) return

    Card(
        colors = CardDefaults.cardColors(containerColor = Color.White),
        shape = RoundedCornerShape(8.dp),
        border = BorderStroke(1.dp, TppColors.Outline),
        modifier = Modifier.fillMaxWidth(),
    ) {
        Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(10.dp)) {
            Button(
                onClick = onComplete,
                enabled = isOnline && !completing,
                modifier = Modifier.fillMaxWidth().height(58.dp),
                colors = ButtonDefaults.buttonColors(
                    containerColor = TppColors.Forest,
                    disabledContainerColor = TppColors.Outline,
                ),
                shape = RoundedCornerShape(8.dp),
            ) {
                if (completing) {
                    CircularProgressIndicator(
                        modifier = Modifier.size(20.dp),
                        color = Color.White,
                        strokeWidth = 2.dp,
                    )
                } else {
                    Icon(Icons.Outlined.CheckCircle, contentDescription = null, tint = Color.White)
                }
                Spacer(Modifier.width(10.dp))
                Text(
                    if (completing) "Kończę kurs..." else "Zakończ kurs",
                    color = Color.White,
                    fontWeight = FontWeight.Bold,
                )
            }
            if (!isOnline) {
                Text(
                    "Kurs można zakończyć tylko po połączeniu z serwerem.",
                    color = TppColors.Muted,
                    fontFamily = FontFamily.Monospace,
                    style = MaterialTheme.typography.labelMedium,
                )
            }
        }
    }
}

private fun photoCountLabel(count: Int): String =
    when {
        count == 1 -> "zdjęcie"
        count in 2..4 -> "zdjęcia"
        else -> "zdjęć"
    }

@Composable
private fun ManifestSection(route: DriverRouteDto, onNavigate: (NavigationPointDto) -> Unit) {
    val stripColor = routeStatusColor(route.status)
    Card(
        colors = CardDefaults.cardColors(containerColor = Color.White),
        shape = RoundedCornerShape(8.dp),
        border = BorderStroke(1.dp, TppColors.Outline),
    ) {
        Column {
            Column(Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(14.dp)) {
                Row(
                    Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.SpaceBetween,
                    verticalAlignment = Alignment.Top,
                ) {
                    Column(verticalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.weight(1f)) {
                        StatusChip(route.status, stripColor)
                        Text(
                            "Zlecenie #${route.contractCode ?: route.id}",
                            style = MaterialTheme.typography.headlineSmall,
                            fontWeight = FontWeight.Bold,
                            color = TppColors.Ink,
                        )
                    }
                }
            }
            HorizontalDivider(color = TppColors.Outline.copy(alpha = 0.65f))
            RoutePointBlock(
                label = "Załadunek",
                title = route.originName.ifBlank { "Nie podano miejsca załadunku" },
                subtitle = route.contractorName,
                icon = Icons.Outlined.Factory,
                iconBackground = TppColors.Panel,
                iconTint = TppColors.ContainerGreen,
                navigationPoint = route.originNavigation,
                onNavigate = onNavigate,
            )
            HorizontalDivider(color = TppColors.Outline.copy(alpha = 0.45f))
            RoutePointBlock(
                label = "Rozładunek",
                title = route.destinationName.ifBlank { "Nie podano miejsca rozładunku" },
                subtitle = route.notes?.takeIf { it.isNotBlank() } ?: "Uwagi do zlecenia pojawią się tutaj.",
                icon = Icons.Outlined.LocationOn,
                iconBackground = TppColors.Panel.copy(alpha = 0.72f),
                iconTint = TppColors.Forest,
                framed = true,
                navigationPoint = route.destinationNavigation,
                onNavigate = onNavigate,
            )
            RouteFactsFooter(route)
        }
    }
}

@Composable
private fun RoutePointBlock(
    label: String,
    title: String,
    subtitle: String,
    icon: ImageVector,
    iconBackground: Color,
    iconTint: Color,
    framed: Boolean = false,
    navigationPoint: NavigationPointDto? = null,
    onNavigate: (NavigationPointDto) -> Unit = {},
) {
    Row(
        Modifier.fillMaxWidth().padding(20.dp),
        horizontalArrangement = Arrangement.spacedBy(16.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Box(
            Modifier.size(48.dp).background(iconBackground, RoundedCornerShape(24.dp)),
            contentAlignment = Alignment.Center,
        ) {
            Icon(icon, contentDescription = null, tint = iconTint, modifier = Modifier.size(26.dp))
        }
        Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(6.dp)) {
            Text(
                label.uppercase(Locale("pl", "PL")),
                color = TppColors.Muted,
                fontFamily = FontFamily.Monospace,
                fontWeight = FontWeight.Bold,
                style = MaterialTheme.typography.labelMedium,
            )
            Text(title, color = TppColors.Ink, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
            if (framed) {
                Box(Modifier.fillMaxWidth().background(TppColors.Panel, RoundedCornerShape(4.dp)).padding(12.dp)) {
                    Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
                        Text("UWAGI DO ZLECENIA", color = TppColors.Muted, fontFamily = FontFamily.Monospace, style = MaterialTheme.typography.labelSmall)
                        Text(subtitle, color = TppColors.Ink, fontWeight = FontWeight.SemiBold)
                    }
                }
            } else {
                Text(subtitle, color = TppColors.Muted, style = MaterialTheme.typography.bodyLarge)
            }
        }
        if (navigationPoint?.hasCoordinates() == true) {
            IconButton(
                onClick = { onNavigate(navigationPoint) },
                modifier = Modifier.size(48.dp).background(TppColors.Forest, RoundedCornerShape(24.dp)),
            ) {
                Icon(Icons.Outlined.Navigation, contentDescription = "Nawiguj", tint = Color.White)
            }
        }
    }
}

@Composable
private fun RouteFactsFooter(route: DriverRouteDto) {
    Column(Modifier.fillMaxWidth().background(TppColors.Panel).padding(20.dp), verticalArrangement = Arrangement.spacedBy(14.dp)) {
        Row(horizontalArrangement = Arrangement.spacedBy(14.dp), modifier = Modifier.fillMaxWidth()) {
            RouteFact("Typ ładunku", route.contractName ?: route.contractorName, Modifier.weight(1f))
            RouteFact("Pojazd", route.truckLabel(), Modifier.weight(1f))
        }
        Row(horizontalArrangement = Arrangement.spacedBy(14.dp), modifier = Modifier.fillMaxWidth()) {
            RouteFact("Nr kontraktu", route.contractCode ?: "#${route.id}", Modifier.weight(1f))
            RouteFact("Dystans", route.distanceLabel(), Modifier.weight(1f))
        }
    }
}

@Composable
private fun RouteFact(label: String, value: String, modifier: Modifier = Modifier) {
    Column(modifier, verticalArrangement = Arrangement.spacedBy(4.dp)) {
        Text(label, color = TppColors.Muted, fontFamily = FontFamily.Monospace, style = MaterialTheme.typography.labelSmall)
        Text(value.ifBlank { "-" }, color = TppColors.Ink, fontWeight = FontWeight.Bold)
    }
}

@Composable
private fun CargoDocumentationSection(
    photos: List<RoutePhotoDto>,
    uploads: List<PhotoUploadEntity>,
    deletingPhotoIds: Set<String>,
    imageAuthHeader: String?,
    allowGalleryUploads: Boolean,
    canManagePhotos: Boolean,
    onPhoto: (RoutePhotoDto) -> Unit,
    onDeletePhoto: (RoutePhotoDto) -> Unit,
    onDeleteUpload: (PhotoUploadEntity) -> Unit,
    onRetryUpload: (PhotoUploadEntity) -> Unit,
    onCamera: () -> Unit,
    onGallery: () -> Unit,
) {
    Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
        Column {
            Text("Dokumentacja ładunku", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, color = TppColors.Ink)
            Spacer(Modifier.height(8.dp))
            Box(Modifier.width(116.dp).height(3.dp).background(TppColors.ContainerGreen))
        }
        Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
            if (canManagePhotos) {
                if (allowGalleryUploads) {
                    CargoActionButton("Dodaj zdjęcie z galerii", Icons.Outlined.AddPhotoAlternate, TppColors.ContainerGreen, onGallery)
                }
                CargoActionButton("Zrób nowe zdjęcie", Icons.Outlined.CameraAlt, TppColors.Forest, onCamera)
            } else {
                Box(Modifier.fillMaxWidth().background(TppColors.Panel, RoundedCornerShape(4.dp)).padding(14.dp)) {
                    Text("Zdjęcia można dodawać tylko dla dzisiejszych kursów.", color = TppColors.Muted, fontFamily = FontFamily.Monospace)
                }
            }
        }
        PhotoGrid(photos, uploads, deletingPhotoIds, imageAuthHeader, onPhoto, onDeletePhoto, onDeleteUpload, onRetryUpload)
    }
}

@Composable
private fun CargoActionButton(label: String, icon: ImageVector, color: Color, onClick: () -> Unit) {
    Button(
        onClick = onClick,
        modifier = Modifier.fillMaxWidth().height(64.dp),
        colors = ButtonDefaults.buttonColors(containerColor = color),
        shape = RoundedCornerShape(8.dp),
    ) {
        Icon(icon, contentDescription = null, tint = Color.White)
        Spacer(Modifier.width(10.dp))
        Text(label, color = Color.White, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold)
    }
}

@Composable
private fun PhotoGrid(
    photos: List<RoutePhotoDto>,
    uploads: List<PhotoUploadEntity>,
    deletingPhotoIds: Set<String>,
    imageAuthHeader: String?,
    onPhoto: (RoutePhotoDto) -> Unit,
    onDeletePhoto: (RoutePhotoDto) -> Unit,
    onDeleteUpload: (PhotoUploadEntity) -> Unit,
    onRetryUpload: (PhotoUploadEntity) -> Unit,
) {
    var pendingDelete by remember { mutableStateOf<PhotoDeleteTarget?>(null) }
    val items = (uploads.map { PhotoGridItem.Upload(it) } + photos.map { PhotoGridItem.Server(it) })
        .sortedByDescending { it.sortEpochMillis }
    val visibleAttachmentCount = visiblePhotoAttachmentCount(photos.size, uploads.size)

    Card(
        colors = CardDefaults.cardColors(containerColor = Color.White),
        shape = RoundedCornerShape(8.dp),
        border = BorderStroke(1.dp, TppColors.Outline),
    ) {
        Column(Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
            Text(
                "Załączone zdjęcia ($visibleAttachmentCount)",
                color = TppColors.Muted,
                fontFamily = FontFamily.Monospace,
                fontWeight = FontWeight.SemiBold,
            )
            if (items.isEmpty()) {
                EmptyPhotoState()
            } else {
                Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
                    repeat(inlinePhotoGridRows(items.size)) { rowIndex ->
                        val startIndex = rowIndex * 2
                        val rowItems = items.subList(startIndex, minOf(startIndex + 2, items.size))
                        Row(horizontalArrangement = Arrangement.spacedBy(12.dp), modifier = Modifier.fillMaxWidth()) {
                            rowItems.forEach { item ->
                                when (item) {
                                    is PhotoGridItem.Server -> PhotoTile(
                                        photo = item.photo,
                                        imageAuthHeader = imageAuthHeader,
                                        isDeleting = item.photo.id in deletingPhotoIds,
                                        onPhoto = onPhoto,
                                        onDeletePhoto = { pendingDelete = PhotoDeleteTarget.Server(it) },
                                        modifier = Modifier.weight(1f),
                                    )
                                    is PhotoGridItem.Upload -> PendingPhotoTile(
                                        upload = item.upload,
                                        isDeleting = item.upload.serverPhotoId?.let { it in deletingPhotoIds } == true,
                                        onPhoto = { upload -> localUploadPreviewPhoto(upload)?.let(onPhoto) },
                                        onDelete = { pendingDelete = PhotoDeleteTarget.Upload(it) },
                                        onRetry = onRetryUpload,
                                        modifier = Modifier.weight(1f),
                                    )
                                }
                            }
                            if (rowItems.size == 1) {
                                Spacer(Modifier.weight(1f))
                            }
                        }
                    }
                }
            }
        }
    }

    pendingDelete?.let { target ->
        AlertDialog(
            onDismissRequest = { pendingDelete = null },
            title = { Text("Usunąć zdjęcie?", color = TppColors.Ink, fontWeight = FontWeight.Bold) },
            text = { Text("Czy na pewno chcesz usunąć to zdjęcie?", color = TppColors.Muted) },
            confirmButton = {
                TextButton(
                    onClick = {
                        pendingDelete = null
                        when (target) {
                            is PhotoDeleteTarget.Server -> onDeletePhoto(target.photo)
                            is PhotoDeleteTarget.Upload -> onDeleteUpload(target.upload)
                        }
                    },
                ) {
                    Text("Usuń", color = TppColors.Error, fontWeight = FontWeight.Bold)
                }
            },
            dismissButton = {
                TextButton(onClick = { pendingDelete = null }) {
                    Text("Anuluj", color = TppColors.Muted)
                }
            },
        )
    }
}

private sealed class PhotoGridItem(open val sortEpochMillis: Long) {
    data class Server(val photo: RoutePhotoDto) : PhotoGridItem(
        routePhotoSortEpochMillis(photo.createdAt, photo.takenAt, fallback = 0L),
    )

    data class Upload(val upload: PhotoUploadEntity) : PhotoGridItem(upload.createdAtEpochMillis)
}

private sealed class PhotoDeleteTarget {
    data class Server(val photo: RoutePhotoDto) : PhotoDeleteTarget()
    data class Upload(val upload: PhotoUploadEntity) : PhotoDeleteTarget()
}

@Composable
private fun PhotoTile(
    photo: RoutePhotoDto,
    imageAuthHeader: String?,
    isDeleting: Boolean,
    onPhoto: (RoutePhotoDto) -> Unit,
    onDeletePhoto: (RoutePhotoDto) -> Unit,
    modifier: Modifier = Modifier,
) {
    Box(modifier.aspectRatio(1f)) {
        AsyncImage(
            model = imageRequest(photo.url, imageAuthHeader),
            contentDescription = "Zdjecie ladunku",
            contentScale = ContentScale.Crop,
            modifier = Modifier.fillMaxSize().clickable { onPhoto(photo) },
        )
        if (isDeleting) {
            Box(
                Modifier.fillMaxSize().background(Color.White.copy(alpha = 0.72f)),
                contentAlignment = Alignment.Center,
            ) {
                Text("Usuwam", color = TppColors.Muted, fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold)
            }
        }
        if (photo.canDelete) {
            IconButton(
                onClick = { onDeletePhoto(photo) },
                enabled = !isDeleting,
                modifier = Modifier
                    .align(Alignment.TopEnd)
                    .padding(8.dp)
                    .size(40.dp)
                    .background(TppColors.Surface.copy(alpha = 0.96f), RoundedCornerShape(20.dp))
                    .border(1.dp, TppColors.Error.copy(alpha = 0.35f), RoundedCornerShape(20.dp)),
            ) {
                Icon(
                    Icons.Outlined.Delete,
                    contentDescription = "Usuń zdjęcie",
                    tint = TppColors.Error,
                    modifier = Modifier.size(19.dp),
                )
            }
        }
    }
}

@Composable
private fun PendingPhotoTile(
    upload: PhotoUploadEntity,
    isDeleting: Boolean,
    onPhoto: (PhotoUploadEntity) -> Unit,
    onDelete: (PhotoUploadEntity) -> Unit,
    onRetry: (PhotoUploadEntity) -> Unit,
    modifier: Modifier = Modifier,
) {
    val status = upload.statusType
    val canDeleteServerPhoto = confirmedUploadServerPhotoId(upload.status, upload.serverPhotoId) != null
    val hasFailureDetails = canShowPhotoUploadFailureDetails(upload.status, upload.lastError)
    var showFailureDetails by remember { mutableStateOf(false) }
    if (showFailureDetails) {
        PhotoUploadFailureDialog(
            details = upload.lastError.orEmpty(),
            onDismiss = { showFailureDetails = false },
        )
    }
    Box(modifier.aspectRatio(1f).background(TppColors.Panel, RoundedCornerShape(4.dp))) {
        AsyncImage(
            model = File(upload.localPath),
            contentDescription = "Zdjęcie oczekujące na zapis",
            contentScale = ContentScale.Crop,
            modifier = Modifier.fillMaxSize().clickable { onPhoto(upload) },
        )
        Box(Modifier.fillMaxSize().background(Color.Black.copy(alpha = 0.34f)))
        if (isDeleting) {
            Box(
                Modifier.fillMaxSize().background(Color.White.copy(alpha = 0.72f)),
                contentAlignment = Alignment.Center,
            ) {
                Text("Usuwam", color = TppColors.Muted, fontFamily = FontFamily.Monospace, fontWeight = FontWeight.Bold)
            }
        }
        if (canDeleteServerPhoto) {
            IconButton(
                onClick = { onDelete(upload) },
                enabled = !isDeleting,
                modifier = Modifier
                    .align(Alignment.TopEnd)
                    .padding(8.dp)
                    .size(40.dp)
                    .background(TppColors.Surface.copy(alpha = 0.96f), RoundedCornerShape(20.dp))
                    .border(1.dp, TppColors.Error.copy(alpha = 0.35f), RoundedCornerShape(20.dp)),
            ) {
                Icon(
                    Icons.Outlined.Delete,
                    contentDescription = "Usuń zdjęcie",
                    tint = TppColors.Error,
                    modifier = Modifier.size(19.dp),
                )
            }
        }
        if (hasFailureDetails) {
            IconButton(
                onClick = { showFailureDetails = true },
                enabled = !isDeleting,
                modifier = Modifier
                    .align(Alignment.TopStart)
                    .padding(8.dp)
                    .size(40.dp)
                    .background(TppColors.Surface.copy(alpha = 0.96f), RoundedCornerShape(20.dp))
                    .border(1.dp, TppColors.Error.copy(alpha = 0.35f), RoundedCornerShape(20.dp)),
            ) {
                Icon(
                    Icons.Outlined.Info,
                    contentDescription = "Pokaż szczegóły błędu wysłania",
                    tint = TppColors.Error,
                    modifier = Modifier.size(20.dp),
                )
            }
        }
        if (canRetryPhotoUpload(upload.status)) {
            IconButton(
                onClick = { onRetry(upload) },
                enabled = !isDeleting,
                modifier = Modifier
                    .align(Alignment.TopEnd)
                    .padding(8.dp)
                    .size(40.dp)
                    .background(TppColors.Surface.copy(alpha = 0.96f), RoundedCornerShape(20.dp))
                    .border(1.dp, TppColors.Forest.copy(alpha = 0.35f), RoundedCornerShape(20.dp)),
            ) {
                Icon(
                    Icons.Outlined.Refresh,
                    contentDescription = "Ponów wysłanie zdjęcia",
                    tint = TppColors.Forest,
                    modifier = Modifier.size(20.dp),
                )
            }
        }
        Column(
            Modifier.align(Alignment.BottomStart).fillMaxWidth().background(Color.White.copy(alpha = 0.94f)).padding(10.dp),
            verticalArrangement = Arrangement.spacedBy(8.dp),
        ) {
            Text(
                upload.lastError?.lineSequence()?.firstOrNull { it.isNotBlank() }?.takeIf { hasFailureDetails } ?: status.label,
                color = if (hasFailureDetails) TppColors.Error else TppColors.Ink,
                fontFamily = FontFamily.Monospace,
                fontWeight = FontWeight.Bold,
                style = MaterialTheme.typography.labelMedium,
            )
            if (status == PhotoUploadStatus.Uploading || status == PhotoUploadStatus.Verifying) {
                androidx.compose.material3.LinearProgressIndicator(
                    progress = { (upload.progress.coerceIn(0, 100) / 100f) },
                    modifier = Modifier.fillMaxWidth(),
                    color = TppColors.Forest,
                    trackColor = TppColors.Outline.copy(alpha = 0.4f),
                )
            }
        }
    }
}

@Composable
private fun EmptyPhotoState() {
    Box(
        Modifier.fillMaxWidth().height(132.dp).background(TppColors.Panel, RoundedCornerShape(4.dp)),
        contentAlignment = Alignment.Center,
    ) {
        Text("Brak załączonych zdjęć", color = TppColors.Muted, fontFamily = FontFamily.Monospace)
    }
}

private fun DriverRouteDto.truckLabel(): String {
    val registration = truck?.registration.orEmpty()
    val trailerRegistration = truck?.trailerRegistration.orEmpty()
    val name = truck?.name.orEmpty()

    val vehicle = when {
        registration.isNotBlank() && name.isNotBlank() -> "$registration ($name)"
        registration.isNotBlank() -> registration
        name.isNotBlank() -> name
        else -> "-"
    }

    return if (trailerRegistration.isNotBlank()) {
        "$vehicle\nNaczepa: $trailerRegistration"
    } else {
        vehicle
    }
}

private fun DriverRouteDto.distanceLabel(): String =
    distanceKm?.takeIf { it > 0 }?.let { "%.1f km".format(Locale.US, it) } ?: "-"

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun PhotoScreen(state: DriverUiState, onBack: () -> Unit) {
    val photo = state.selectedPhoto
    Scaffold(
        topBar = {
            TopAppBar(
                modifier = Modifier.statusBarsPadding(),
                title = { Text("Podglad zdjecia") },
                navigationIcon = { IconButton(onClick = onBack) { Icon(Icons.Outlined.ArrowBack, contentDescription = "Wstecz") } },
                colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Black, titleContentColor = Color.White, navigationIconContentColor = Color.White),
            )
        },
        containerColor = Color.Black,
    ) { padding ->
        AsyncImage(
            model = imageRequest(photo?.url, state.imageAuthHeader),
            contentDescription = "Zdjecie ladunku",
            contentScale = ContentScale.Fit,
            modifier = Modifier.fillMaxSize().padding(padding),
        )
    }
}

@Composable
private fun PrimaryButton(label: String, onClick: () -> Unit) {
    Button(
        onClick = onClick,
        modifier = Modifier.fillMaxWidth().height(56.dp),
        colors = ButtonDefaults.buttonColors(containerColor = TppColors.Forest),
        shape = RoundedCornerShape(4.dp),
    ) { Text(label, fontWeight = FontWeight.Bold) }
}

@Composable
private fun EmptyState(text: String) {
    Card(colors = CardDefaults.cardColors(containerColor = Color.White), shape = RoundedCornerShape(8.dp)) {
        Text(text, modifier = Modifier.padding(24.dp), color = TppColors.Muted)
    }
}

@Composable
private fun ErrorText(error: String?) {
    if (!error.isNullOrBlank()) Text(error, color = TppColors.Error, modifier = Modifier.padding(vertical = 8.dp))
}

@Composable
private fun OfflineStaleBanner(state: DriverUiState) {
    if (state.isOnline && !state.isStale) return

    val syncLabel = state.lastSuccessfulSyncAtEpochMillis?.let(::formatSyncTime) ?: "brak zapisanej synchronizacji"
    Card(
        colors = CardDefaults.cardColors(containerColor = Color(0xFFFFF7E6)),
        border = BorderStroke(1.dp, Color(0xFFE6B85C)),
        shape = RoundedCornerShape(6.dp),
        modifier = Modifier.fillMaxWidth(),
    ) {
        Text(
            text = if (state.isOnline) {
                "Dane mogą być nieaktualne. Ostatnia synchronizacja: $syncLabel."
            } else {
                "Brak połączenia z serwerem. Dane z $syncLabel mogą być nieaktualne."
            },
            color = Color(0xFF5F4200),
            fontWeight = FontWeight.SemiBold,
            modifier = Modifier.padding(12.dp),
        )
    }
}

private fun formatSyncTime(epochMillis: Long): String =
    runCatching {
        DateTimeFormatter.ofPattern("HH:mm", Locale("pl", "PL"))
            .format(Instant.ofEpochMilli(epochMillis).atZone(ZoneId.systemDefault()))
    }.getOrDefault("nieznana godzina")

private fun polishPhoneDigits(value: String): String {
    val digits = value.filter(Char::isDigit)
    val withoutCountryCode = if (digits.length > 9 && digits.startsWith("48")) digits.drop(2) else digits

    return withoutCountryCode.take(9)
}

private fun createCameraUri(context: Context): Uri {
    val dir = File(context.cacheDir, "camera").apply { mkdirs() }
    val file = File.createTempFile("ladunek-", ".jpg", dir)
    return FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file)
}

private fun NavigationPointDto.hasCoordinates(): Boolean = latitude != null && longitude != null

private fun openNavigation(context: Context, point: NavigationPointDto) {
    val latitude = point.latitude ?: return
    val longitude = point.longitude ?: return
    val googleMapsIntent = Intent(Intent.ACTION_VIEW, Uri.parse("google.navigation:q=$latitude,$longitude"))
        .setPackage("com.google.android.apps.maps")

    if (googleMapsIntent.resolveActivity(context.packageManager) != null) {
        context.startActivity(googleMapsIntent)
        return
    }

    val label = Uri.encode(point.label?.takeIf { it.isNotBlank() } ?: "Cel")
    val geoIntent = Intent(Intent.ACTION_VIEW, Uri.parse("geo:0,0?q=$latitude,$longitude($label)"))
    if (geoIntent.resolveActivity(context.packageManager) != null) {
        context.startActivity(geoIntent)
    }
}

private fun cameraCapturePermissions(context: Context, requirePreciseLocation: Boolean): Array<String> = buildList {
    if (!hasPermission(context, Manifest.permission.CAMERA)) {
        add(Manifest.permission.CAMERA)
    }
    if (requirePreciseLocation) {
        if (!hasPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)) {
            add(Manifest.permission.ACCESS_FINE_LOCATION)
        }
    } else if (!hasLocationPermission(context)) {
        add(Manifest.permission.ACCESS_FINE_LOCATION)
        add(Manifest.permission.ACCESS_COARSE_LOCATION)
    }
}.toTypedArray()

private fun cameraPhotoMetadata(context: Context): PhotoUploadMetadata {
    val location = bestLastKnownLocation(context)

    return PhotoUploadMetadata(
        takenAt = Instant.now().toString(),
        latitude = location?.latitude,
        longitude = location?.longitude,
        locationAccuracyMeters = location?.accuracy?.toDouble(),
    )
}

private fun hasPermission(context: Context, permission: String): Boolean =
    ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED

private fun hasLocationPermission(context: Context): Boolean =
    hasPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) ||
        hasPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)

private fun openAppSettings(context: Context) {
    val intent = Intent(
        Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
        Uri.fromParts("package", context.packageName, null),
    )
    context.startActivity(intent)
}

private fun bestLastKnownLocation(context: Context): Location? {
    if (!hasLocationPermission(context)) return null

    val manager = context.getSystemService(Context.LOCATION_SERVICE) as? LocationManager ?: return null
    val providers = listOf(
        LocationManager.GPS_PROVIDER,
        LocationManager.NETWORK_PROVIDER,
        LocationManager.PASSIVE_PROVIDER,
    )

    return providers.mapNotNull { provider ->
        runCatching {
            if (manager.isProviderEnabled(provider) || provider == LocationManager.PASSIVE_PROVIDER) {
                manager.getLastKnownLocation(provider)
            } else {
                null
            }
        }.getOrNull()
    }.maxByOrNull { it.time }
}

@Composable
private fun SmsUserConsentEffect(enabled: Boolean, onCode: (String) -> Unit) {
    val context = LocalContext.current
    val launcher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            val message = result.data?.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE).orEmpty()
            OtpCodeExtractor.extract(message)?.let(onCode)
        }
    }

    DisposableEffect(enabled, context) {
        if (!enabled) return@DisposableEffect onDispose { }

        SmsRetriever.getClient(context).startSmsUserConsent(null)

        val receiver = object : BroadcastReceiver() {
            override fun onReceive(receiverContext: Context?, intent: Intent?) {
                if (intent?.action != SmsRetriever.SMS_RETRIEVED_ACTION) return

                val status = intent.smsRetrieverStatus() ?: return
                if (status.statusCode != CommonStatusCodes.SUCCESS) return

                intent.smsConsentIntent()?.let(launcher::launch)
            }
        }

        val filter = IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION)
        ContextCompat.registerReceiver(
            context,
            receiver,
            filter,
            SmsRetriever.SEND_PERMISSION,
            null,
            ContextCompat.RECEIVER_EXPORTED,
        )

        onDispose {
            runCatching { context.unregisterReceiver(receiver) }
        }
    }
}

private fun Intent.smsRetrieverStatus(): Status? {
    val extras = extras ?: return null
    @Suppress("DEPRECATION")
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        extras.getParcelable(SmsRetriever.EXTRA_STATUS, Status::class.java)
    } else {
        extras.getParcelable(SmsRetriever.EXTRA_STATUS)
    }
}

private fun Intent.smsConsentIntent(): Intent? {
    val extras = extras ?: return null
    @Suppress("DEPRECATION")
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        extras.getParcelable(SmsRetriever.EXTRA_CONSENT_INTENT, Intent::class.java)
    } else {
        extras.getParcelable(SmsRetriever.EXTRA_CONSENT_INTENT)
    }
}

@Composable
private fun imageRequest(url: String?, authorization: String?): ImageRequest {
    val context = LocalContext.current
    return ImageRequest.Builder(context)
        .data(url)
        .apply {
            if (!authorization.isNullOrBlank()) addHeader("Authorization", authorization)
        }
        .crossfade(true)
        .build()
}
