package com.isidroid.c23.domain.use_case

import android.Manifest
import android.content.Context
import androidx.compose.ui.text.intl.Locale
import com.isidroid.c23.JobNotFoundException
import com.isidroid.c23.NoLocationPermissionException
import com.isidroid.c23.constant.Argument
import com.isidroid.c23.data.mapper.createListItem
import com.isidroid.c23.domain.dto.PrintJobListItem
import com.isidroid.c23.ext.getPrintJobStatus
import com.isidroid.c23.ext.getPrintJobStatusColor
import com.isidroid.c23.ext.isDebug
import com.isidroid.core.FlowResult
import com.isidroid.job.repository.JobRepository
import com.isidroid.job_sender.domain.dto.JobSenderResult
import com.isidroid.location.ext.isMileUnit
import com.isidroid.location.repository.LocationRepository
import com.isidroid.spot.repository.SpotRepository
import com.isidroid.ui.maps.ext.addKilometerToLatLng
import com.isidroid.ui.maps.model.MapMarker
import com.isidroid.utils.hasPermission
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.flow
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.round

@Singleton
class DetailsUseCase @Inject constructor(
    @ApplicationContext private val context: Context,
    private val jobRepository: JobRepository,
    private val spotRepository: SpotRepository,
    private val locationRepository: LocationRepository,
) {
    fun loadDetails(id: String) = flow {
        emit(FlowResult.Loading)

        val hasLocationPermission = context.hasPermission(Manifest.permission.ACCESS_FINE_LOCATION)
        if (!hasLocationPermission) {
            throw NoLocationPermissionException()
        }

        val job = jobRepository.readLocalList(ids = listOf(id)).firstOrNull() ?: throw JobNotFoundException()
        val richSpot = spotRepository.findLocalRichSpots(ids = listOf(job.spotId))?.firstOrNull()

        val result = job.createListItem(context = context, richSpot = richSpot)
        val currentLocation = locationRepository.getCurrentLocation()
        val distanceKm = locationRepository.findDistance(
            currentLocation.first,
            currentLocation.second,
            richSpot?.spot?.lat,
            richSpot?.spot?.lng
        )?.let { (round(it * 100) / 100) }

        val distance = buildString {
            distanceKm?.let { append("$distanceKm ") }
            if (Locale.current.isMileUnit)
                append("miles")
            else
                append("km")
        }

        val location = if (isDebug() && richSpot?.spot != null)
            addKilometerToLatLng(richSpot.spot.lat, richSpot.spot.lng, distance = 0.2)
        else
            currentLocation

        val mapMarker = richSpot?.spot?.let { spot ->
            MapMarker(id = spot.id, lat = spot.lat, lng = spot.lng, name = spot.name)
        }

        emit(
            FlowResult.Success(
                mapOf(
                    Argument.INFO to result,
                    Argument.LATITUDE to location.first,
                    Argument.LONGITUDE to location.second,
                    Argument.MARKER to mapMarker,
                    Argument.DISTANCE to distance
                )
            )
        )
    }

    fun updateStatuses(printJob: PrintJobListItem?, info: JobSenderResult.Statuses): PrintJobListItem? {
        return printJob?.copy(
            statusName = context.getString(getPrintJobStatus(info.status)),
            statusColor = getPrintJobStatusColor(info.status)
        )
    }
}