package com.isidroid.c23.domain.use_case

import android.Manifest
import android.content.Context
import com.isidroid.core.FlowResult
import com.isidroid.location.model.LatLng2
import com.isidroid.location.repository.LocationRepository
import com.isidroid.spot.model.RichSpot
import com.isidroid.spot.repository.ActiveSpotRepository
import com.isidroid.spot.repository.SpotRepository
import com.isidroid.utils.hasPermission
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.flow
import javax.inject.Inject

class MapUseCase @Inject constructor(
    @ApplicationContext private val context: Context,
    private val locationRepository: LocationRepository,
    private val spotRepository: SpotRepository,
    private val activeSpotRepository: ActiveSpotRepository,
) {
    fun prepareData(lat: Double?, lng: Double?, spotCode: String?) = flow {
        val hasPermissions = context.hasPermission(Manifest.permission.ACCESS_FINE_LOCATION)
        if (!hasPermissions) {
            emit(MapPrepareDto.NoPermissions)
            return@flow
        }

        val location = parseLocation(lat = lat, lng = lng, spotCode = spotCode)
        emit(MapPrepareDto.Location(lat = location.first, lng = location.second))
    }

    fun myLocation() = flow {
        emit(locationRepository.getCurrentLocation())
    }

    fun findSpots(lat: Double, lng: Double, radius: Int) = flow {
        emit(FlowResult.Loading)

        val richSpotList = spotRepository.locateSpots(lat = lat, lng = lng, distance = radius)
        val result = richSpotList.groupBy { LatLng2(it.spot.lat, it.spot.lng) }

        emit(FlowResult.Success(result))
    }

    fun selectSpot(richSpot: RichSpot?) = flow {
        // save spot and all information locally
        richSpot ?: throw IllegalStateException("Failed to get Spot information")
        spotRepository.save(richSpot)
        activeSpotRepository.makeDefaultSpot(richSpot.spot.id)

        emit(1)
    }

    private suspend fun parseLocation(lat: Double?, lng: Double?, spotCode: String? = null): Pair<Double, Double> {
        val spot = spotRepository.findRichSpot(spotCode)?.spot ?: activeSpotRepository.getDefaultSpot()?.spot

        return when {
            spot != null -> Pair(spot.lat, spot.lng)
            lat == null || lng == null -> locationRepository.getCurrentLocation()
            else -> Pair(lat, lng)
        }
    }

    sealed interface MapPrepareDto {
        data object NoPermissions : MapPrepareDto
        data class Location(val lat: Double, val lng: Double) : MapPrepareDto
    }
}

