package pl.firmatpp.kierowca.ui

import java.io.File
import java.time.DayOfWeek
import java.time.LocalDate
import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
import pl.firmatpp.kierowca.data.model.DriverRouteDto
import pl.firmatpp.kierowca.data.model.DispatchSheetReminderDto
import pl.firmatpp.kierowca.data.model.RoutePhotoDto
import pl.firmatpp.kierowca.data.upload.PhotoUploadEntity
import pl.firmatpp.kierowca.data.upload.RouteActionEntity
import pl.firmatpp.kierowca.data.upload.RouteActionStatus
import pl.firmatpp.kierowca.data.upload.RouteActionType

private val shortDateFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("dd.MM")

enum class RouteFlowStepState {
    Todo,
    LocalComplete,
    Syncing,
    Confirmed,
    NeedsAttention,
}

enum class RouteLifecycleAction {
    Start,
    Finish,
}

data class RouteFlowStepUi(
    val key: String,
    val title: String,
    val state: RouteFlowStepState,
    val stateLabel: String,
)

data class RouteLifecycleActionUi(
    val label: String,
    val action: RouteLifecycleAction,
)

data class RouteSyncCalloutUi(
    val text: String,
    val isConflict: Boolean,
)

fun canManageRoutePhotos(selectedDate: String, today: LocalDate = LocalDate.now()): Boolean =
    runCatching { LocalDate.parse(selectedDate).isEqual(today) }.getOrDefault(false)

fun canCompleteRouteFromDriverApp(
    route: DriverRouteDto,
    selectedDate: String,
    today: LocalDate = LocalDate.now(),
): Boolean =
    canManageRoutePhotos(selectedDate, today)
        && route.status in setOf("ZAPLANOWANA", "W TRAKCIE")

fun canStartRouteFromDriverApp(
    route: DriverRouteDto,
    selectedDate: String,
    today: LocalDate = LocalDate.now(),
): Boolean =
    canManageRoutePhotos(selectedDate, today)
        && route.status == "ZAPLANOWANA"

fun canFinishRouteFromDriverApp(
    route: DriverRouteDto,
    selectedDate: String,
    today: LocalDate = LocalDate.now(),
): Boolean =
    canManageRoutePhotos(selectedDate, today)
        && route.status == "W TRAKCIE"

fun canSubmitRouteStageForm(weightText: String, serverPhotoCount: Int, localUploadCount: Int): Boolean {
    return routeStageSubmitBlocker(weightText, serverPhotoCount, localUploadCount) == null
}

fun loadingPhotosSubmitBlocker(serverPhotoCount: Int, localUploadCount: Int): String? =
    if (visiblePhotoAttachmentCount(serverPhotoCount, localUploadCount) <= 0) {
        "Dodaj co najmniej jedno zdjęcie załadunku."
    } else {
        null
    }

fun loadingWeightSubmitBlocker(weightText: String, serverPhotoCount: Int, localUploadCount: Int): String? {
    val trimmed = weightText.trim()
    val normalizedWeight = trimmed.replace(',', '.').toDoubleOrNull()

    return when {
        trimmed.isBlank() -> "Podaj wagę."
        normalizedWeight == null -> "Podaj poprawną wagę."
        normalizedWeight <= 0.0 -> "Waga musi być większa od zera."
        loadingPhotosSubmitBlocker(serverPhotoCount, localUploadCount) != null -> "Dodaj co najmniej jedno zdjęcie załadunku."
        else -> null
    }
}

fun routeStageSubmitBlocker(weightText: String, serverPhotoCount: Int, localUploadCount: Int): String? {
    val trimmed = weightText.trim()
    val normalizedWeight = trimmed.replace(',', '.').toDoubleOrNull()

    return when {
        trimmed.isBlank() -> "Podaj wagę."
        normalizedWeight == null -> "Podaj poprawną wagę."
        normalizedWeight <= 0.0 -> "Waga musi być większa od zera."
        visiblePhotoAttachmentCount(serverPhotoCount, localUploadCount) <= 0 -> "Dodaj co najmniej jedno zdjęcie etapu."
        else -> null
    }
}

