package de.krisenvorrat.app.data.repository

import de.krisenvorrat.app.data.db.dao.LocationDao
import de.krisenvorrat.app.data.db.entity.LocationEntity
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 FakeLocationDao : LocationDao {
    private val items = mutableListOf<LocationEntity>()
    private val flow = MutableStateFlow<List<LocationEntity>>(emptyList())

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

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

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

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

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

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

class LocationRepositoryImplTest {

    private val fakeDao = FakeLocationDao()
    private val repository = LocationRepositoryImpl(fakeDao)

    @Test
    fun test_insert_withNewLocation_locationAppearsInGetAll() = runBlocking {
        // Given
        val location = LocationEntity(id = 1, name = "Keller")

        // When
        repository.insert(location)

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

    @Test
    fun test_update_withExistingLocation_locationIsUpdated() = runBlocking {
        // Given
        val original = LocationEntity(id = 1, name = "Keller")
        repository.insert(original)
        val updated = LocationEntity(id = 1, name = "Dachboden")

        // When
        repository.update(updated)

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

    @Test
    fun test_delete_withExistingLocation_locationRemovedFromGetAll() = runBlocking {
        // Given
        val location = LocationEntity(id = 1, name = "Keller")
        repository.insert(location)

        // When
        repository.delete(location)

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

    @Test
    fun test_getAll_withMultipleLocations_returnsAllLocations() = runBlocking {
        // Given
        val loc1 = LocationEntity(id = 1, name = "Keller")
        val loc2 = LocationEntity(id = 2, name = "Küche")
        repository.insert(loc1)
        repository.insert(loc2)

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

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