package com.fabledsword.minstrel.home.data

import app.cash.turbine.test
import com.fabledsword.minstrel.cache.db.dao.CachedHomeIndexDao
import com.fabledsword.minstrel.cache.db.entities.CachedHomeIndexEntity
import com.fabledsword.minstrel.metadata.HomeArtistPrewarmer
import com.fabledsword.minstrel.metadata.MetadataProvider
import com.fabledsword.minstrel.models.AlbumRef
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.mockwebserver.MockWebServer
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import retrofit2.Retrofit
import kotlin.test.assertEquals
import kotlin.test.assertNull

class HomeRepositoryTest {
    private lateinit var server: MockWebServer
    private lateinit var retrofit: Retrofit
    private val homeIndexDao = mockk<CachedHomeIndexDao>(relaxed = true)
    private val metadataProvider = mockk<MetadataProvider>(relaxed = true)
    private val prewarmer = mockk<HomeArtistPrewarmer>(relaxed = true)

    @BeforeEach
    fun setup() {
        server = MockWebServer().apply { start() }
        retrofit = Retrofit.Builder()
            .baseUrl(server.url("/"))
            .client(OkHttpClient.Builder().build())
            .addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
            .build()
    }

    @AfterEach
    fun teardown() {
        server.shutdown()
    }

    private companion object {
        private val json = Json { ignoreUnknownKeys = true }
    }

    private fun repo() = HomeRepository(homeIndexDao, metadataProvider, prewarmer, retrofit)

    @Test
    fun `rediscover albums emit skeleton tiles then reveal as rows land`() = runTest {
        val indexRow = CachedHomeIndexEntity(
            section = HomeRepository.SECTION_REDISCOVER_ALBUMS,
            position = 0,
            entityType = "album",
            entityId = "al1",
        )
        every {
            homeIndexDao.observeBySection(HomeRepository.SECTION_REDISCOVER_ALBUMS)
        } returns flowOf(listOf(indexRow))
        val albumFlow = MutableStateFlow<AlbumRef?>(null)
        every { metadataProvider.observeAlbum("al1") } returns albumFlow

        repo().observeRediscoverAlbums().test {
            val skeleton = awaitItem()
            assertEquals(1, skeleton.size)
            assertEquals("al1", skeleton[0].id)
            assertNull(skeleton[0].value)   // skeleton in position

            albumFlow.value = AlbumRef(id = "al1", title = "Geogaddi", artistId = "a1")
            val revealed = awaitItem()
            assertEquals("al1", revealed[0].id)
            assertEquals("Geogaddi", revealed[0].value?.title)   // tile reveals
            cancelAndIgnoreRemainingEvents()
        }
    }

    @Test
    fun `empty section emits empty list`() = runTest {
        every {
            homeIndexDao.observeBySection(HomeRepository.SECTION_REDISCOVER_ALBUMS)
        } returns flowOf(emptyList())

        repo().observeRediscoverAlbums().test {
            assertEquals(0, awaitItem().size)
            awaitComplete()
        }
    }
}