fun routeLifecyclePrimaryAction(
    route: DriverRouteDto,
    selectedDate: String,
    today: LocalDate = LocalDate.now(),
): RouteLifecycleActionUi? =
    when {
        canStartRouteFromDriverApp(route, selectedDate, today) -> RouteLifecycleActionUi("Rozpocznij kurs", RouteLifecycleAction.Start)
        canFinishRouteFromDriverApp(route, selectedDate, today) -> RouteLifecycleActionUi("Zakończ kurs", RouteLifecycleAction.Finish)
        else -> null
    }

fun routeSyncCallout(actions: List<RouteActionEntity>): RouteSyncCalloutUi? {
    val unresolved = actions
        .filter { it.status in routeActionVisibleStatuses }
        .maxByOrNull { it.createdAtEpochMillis }
        ?: return null
    val isConflict = routeActionNeedsAttention(unresolved.status)

    return RouteSyncCalloutUi(
        text = if (isConflict) {
            "Kurs wymaga obsługi. Dane wpisane w telefonie zostały zachowane."
        } else {
            when (unresolved.status) {
                RouteActionStatus.WaitingForPhotos -> "Czekam na wysłanie zdjęć etapu, potem wyślę zmianę statusu."
                RouteActionStatus.Pending,
                RouteActionStatus.Syncing -> "Trwa wysyłanie zmiany statusu kursu."
                RouteActionStatus.FailedRetryable -> "Nie udało się wysłać. Aplikacja spróbuje ponownie."
                else -> "Zmiana zapisana w telefonie. Aplikacja wyśle ją automatycznie."
            }
        },
        isConflict = isConflict,
    )
}

fun routeFlowSteps(route: DriverRouteDto, actions: List<RouteActionEntity>): List<RouteFlowStepUi> {
    val startOverride = routeActionStepState(actions, RouteActionType.Start)
    val finishOverride = routeActionStepState(actions, RouteActionType.Finish)
    val loadingPhotoCount = routePhotosForStage(route.photos, "loading").size
    val loadingPhotosState = when {
        route.status == "ZAKOŃCZONA" -> RouteFlowStepState.Confirmed
        route.status == "W TRAKCIE" -> RouteFlowStepState.Confirmed
        route.loadingWeight != null -> RouteFlowStepState.LocalComplete
        loadingPhotoCount > 0 -> RouteFlowStepState.LocalComplete
        else -> RouteFlowStepState.Todo
    }
    val loadingWeightState = startOverride ?: when {
        route.status == "ZAKOŃCZONA" -> RouteFlowStepState.Confirmed
        route.status == "W TRAKCIE" -> RouteFlowStepState.Confirmed
        route.loadingWeight != null -> RouteFlowStepState.LocalComplete
        else -> RouteFlowStepState.Todo
    }
    val transitState = when {
        route.status == "ZAKOŃCZONA" -> RouteFlowStepState.Confirmed
        finishOverride == RouteFlowStepState.NeedsAttention -> RouteFlowStepState.LocalComplete
        finishOverride == RouteFlowStepState.Syncing -> RouteFlowStepState.LocalComplete
        route.status == "W TRAKCIE" -> RouteFlowStepState.LocalComplete
        else -> RouteFlowStepState.Todo
    }
    val unloadingState = finishOverride ?: when {
        route.status == "ZAKOŃCZONA" -> RouteFlowStepState.Confirmed
        route.unloadingWeight != null -> RouteFlowStepState.LocalComplete
        else -> RouteFlowStepState.Todo
    }

    return listOf(
        RouteFlowStepUi("loading_photos", "Zdjęcia załadunku", loadingPhotosState, routeFlowStateLabel(loadingPhotosState)),
        RouteFlowStepUi("loading_weight", "Waga załadunku", loadingWeightState, routeFlowStateLabel(loadingWeightState)),
        RouteFlowStepUi("transit", "W trasie", transitState, routeFlowStateLabel(transitState)),
        RouteFlowStepUi("unloading", "Rozładunek", unloadingState, routeFlowStateLabel(unloadingState)),
    )
}

fun routePhotosForStage(photos: List<RoutePhotoDto>?, stage: String): List<RoutePhotoDto> {
    val normalized = normalizedRoutePhotoStage(stage)
    return photos.orEmpty().filter { normalizedRoutePhotoStage(it.stage) == normalized }
}

