package pl.firmatpp.kierowca.ui

import android.app.Application
import android.net.Uri
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import java.time.LocalDate
import pl.firmatpp.kierowca.data.DriverRepository
import pl.firmatpp.kierowca.data.PhotoUploadMetadata
import pl.firmatpp.kierowca.data.model.DriverDto
import pl.firmatpp.kierowca.data.model.DriverRouteDto
import pl.firmatpp.kierowca.data.model.RoutePhotoDto

enum class DriverScreen { Phone, Otp, Routes, Profile, Detail, Photo }

data class DriverUiState(
    val screen: DriverScreen = DriverScreen.Phone,
    val loading: Boolean = false,
    val refreshing: Boolean = false,
    val phone: String = "",
    val otpCode: String = "",
    val maskedPhone: String = "",
    val driver: DriverDto? = null,
    val routes: List<DriverRouteDto> = emptyList(),
    val selectedDate: String = LocalDate.now().toString(),
    val minRouteDate: String = LocalDate.now().minusDays(7).toString(),
    val maxRouteDate: String = LocalDate.now().plusDays(1).toString(),
    val autoRefreshSeconds: Int = 60,
    val allowGalleryUploads: Boolean = true,
    val selectedRoute: DriverRouteDto? = null,
    val selectedPhoto: RoutePhotoDto? = null,
    val imageAuthHeader: String? = null,
    val error: String? = null,
)

class DriverViewModel(application: Application) : AndroidViewModel(application) {
    private val repository = DriverRepository(application)
    private val _state = MutableStateFlow(DriverUiState(loading = true))
    val state: StateFlow<DriverUiState> = _state

    init {
        viewModelScope.launch {
            if (repository.hasToken()) refreshRoutes() else _state.update { it.copy(loading = false) }
        }
    }

    fun requestOtp(phone: String) = runLoading {
        val response = repository.requestOtp(phone)
        _state.update {
            it.copy(
                screen = DriverScreen.Otp,
                phone = phone,
                otpCode = "",
                maskedPhone = response.phoneMasked.orEmpty(),
                error = null,
            )
        }
    }

    fun verifyOtp(code: String) = runLoading {
        val driver = repository.verifyOtp(_state.value.phone, code, android.os.Build.MODEL ?: "Android")
        _state.update { it.copy(driver = driver, imageAuthHeader = repository.imageAuthHeader()) }
        refreshRoutes()
    }

    fun updateOtpCode(code: String) {
        _state.update { it.copy(otpCode = code.filter(Char::isDigit).take(10)) }
    }

    fun refreshRoutes() = loadRoutes(date = _state.value.selectedDate, showLoading = true, navigateToRoutes = true)

    fun refreshRoutesSilently() = loadRoutes(date = _state.value.selectedDate, showLoading = false, navigateToRoutes = false)

    fun selectRouteDate(date: String) = loadRoutes(date = date, showLoading = true, navigateToRoutes = true)

    private fun loadRoutes(date: String?, showLoading: Boolean, navigateToRoutes: Boolean) {
        viewModelScope.launch {
            _state.update { it.copy(loading = showLoading, refreshing = !showLoading, error = null) }

            runCatching {
                val response = repository.bootstrap(date)
                val settings = response.driverAppSettings
                _state.update {
                    it.copy(
                        screen = if (navigateToRoutes) DriverScreen.Routes else it.screen,
                        driver = response.session.driver,
                        routes = response.routes.today,
                        selectedDate = settings?.selectedDate ?: date ?: it.selectedDate,
                        minRouteDate = settings?.minDate ?: it.minRouteDate,
                        maxRouteDate = settings?.maxDate ?: it.maxRouteDate,
                        autoRefreshSeconds = settings?.autoRefreshSeconds ?: it.autoRefreshSeconds,
                        allowGalleryUploads = settings?.allowGalleryUploads ?: it.allowGalleryUploads,
                        imageAuthHeader = repository.imageAuthHeader(),
                        error = null,
                    )
                }
            }.onFailure { throwable ->
                _state.update { it.copy(error = throwable.message ?: "Wystapil blad.") }
            }

            _state.update {
                it.copy(loading = false, refreshing = false)
            }
        }
    }

    fun openRoute(routeId: String) = runLoading {
        val response = repository.route(routeId)
        _state.update { it.copy(screen = DriverScreen.Detail, selectedRoute = response.route, error = null) }
    }

    fun refreshSelectedRoute() {
        val routeId = _state.value.selectedRoute?.id ?: return

        viewModelScope.launch {
            _state.update { it.copy(refreshing = true, error = null) }

            runCatching { repository.route(routeId) }
                .onSuccess { response ->
                    _state.update {
                        it.copy(
                            selectedRoute = response.route,
                            routes = it.routes.map { route -> if (route.id == routeId) response.route else route },
                            error = null,
                        )
                    }
                }
                .onFailure { throwable ->
                    _state.update { it.copy(error = throwable.message ?: "Wystapil blad.") }
                }

            _state.update { it.copy(refreshing = false) }
        }
    }

    fun openProfile() {
        _state.update { it.copy(screen = DriverScreen.Profile, error = null) }
    }

    fun uploadPhoto(uri: Uri, source: String, metadata: PhotoUploadMetadata = PhotoUploadMetadata()) = runLoading {
        val route = _state.value.selectedRoute ?: return@runLoading
        repository.uploadPhoto(route.id, uri, source, metadata)
        openRoute(route.id)
    }

    fun openPhoto(photo: RoutePhotoDto) {
        _state.update { it.copy(screen = DriverScreen.Photo, selectedPhoto = photo) }
    }

    fun back() {
        _state.update {
            when (it.screen) {
                DriverScreen.Photo -> it.copy(screen = DriverScreen.Detail, selectedPhoto = null)
                DriverScreen.Detail -> it.copy(screen = DriverScreen.Routes, selectedRoute = null)
                DriverScreen.Profile -> it.copy(screen = DriverScreen.Routes)
                DriverScreen.Otp -> it.copy(screen = DriverScreen.Phone)
                else -> it
            }
        }
    }

    fun logout() = runLoading {
        repository.logout()
        _state.update { DriverUiState(screen = DriverScreen.Phone) }
    }

    private fun runLoading(block: suspend () -> Unit) {
        viewModelScope.launch {
            _state.update { it.copy(loading = true, error = null) }
            runCatching { block() }
                .onFailure { throwable ->
                    _state.update { it.copy(error = throwable.message ?: "Wystapil blad.") }
                }
            _state.update { it.copy(loading = false) }
        }
    }
}
