package com.fabledsword.minstrel.discover.data

import com.fabledsword.minstrel.api.endpoints.DiscoverApi
import com.fabledsword.minstrel.cache.mutations.MutationQueue
import com.fabledsword.minstrel.cache.mutations.RequestCreatePayload
import com.fabledsword.minstrel.models.ArtistSuggestionRef
import com.fabledsword.minstrel.models.LidarrRequestKind
import com.fabledsword.minstrel.models.LidarrSearchResultRef
import com.fabledsword.minstrel.models.SeedContributionRef
import com.fabledsword.minstrel.models.wire.ArtistSuggestionWire
import com.fabledsword.minstrel.models.wire.CreateRequestBody
import com.fabledsword.minstrel.models.wire.LidarrSearchResultWire
import com.fabledsword.minstrel.models.wire.SeedContributionWire
import retrofit2.Retrofit
import retrofit2.create
import javax.inject.Inject
import javax.inject.Singleton

/**
 * Outcome of a `createRequest` call — discriminates "server accepted"
 * from "network down, queued for later replay". The UI uses this to
 * pick between "Requested" and "Queued" snackbars.
 */
enum class RequestOutcome { ACCEPTED, QUEUED }

/**
 * Read-through Discover surface: Lidarr search, suggestion feed, and
 * the request-creation write path. Mirrors the Flutter
 * `DiscoverApi` + `mutationQueueProvider` integration —
 * `feedback_offline_first_for_server_writes` says writes never go
 * fire-and-forget, so `createRequest` falls back to the
 * MutationQueue on transport failure.
 *
 * Reads are not cached locally yet — discovery results are inherently
 * transient (60s server-side LRU); the cache wins are minor and
 * unblock no UX. Phase 13's offline pass adds an offline scrollback
 * for the suggestion feed only.
 */
@Singleton
class DiscoverRepository @Inject constructor(
    private val mutationQueue: MutationQueue,
    retrofit: Retrofit,
) {
    private val api: DiscoverApi = retrofit.create()

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

    suspend fun search(query: String, kind: LidarrRequestKind): List<LidarrSearchResultRef> =
        api.search(query = query, kind = kind.wire).map { it.toDomain() }

    /**
     * Fires `POST /api/requests`; on transport failure enqueues a
     * `RequestCreatePayload` for the Phase-12.2 replayer to drain.
     * Returns [RequestOutcome.ACCEPTED] when the server responded
     * 2xx, [RequestOutcome.QUEUED] when the call was buffered.
     */
    suspend fun createRequest(
        kind: LidarrRequestKind,
        artistMbid: String,
        artistName: String,
        albumMbid: String? = null,
        albumTitle: String? = null,
        trackMbid: String? = null,
        trackTitle: String? = null,
    ): RequestOutcome {
        val payload = RequestCreatePayload(
            kind = kind.wire,
            artistMbid = artistMbid,
            artistName = artistName,
            albumMbid = albumMbid?.ifEmpty { null },
            albumTitle = albumTitle?.ifEmpty { null },
            trackMbid = trackMbid?.ifEmpty { null },
            trackTitle = trackTitle?.ifEmpty { null },
        )
        return try {
            api.createRequest(payload.toBody())
            RequestOutcome.ACCEPTED
        } catch (
            @Suppress("TooGenericExceptionCaught", "SwallowedException") e: Throwable,
        ) {
            // Intentional swallow — see LikesRepository.toggleLike for
            // the same offline-first rationale.
            mutationQueue.enqueueRequestCreate(payload)
            RequestOutcome.QUEUED
        }
    }
}

// ── Mappers (internal — wire types stay out of UI) ──

private fun LidarrSearchResultWire.toDomain(): LidarrSearchResultRef = LidarrSearchResultRef(
    mbid = mbid,
    name = name,
    secondaryText = secondaryText,
    imageUrl = imageUrl,
    artistMbid = artistMbid,
    albumMbid = albumMbid,
    inLibrary = inLibrary,
    requested = requested,
)

private fun ArtistSuggestionWire.toDomain(): ArtistSuggestionRef = ArtistSuggestionRef(
    mbid = mbid,
    name = name,
    imageUrl = imageUrl,
    attribution = attribution.map { it.toDomain() },
)

private fun SeedContributionWire.toDomain(): SeedContributionRef = SeedContributionRef(
    name = name,
    isLiked = isLiked,
)

private fun RequestCreatePayload.toBody(): CreateRequestBody = CreateRequestBody(
    kind = kind,
    artistMbid = artistMbid,
    artistName = artistName,
    albumMbid = albumMbid,
    albumTitle = albumTitle,
    trackMbid = trackMbid,
    trackTitle = trackTitle,
)
