package pl.firmatpp.kierowca.data.upload

import android.content.Context
import androidx.work.BackoffPolicy
import androidx.work.Constraints
import androidx.work.ExistingWorkPolicy
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.workDataOf
import com.google.gson.Gson
import java.time.Instant
import java.util.UUID
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.flow.Flow

class RouteActionOutbox(
    private val context: Context,
    private val dao: RouteActionDao = DriverDatabase.get(context).routeActionDao(),
    private val workManager: WorkManager = WorkManager.getInstance(context),
    private val gson: Gson = Gson(),
) {
    fun observeUnresolvedForRoute(routeId: String): Flow<List<RouteActionEntity>> =
        dao.observeUnresolvedForRoute(routeId)

    fun observeVisibleActions(): Flow<List<RouteActionEntity>> =
        dao.observeVisibleActions()

    suspend fun enqueueStart(routeId: String, loadingWeight: Double, photoClientRequestIds: List<String>): RouteActionEntity =
        enqueue(RouteActionType.Start, routeId, loadingWeight, photoClientRequestIds)

    suspend fun enqueueFinish(routeId: String, unloadingWeight: Double, photoClientRequestIds: List<String>): RouteActionEntity =
        enqueue(RouteActionType.Finish, routeId, unloadingWeight, photoClientRequestIds)

    private suspend fun enqueue(
        action: String,
        routeId: String,
        weight: Double,
        photoClientRequestIds: List<String>,
    ): RouteActionEntity {
        val entity = RouteActionEntity(
            clientActionId = UUID.randomUUID().toString(),
            routeId = routeId,
            action = action,
            weight = weight,
            occurredAt = Instant.now().toString(),
            photoClientRequestIdsJson = gson.toJson(photoClientRequestIds.distinct()),
        )

        dao.upsert(entity)
        enqueueWorker(entity.clientActionId)

        return entity
    }

    fun enqueueWorker(clientActionId: String) {
        val request = OneTimeWorkRequestBuilder<RouteActionWorker>()
            .setInputData(workDataOf(RouteActionWorker.KEY_CLIENT_ACTION_ID to clientActionId))
            .setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build())
            .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS)
            .build()

        workManager.enqueueUniqueWork(
            RouteActionWorker.uniqueWorkName(clientActionId),
            ExistingWorkPolicy.REPLACE,
            request,
        )
    }
}
