package de.krisenvorrat.app.data.repository

import de.krisenvorrat.app.data.db.dao.CategoryDao
import de.krisenvorrat.app.data.db.entity.CategoryEntity
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test

private class FakeCategoryDao : CategoryDao {
    private val items = mutableListOf<CategoryEntity>()
    private val flow = MutableStateFlow<List<CategoryEntity>>(emptyList())

    override suspend fun insert(category: CategoryEntity) {
        items.add(category)
        flow.value = items.toList()
    }

    override suspend fun update(category: CategoryEntity) {
        val idx = items.indexOfFirst { it.id == category.id }
        if (idx >= 0) {
            items[idx] = category
            flow.value = items.toList()
        }
    }

    override suspend fun delete(category: CategoryEntity) {
        items.remove(category)
        flow.value = items.toList()
    }

    override fun getAll(): Flow<List<CategoryEntity>> = flow

    override suspend fun getById(id: Int): CategoryEntity? = items.find { it.id == id }

    override suspend fun upsertAll(categories: List<CategoryEntity>) {
        categories.forEach { category ->
            val idx = items.indexOfFirst { it.id == category.id }
            if (idx >= 0) items[idx] = category else items.add(category)
        }
        flow.value = items.toList()
    }
}

class CategoryRepositoryImplTest {

    private val fakeDao = FakeCategoryDao()
    private val repository = CategoryRepositoryImpl(fakeDao)

    @Test
    fun test_insert_withNewCategory_categoryAppearsInGetAll() = runBlocking {
        // Given
        val category = CategoryEntity(id = 1, name = "Lebensmittel")

        // When
        repository.insert(category)

        // Then
        val result = repository.getAll().first()
        assertTrue(result.contains(category))
    }

    @Test
    fun test_update_withExistingCategory_categoryIsUpdated() = runBlocking {
        // Given
        val original = CategoryEntity(id = 1, name = "Alt")
        repository.insert(original)
        val updated = CategoryEntity(id = 1, name = "Neu")

        // When
        repository.update(updated)

        // Then
        val result = repository.getAll().first()
        assertEquals("Neu", result.first { it.id == 1 }.name)
    }

    @Test
    fun test_delete_withExistingCategory_categoryRemovedFromGetAll() = runBlocking {
        // Given
        val category = CategoryEntity(id = 1, name = "Lebensmittel")
        repository.insert(category)

        // When
        repository.delete(category)

        // Then
        val result = repository.getAll().first()
        assertFalse(result.contains(category))
    }

    @Test
    fun test_getAll_withMultipleCategories_returnsAllCategories() = runBlocking {
        // Given
        val cat1 = CategoryEntity(id = 1, name = "Lebensmittel")
        val cat2 = CategoryEntity(id = 2, name = "Hygiene")
        repository.insert(cat1)
        repository.insert(cat2)

        // When
        val result = repository.getAll().first()

        // Then
        assertEquals(2, result.size)
    }
}
