package com.isidroid.c23.ui.screen.details

import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AccountCircle
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.isidroid.c23.NoLocationPermissionException
import com.isidroid.c23.constant.Argument
import com.isidroid.c23.domain.dto.PrintJobListItem
import com.isidroid.c23.domain.use_case.DetailsUseCase
import com.isidroid.c23.ext.isDebug
import com.isidroid.core.FlowResult
import com.isidroid.core.vm.BaseViewModel
import com.isidroid.ui.maps.model.MapMarker
import com.isidroid.utils.catchTimber
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.singleOrNull
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class JobDetailsViewModel @Inject constructor(
    private val useCase: DetailsUseCase,
    savedStateHandle: SavedStateHandle
) : BaseViewModel<JobDetailsContract.Event, JobDetailsContract.State, JobDetailsContract.Effect>() {
    private val _spotResultStateFlow = MutableStateFlow<List<MapMarker>>(emptyList())
    val spotResultStateFlow = _spotResultStateFlow.asStateFlow()

    private lateinit var jobId: String

    init {
        viewModelScope.launch {
            savedStateHandle.getStateFlow<String?>(Argument.ID, null)
                .filterNotNull()
                .onEach {
                    jobId = it
                    loadDetails()
                }
                .singleOrNull()
        }
    }


    override val isDebug: Boolean = isDebug()
    override fun setInitialState(): JobDetailsContract.State = JobDetailsContract.State()
    override suspend fun handleEvents(event: JobDetailsContract.Event) {
        when (event) {
            JobDetailsContract.Event.ToBack -> setEffect { JobDetailsContract.Effect.Navigation.ToBack }
            JobDetailsContract.Event.OpenConfirmationMapRoute -> setState { copy(routeConfirmationVisible = true) }
            JobDetailsContract.Event.DismissBuildRouteConfirmation -> setState { copy(routeConfirmationVisible = false) }
            JobDetailsContract.Event.OpenNavigationApp -> openNavigationApp()
            JobDetailsContract.Event.LocationPermissionGranted -> locationPermissionGranted()
        }
    }

    // handle events
    private suspend fun loadDetails() {
        useCase.loadDetails(jobId)
            .flowOn(Dispatchers.IO)
            .catchTimber {
                when (it) {
                    is NoLocationPermissionException -> setState { copy(requestLocationPermission = true) }
                }
            }
            .collect { res ->
                when (res) {
                    FlowResult.Loading -> setState { copy(isLoading = true) }
                    is FlowResult.Success -> onDetails(
                        item = res.result[Argument.INFO] as? PrintJobListItem,
                        lat = res.result[Argument.LATITUDE] as? Double,
                        lng = res.result[Argument.LONGITUDE] as? Double,
                        mapMarker = res.result[Argument.MARKER] as? MapMarker,
                        distance = res.result[Argument.DISTANCE] as? String
                    )
                }
            }
    }

    private suspend fun onDetails(item: PrintJobListItem?, lat: Double?, lng: Double?, mapMarker: MapMarker?, distance: String?) {
        val myLocation = MapMarker(id = "my_location", lat = lat ?: 0.0, lng = lng ?: 0.0, name = "My location", imageVector = Icons.Outlined.AccountCircle)
        _spotResultStateFlow.emit(listOfNotNull(mapMarker, myLocation))

        setState { copy(isLoading = false, printJob = item, lat = lat, lng = lng, distance = distance) }
    }

    private fun openNavigationApp() {
        setState { copy(routeConfirmationVisible = false) }
        setEffect { JobDetailsContract.Effect.Navigation.ToNavigationApp(viewState.value.lat, viewState.value.lng) }
    }

    private suspend fun locationPermissionGranted() {
        setState { copy(requestLocationPermission = false) }
        loadDetails()
    }
}