package com.fabledsword.minstrel.requests.data

import com.fabledsword.minstrel.api.endpoints.RequestsApi
import com.fabledsword.minstrel.cache.mutations.MutationQueue
import com.fabledsword.minstrel.models.RequestRef
import com.fabledsword.minstrel.models.RequestStatus
import com.fabledsword.minstrel.models.wire.RequestWire
import retrofit2.Retrofit
import retrofit2.create
import java.io.IOException
import javax.inject.Inject
import javax.inject.Singleton

/**
 * Outcome of a [RequestsRepository.cancel] call. Mirrors the
 * [com.fabledsword.minstrel.likes.data.UnflagOutcome] / etc. shape.
 */
enum class CancelOutcome { Synced, Queued }

/**
 * Read-through accessor for the caller's own Lidarr requests +
 * cancel-while-pending write. No Room caching for v1 — same rationale
 * as HistoryRepository; the offline-snapshot mirror lands with a
 * later slice. Cancel is offline-first: direct REST on the happy
 * path, MutationQueue fallback on transport failure so the user's
 * intent persists across connectivity loss.
 */
@Singleton
class RequestsRepository @Inject constructor(
    private val mutationQueue: MutationQueue,
    retrofit: Retrofit,
) {
    private val api: RequestsApi = retrofit.create()

    suspend fun listMine(): List<RequestRef> =
        api.listMine().map { it.toDomain() }

    /**
     * Cancels a pending request. On 2xx, returns the canonical
     * cancelled row from the server (status=rejected). On transport
     * failure, enqueues a REQUEST_CANCEL mutation for later replay
     * and returns null — the caller's optimistic UI removal already
     * reflects the user's intent, and the next listMine() refetch
     * (post-replay) will surface the canonical row.
     */
    suspend fun cancel(id: String): Pair<CancelOutcome, RequestRef?> = try {
        CancelOutcome.Synced to api.cancel(id).toDomain()
    } catch (
        @Suppress("SwallowedException") _: IOException,
    ) {
        mutationQueue.enqueueRequestCancel(id)
        CancelOutcome.Queued to null
    }
}

// ── Mappers (internal — wire stays out of UI) ──

private fun RequestWire.toDomain(): RequestRef = RequestRef(
    id = id,
    userId = userId,
    status = RequestStatus.fromWire(status),
    kind = kind,
    artistName = artistName,
    albumTitle = albumTitle,
    trackTitle = trackTitle,
    requestedAt = requestedAt,
    decidedAt = decidedAt,
    notes = notes,
    importedAlbumCount = importedAlbumCount,
    importedTrackCount = importedTrackCount,
    matchedTrackId = matchedTrackId,
    matchedAlbumId = matchedAlbumId,
    matchedArtistId = matchedArtistId,
)
