package pl.firmatpp.kierowca.data.upload

import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import java.io.File
import pl.firmatpp.kierowca.data.ApiErrorKind
import pl.firmatpp.kierowca.data.ApiErrorMapper
import pl.firmatpp.kierowca.data.DriverRepository

class PhotoUploadWorker(
    appContext: Context,
    params: WorkerParameters,
) : CoroutineWorker(appContext, params) {
    private val dao = DriverDatabase.get(appContext).photoUploadDao()
    private val repository = DriverRepository(appContext)

    override suspend fun doWork(): Result {
        val clientRequestId = inputData.getString(KEY_CLIENT_REQUEST_ID) ?: return Result.failure()
        val upload = dao.find(clientRequestId) ?: return Result.failure()
        val file = File(upload.localPath)

        if (!file.exists()) {
            val message = "Lokalny plik zdjęcia nie istnieje. Zdjęcie nie zostało zapisane."
            dao.updateStatus(
                clientRequestId = clientRequestId,
                status = PhotoUploadStatus.FailedPermanent.storageValue,
                progress = 0,
                lastError = PhotoUploadFailureDetails.local(message),
                attemptIncrement = 0,
            )
            return Result.failure()
        }

        dao.updateStatus(
            clientRequestId = clientRequestId,
            status = PhotoUploadStatus.Uploading.storageValue,
            progress = 10,
            lastError = null,
            attemptIncrement = 1,
        )
        setProgress(workDataOf(KEY_PROGRESS to 10))

        return runCatching {
            setProgress(workDataOf(KEY_PROGRESS to 70))
            val response = repository.uploadQueuedPhoto(upload)

            dao.updateStatus(
                clientRequestId = clientRequestId,
                status = PhotoUploadStatus.Verifying.storageValue,
                progress = 90,
                lastError = null,
                attemptIncrement = 0,
            )
            setProgress(workDataOf(KEY_PROGRESS to 90))

            val receipt = response.receipt
            if (!PhotoUploadReceiptVerifier.matches(upload, receipt)) {
                error("Serwer nie potwierdził zgodności zdjęcia. Zdjęcie nie zostało zapisane.")
            }

            dao.markConfirmed(clientRequestId, receipt.serverPhotoId)
            Result.success()
        }.getOrElse { throwable ->
            val error = ApiErrorMapper.map(throwable)
            val status = if (error.retryable) PhotoUploadStatus.FailedRetryable else PhotoUploadStatus.FailedPermanent
            dao.updateStatus(
                clientRequestId = clientRequestId,
                status = status.storageValue,
                progress = 0,
                lastError = PhotoUploadFailureDetails.fromApiError(error),
                attemptIncrement = 0,
            )

            if (error.retryable && error.kind != ApiErrorKind.Auth) Result.retry() else Result.failure()
        }
    }

    companion object {
        const val KEY_CLIENT_REQUEST_ID = "clientRequestId"
        const val KEY_PROGRESS = "progress"

        fun uniqueWorkName(clientRequestId: String): String = "photo-upload-$clientRequestId"
    }
}
