package de.krisenvorrat.app.data.repository

import de.krisenvorrat.app.data.db.dao.SettingsDao
import de.krisenvorrat.app.data.db.entity.SettingsEntity
import de.krisenvorrat.app.data.security.SecureTokenStorage
import de.krisenvorrat.app.domain.model.SettingsKeys
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.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test

private class FakeSettingsDao : SettingsDao {
    private val store = mutableMapOf<String, String>()
    private val flow = MutableStateFlow<List<SettingsEntity>>(emptyList())

    override suspend fun upsert(setting: SettingsEntity) {
        store[setting.key] = setting.value
        flow.value = store.map { SettingsEntity(key = it.key, value = it.value) }
    }

    override suspend fun getValue(key: String): String? = store[key]

    override fun observeValue(key: String): Flow<String?> =
        MutableStateFlow(store[key])

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

    override suspend fun upsertAll(settings: List<SettingsEntity>) {
        settings.forEach { store[it.key] = it.value }
        flow.value = store.map { SettingsEntity(key = it.key, value = it.value) }
    }
}

private class FakeSecureTokenStorage : SecureTokenStorage {
    private val store = mutableMapOf<String, String>()

    override fun get(key: String): String? = store[key]

    override fun set(key: String, value: String) {
        store[key] = value
    }

    override fun remove(key: String) {
        store.remove(key)
    }
}

class SettingsRepositoryImplTest {

    private val fakeDao = FakeSettingsDao()
    private val fakeSecureStorage = FakeSecureTokenStorage()
    private val repository = SettingsRepositoryImpl(fakeDao, fakeSecureStorage)

    @Test
    fun test_setValue_withNewKey_valueCanBeRetrieved() = runBlocking {
        // Given
        val key = "theme"
        val value = "dark"

        // When
        repository.setValue(key, value)

        // Then
        val result = repository.getValue(key)
        assertEquals(value, result)
    }

    @Test
    fun test_setValue_withExistingKey_valueIsUpdated() = runBlocking {
        // Given
        repository.setValue("theme", "light")

        // When
        repository.setValue("theme", "dark")

        // Then
        assertEquals("dark", repository.getValue("theme"))
    }

    @Test
    fun test_getValue_withUnknownKey_returnsNull() = runBlocking {
        // Given / When
        val result = repository.getValue("nonexistent")

        // Then
        assertNull(result)
    }

    @Test
    fun test_getAll_afterInsertingTwoSettings_returnsBothSettings() = runBlocking {
        // Given
        repository.setValue("theme", "dark")
        repository.setValue("language", "de")

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

        // Then
        assertEquals(2, result.size)
        assertTrue(result.any { it.key == "theme" && it.value == "dark" })
        assertTrue(result.any { it.key == "language" && it.value == "de" })
    }

    @Test
    fun test_setValue_withSensitiveKey_routesToSecureStorage() = runBlocking {
        // Given
        val token = "eyJhbGciOiJIUzI1NiJ9.test"

        // When
        repository.setValue(SettingsKeys.AUTH_ACCESS_TOKEN, token)

        // Then
        assertEquals(token, repository.getValue(SettingsKeys.AUTH_ACCESS_TOKEN))
        assertEquals(token, fakeSecureStorage.get(SettingsKeys.AUTH_ACCESS_TOKEN))
        assertNull(fakeDao.getValue(SettingsKeys.AUTH_ACCESS_TOKEN))
    }

    @Test
    fun test_setValue_withNonSensitiveKey_routesToDao() = runBlocking {
        // Given
        val value = "https://example.com"

        // When
        repository.setValue(SettingsKeys.SERVER_URL, value)

        // Then
        assertEquals(value, repository.getValue(SettingsKeys.SERVER_URL))
        assertEquals(value, fakeDao.getValue(SettingsKeys.SERVER_URL))
        assertNull(fakeSecureStorage.get(SettingsKeys.SERVER_URL))
    }

    @Test
    fun test_setValue_withBlankSensitiveValue_removesFromSecureStorage() = runBlocking {
        // Given
        repository.setValue(SettingsKeys.AUTH_ACCESS_TOKEN, "token123")

        // When
        repository.setValue(SettingsKeys.AUTH_ACCESS_TOKEN, "")

        // Then
        assertNull(repository.getValue(SettingsKeys.AUTH_ACCESS_TOKEN))
    }

    @Test
    fun test_setValue_allSensitiveKeys_routeToSecureStorage() = runBlocking {
        // Given / When
        for (key in SettingsKeys.SENSITIVE_KEYS) {
            repository.setValue(key, "value_$key")
        }

        // Then
        for (key in SettingsKeys.SENSITIVE_KEYS) {
            assertEquals("value_$key", fakeSecureStorage.get(key))
            assertNull(fakeDao.getValue(key))
        }
    }
}
