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 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.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.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
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.Factory
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.ExperimentalMaterialApi
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
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.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.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.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.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.format.DateTimeFormatter
import kotlin.math.ceil
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.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.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.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,
                onRoute = viewModel::openRoute,
            )
            DriverScreen.Profile -> ProfileScreen(state, viewModel::refreshRoutes, viewModel::openProfile, viewModel::logout)
            DriverScreen.Detail -> DetailScreen(state, viewModel::back, viewModel::uploadPhoto, viewModel::openPhoto, viewModel::refreshSelectedRoute)
            DriverScreen.Photo -> PhotoScreen(state, viewModel::back)
        }

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

@Composable
private fun PhoneScreen(state: DriverUiState, onSubmit: (String) -> Unit) {
    var phone by remember { mutableStateOf(state.phone) }
    AuthShell(title = "TPP Kierowca", subtitle = "Zaloguj sie numerem telefonu kierowcy.") {
        OutlinedTextField(
            value = phone,
            onValueChange = { phone = it },
            label = { Text("Numer telefonu") },
            leadingIcon = { Icon(Icons.Outlined.Phone, contentDescription = null) },
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone),
            modifier = Modifier.fillMaxWidth(),
            singleLine = true,
        )
        ErrorText(state.error)
        Spacer(Modifier.height(if (state.error.isNullOrBlank()) 18.dp else 8.dp))
        PrimaryButton("Wyslij kod") { onSubmit(phone) }
    }
}

@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,
    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,
                    )
                }
                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 StitchHeader() {
    Column(Modifier.fillMaxWidth().background(Color.White).statusBarsPadding()) {
        Box(
            Modifier.fillMaxWidth().height(104.dp).padding(horizontal = 24.dp),
            contentAlignment = Alignment.Center,
        ) {
            TppLogo()
        }
        HorizontalDivider(color = TppColors.Outline.copy(alpha = 0.8f), thickness = 2.dp)
    }
}

@Composable
private fun TppLogo() {
    Image(
        painter = painterResource(R.drawable.tpp5),
        contentDescription = "TPP",
        contentScale = ContentScale.Fit,
        modifier = Modifier.width(142.dp).height(50.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)) }
    }

    LazyRow(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,
    onRefresh: () -> Unit,
) {
    val context = LocalContext.current
    var cameraUri by remember { mutableStateOf<Uri?>(null) }
    val pullRefreshState = rememberPullRefreshState(state.refreshing, onRefresh)
    val cameraLauncher = rememberLauncherForActivityResult(ActivityResultContracts.TakePicture()) { ok ->
        val capturedUri = cameraUri
        if (ok && capturedUri != null) 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)
        if (cameraGranted && pendingUri != null) cameraLauncher.launch(pendingUri)
    }
    val pickerLauncher = rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
        if (uri != null) onUpload(uri, "gallery", PhotoUploadMetadata())
    }
    val route = state.selectedRoute

    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 {
                    CargoDocumentationSection(
                        photos = route.photos,
                        imageAuthHeader = state.imageAuthHeader,
                        allowGalleryUploads = state.allowGalleryUploads,
                        onPhoto = onPhoto,
                        onCamera = {
                            val newUri = createCameraUri(context)
                            cameraUri = newUri
                            val missingPermissions = cameraCapturePermissions(context)
                            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,
            )
        }
    }
}

@Composable
private fun DetailHeader(onBack: () -> Unit) {
    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(
                "Szczegóły Trasy",
                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 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>,
    imageAuthHeader: String?,
    allowGalleryUploads: Boolean,
    onPhoto: (RoutePhotoDto) -> 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 (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)
        }
        PhotoGrid(photos, imageAuthHeader, onPhoto)
    }
}

@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>,
    imageAuthHeader: String?,
    onPhoto: (RoutePhotoDto) -> Unit,
) {
    val rows = ceil((photos.size.coerceAtLeast(1) / 2f).toDouble()).toInt()
    val gridHeight = (rows * 172).coerceIn(172, 520).dp

    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 (${photos.size})",
                color = TppColors.Muted,
                fontFamily = FontFamily.Monospace,
                fontWeight = FontWeight.SemiBold,
            )
            if (photos.isEmpty()) {
                EmptyPhotoState()
            } else {
                LazyVerticalGrid(
                    columns = GridCells.Fixed(2),
                    modifier = Modifier.height(gridHeight),
                    verticalArrangement = Arrangement.spacedBy(12.dp),
                    horizontalArrangement = Arrangement.spacedBy(12.dp),
                ) {
                    items(photos, key = { it.id }) { photo ->
                        AsyncImage(
                            model = imageRequest(photo.url, imageAuthHeader),
                            contentDescription = "Zdjecie ladunku",
                            contentScale = ContentScale.Crop,
                            modifier = Modifier.aspectRatio(1f).clickable { onPhoto(photo) },
                        )
                    }
                }
            }
        }
    }
}

@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))
}

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): Array<String> = buildList {
    if (!hasPermission(context, Manifest.permission.CAMERA)) {
        add(Manifest.permission.CAMERA)
    }
    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 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()
}