fun normalizedRoutePhotoStage(stage: String?): String =
    when (stage) {
        "loading", "unloading" -> stage
        else -> "other"
    }

fun activeRouteUploadIntervalSeconds(
    isMoving: Boolean,
    isUrbanOrTurning: Boolean = false,
    isNearDestination: Boolean = false,
    isHighwayStable: Boolean = false,
    isLikelyOffRoute: Boolean = false,
): Int =
    when {
        isLikelyOffRoute -> 3
        !isMoving -> 45
        isUrbanOrTurning || isNearDestination -> 5
        isHighwayStable -> 20
        else -> 20
    }

fun activeRouteNotificationTarget(arrived: Boolean): DriverScreen =
    if (arrived) DriverScreen.FinishRoute else DriverScreen.Detail

private fun routeActionStepState(actions: List<RouteActionEntity>, actionType: String): RouteFlowStepState? {
    val action = actions
        .filter { it.action == actionType && it.status in routeActionVisibleStatuses }
        .maxByOrNull { it.createdAtEpochMillis }
        ?: return null

    return when (action.status) {
        RouteActionStatus.FailedConflict,
        RouteActionStatus.FailedPermanent -> RouteFlowStepState.NeedsAttention
        else -> RouteFlowStepState.Syncing
    }
}

private fun routeFlowStateLabel(state: RouteFlowStepState): String =
    when (state) {
        RouteFlowStepState.Todo -> "do zrobienia"
        RouteFlowStepState.LocalComplete -> "uzupełnione lokalnie"
        RouteFlowStepState.Syncing -> "synchronizacja"
        RouteFlowStepState.Confirmed -> "potwierdzone"
        RouteFlowStepState.NeedsAttention -> "wymaga uwagi"
    }

fun shouldShowRouteDayLiveUpdate(viewedDate: String, hintDate: String?): Boolean =
    viewedDate.isNotBlank() && (hintDate == null || hintDate == viewedDate)

fun routeDateChipLabel(routeDate: String?, today: LocalDate = LocalDate.now()): String {
    val date = routeDate
        ?.takeIf { it.isNotBlank() }
        ?.let { runCatching { LocalDate.parse(it) }.getOrNull() }
        ?: return "—"

    val fallback = date.format(shortDateFormatter)
    val days = ChronoUnit.DAYS.between(today, date)
    val human = when {
        days == 0L -> "dzisiaj"
        days == 1L -> "jutro"
        days in 2L..7L -> polishWeekdayPhrase(date.dayOfWeek)
        else -> null
    }

    return human?.let { "$it · $fallback" } ?: fallback
}

private fun polishWeekdayPhrase(day: DayOfWeek): String =
    when (day) {
        DayOfWeek.MONDAY -> "w poniedziałek"
        DayOfWeek.TUESDAY -> "we wtorek"
        DayOfWeek.WEDNESDAY -> "w środę"
        DayOfWeek.THURSDAY -> "w czwartek"
        DayOfWeek.FRIDAY -> "w piątek"
        DayOfWeek.SATURDAY -> "w sobotę"
        DayOfWeek.SUNDAY -> "w niedzielę"
    }

fun inlinePhotoGridRows(photoCount: Int): Int =
    if (photoCount <= 0) 0 else (photoCount + 1) / 2

fun shouldShowPhotoPreviewLoadingState(hasPhoto: Boolean, isLoading: Boolean, isError: Boolean): Boolean =
    hasPhoto && isLoading && !isError

fun shouldShowPhotoPreviewErrorState(hasPhoto: Boolean, isError: Boolean): Boolean =
    hasPhoto && isError

fun shouldShowPhotoTileLoadingState(hasPhoto: Boolean, isLoading: Boolean, isError: Boolean): Boolean =
    hasPhoto && isLoading && !isError

fun shouldShowPhotoTileErrorState(hasPhoto: Boolean, isError: Boolean): Boolean =
    hasPhoto && isError

fun authBrandBannerHeightDp(screenHeightDp: Int): Int =
    when {
        screenHeightDp < 640 -> 118
        screenHeightDp < 740 -> 150
        else -> 190
    }

fun appBrandHeaderHeightDp(screenHeightDp: Int): Int =
    when {
        screenHeightDp < 640 -> 92
        screenHeightDp < 740 -> 108
        else -> 124
    }

