package com.fabledsword.minstrel.history.data

import com.fabledsword.minstrel.api.endpoints.HistoryApi
import com.fabledsword.minstrel.cache.db.dao.CachedHistorySnapshotDao
import com.fabledsword.minstrel.cache.db.entities.CachedHistorySnapshotEntity
import com.fabledsword.minstrel.library.data.toDomain
import com.fabledsword.minstrel.models.TrackRef
import com.fabledsword.minstrel.models.wire.HistoryPageWire
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.serialization.json.Json
import retrofit2.Retrofit
import retrofit2.create
import javax.inject.Inject
import javax.inject.Singleton

/**
 * One row of listening history mapped into a UI-friendly shape. The
 * server's HistoryEventWire pairs `played_at` (RFC3339) with the
 * full TrackWire — we keep both, mapping the track through the
 * existing wire→TrackRef converter so playback wiring stays
 * consistent with the Library reads.
 */
data class HistoryEntry(
    val id: String,
    val playedAt: String,
    val track: TrackRef,
)

data class HistoryPage(
    val entries: List<HistoryEntry>,
    val hasMore: Boolean,
)

/**
 * Cache-first listening history. Mirrors Flutter's `_historyProvider`
 * (`cacheFirst` over the `cached_history_snapshot` drift row with
 * `alwaysRefresh`) using the native Room + Flow idiom:
 *
 *   - [observeHistory] emits the cached snapshot instantly (null until
 *     the first fetch lands), so the History tab paints from disk on
 *     cold open with no blocking spinner.
 *   - [refresh] pulls `/api/me/history`, stores the raw wire JSON as
 *     the single snapshot row, and the Flow re-emits — SWR. Errors
 *     propagate to the caller (the VM surfaces them only when there's
 *     no cache to show).
 *
 * The whole page is stored as one JSON blob (single-row table) rather
 * than per-row Room entities — history is a read-only timestamp-keyed
 * snapshot, so a blob is the simplest faithful mirror of the Drift
 * snapshot and avoids ordering/paging bookkeeping for no UX gain.
 */
@Singleton
class HistoryRepository @Inject constructor(
    private val snapshotDao: CachedHistorySnapshotDao,
    private val json: Json,
    retrofit: Retrofit,
) {
    private val api: HistoryApi = retrofit.create()

    fun observeHistory(): Flow<HistoryPage?> =
        snapshotDao.observe().map { row ->
            row?.let { json.decodeFromString<HistoryPageWire>(it.json).toDomain() }
        }

    /** Fetch the latest history and overwrite the cached snapshot. */
    suspend fun refresh() {
        val wire = api.getHistory()
        val blob = json.encodeToString(HistoryPageWire.serializer(), wire)
        snapshotDao.upsert(CachedHistorySnapshotEntity(json = blob))
    }
}

private fun HistoryPageWire.toDomain(): HistoryPage = HistoryPage(
    entries = events.map {
        HistoryEntry(id = it.id, playedAt = it.playedAt, track = it.track.toDomain())
    },
    hasMore = hasMore,
)
