package com.isidroid.c23.ui.screen.print_jobs

import androidx.lifecycle.viewModelScope
import com.isidroid.c23.domain.use_case.PrintJobsUseCase
import com.isidroid.c23.ext.isDebug
import com.isidroid.core.FlowResult
import com.isidroid.core.vm.BaseViewModel
import com.isidroid.job_sender.SendJobEventCollectorFlow
import com.isidroid.job_sender.domain.dto.JobSenderResult
import com.isidroid.spot.model.Spot
import com.isidroid.utils.catchTimber
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class PrintJobsViewModel @Inject constructor(
    private val useCase: PrintJobsUseCase,
    private val sendJobEventCollectorFlow: SendJobEventCollectorFlow
) : BaseViewModel<PrintJobsContract.Event, PrintJobsContract.State, PrintJobsContract.Effect>() {
    init {
        viewModelScope.launch { load() }
        viewModelScope.launch { listenSendJobEvents() }
    }

    override val isDebug: Boolean = isDebug()
    override fun setInitialState(): PrintJobsContract.State = PrintJobsContract.State()
    override suspend fun handleEvents(event: PrintJobsContract.Event) {
        when (event) {
            PrintJobsContract.Event.ToBack -> setEffect { PrintJobsContract.Effect.Navigation.ToBack }
            is PrintJobsContract.Event.ToDetails -> setEffect { PrintJobsContract.Effect.Navigation.ToDetails(event.id) }
            is PrintJobsContract.Event.ToSpot -> openSpot(event.spotCode)
        }
    }

    // handle events
    private suspend fun load() {
        useCase.load()
            .flowOn(Dispatchers.IO)
            .catchTimber { }
            .collect { res ->
                when (res) {
                    FlowResult.Loading -> setState { copy(loading = true) }
                    is FlowResult.Success -> setState { copy(loading = false, jobs = res.result) }
                }
            }
    }

    private suspend fun openSpot(spotCode: String?) {
        spotCode ?: return

        useCase.findSpot(spotCode)
            .flowOn(Dispatchers.IO)
            .catchTimber { }
            .collect { res ->
                when (res) {
                    FlowResult.Loading -> {}
                    is FlowResult.Success -> openSpotOnMap(res.result)
                }
            }
    }

    private suspend fun listenSendJobEvents() {
        sendJobEventCollectorFlow.eventsFlow
            .filterIsInstance<JobSenderResult.Statuses>()
            .collect { st -> updateStatuses(st) }
    }

    private fun updateStatuses(statuses: JobSenderResult.Statuses) {
        val listItems = useCase.updateStatuses(jobs = viewState.value.jobs, info = statuses) ?: return
        setState { copy(jobs = listItems) }
    }

    // handle callbacks
    private fun openSpotOnMap(spot: Spot?) {
        spot ?: return
        setEffect { PrintJobsContract.Effect.Navigation.ToMap(lat = spot.lat, lng = spot.lng) }
    }
}