fun bottomNavigationHeightDp(screenHeightDp: Int): Int =
    when {
        screenHeightDp < 640 -> 72
        screenHeightDp < 740 -> 78
        else -> 82
    }

fun screenHorizontalPaddingDp(screenWidthDp: Int): Int =
    when {
        screenWidthDp < 360 -> 14
        screenWidthDp < 420 -> 16
        else -> 20
    }

fun phonePrefixWidthDp(screenWidthDp: Int): Int =
    when {
        screenWidthDp < 360 -> 70
        screenWidthDp < 420 -> 82
        else -> 94
    }

fun routeFactColumns(screenWidthDp: Int): Int =
    if (screenWidthDp < 360) 1 else 2

fun visiblePhotoAttachmentCount(serverPhotoCount: Int, localUploadCount: Int): Int =
    serverPhotoCount.coerceAtLeast(0) + localUploadCount.coerceAtLeast(0)

fun queuedPhotoUploadCount(statuses: List<String>): Int =
    statuses.count { status -> status != "CONFIRMED" && status != "CANCELLED" }

fun routePhotoSortEpochMillis(createdAt: String?, takenAt: String?, fallback: Long): Long =
    parseIsoOffsetEpochMillis(createdAt)
        ?: parseIsoOffsetEpochMillis(takenAt)
        ?: fallback

fun confirmedUploadServerPhotoId(status: String, serverPhotoId: String?): String? =
    serverPhotoId?.takeIf { status == "CONFIRMED" && it.isNotBlank() }

fun canRetryPhotoUpload(status: String): Boolean =
    status == "FAILED_RETRYABLE"

fun canShowPhotoUploadFailureDetails(status: String, lastError: String?): Boolean =
    status in setOf("FAILED_RETRYABLE", "FAILED_PERMANENT") && !lastError.isNullOrBlank()

fun localUploadPreviewPhoto(upload: PhotoUploadEntity): RoutePhotoDto? {
    if (upload.localPath.isBlank()) return null

    return RoutePhotoDto(
        id = upload.clientRequestId,
        routeId = upload.routeId,
        clientRequestId = upload.clientRequestId,
        contentSha256 = upload.contentSha256,
        source = upload.source,
        stage = normalizedRoutePhotoStage(upload.stage),
        mimeType = upload.mimeType,
        size = upload.size,
        url = File(upload.localPath).toURI().toString(),
        takenAt = upload.takenAt,
        latitude = upload.latitude,
        longitude = upload.longitude,
        locationAccuracyMeters = upload.locationAccuracyMeters,
        createdAt = null,
        canDelete = confirmedUploadServerPhotoId(upload.status, upload.serverPhotoId) != null,
        deleteUntil = null,
    )
}

fun canLaunchCameraWithLocationPolicy(requirePreciseLocation: Boolean, hasFineLocation: Boolean): Boolean =
    !requirePreciseLocation || hasFineLocation

fun shouldShowDispatchSheetReminderCard(reminder: DispatchSheetReminderDto?): Boolean =
    reminder?.dueToday == true && reminder.status in setOf("missing", "uploaded")

fun dispatchSheetPrimaryActionLabel(reminder: DispatchSheetReminderDto?, hasQueuedUpload: Boolean): String =
    when {
        hasQueuedUpload -> "Wysyłam zdjęcie..."
        reminder?.status == "uploaded" -> "Popraw"
        else -> "Zrób zdjęcie"
    }

fun offlineStaleBannerMessage(isOnline: Boolean, isStale: Boolean, syncLabel: String): String? =
    when {
        isOnline && !isStale -> null
        !isOnline && syncLabel == "brak zapisanej synchronizacji" ->
            "Brak połączenia z serwerem. Sprawdź internet i spróbuj ponownie."
        !isOnline -> "Brak połączenia z serwerem. Dane z $syncLabel mogą być nieaktualne."
        else -> "Dane mogą być nieaktualne. Ostatnia synchronizacja: $syncLabel."
    }

private fun parseIsoOffsetEpochMillis(value: String?): Long? =
    value?.takeIf { it.isNotBlank() }?.let {
        runCatching { OffsetDateTime.parse(it).toInstant().toEpochMilli() }.getOrNull()
    }
