package de.krisenvorrat.app.data.export

import de.krisenvorrat.app.data.db.entity.CategoryEntity
import de.krisenvorrat.app.data.db.entity.ItemEntity
import de.krisenvorrat.app.data.db.entity.LocationEntity
import de.krisenvorrat.app.data.db.entity.SettingsEntity
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import java.time.LocalDate

class JsonRoundtripTest {

    private fun buildRepository(
        categoryDao: FakeCategoryDao = FakeCategoryDao(),
        locationDao: FakeLocationDao = FakeLocationDao(),
        itemDao: FakeItemDao = FakeItemDao(),
        settingsDao: FakeSettingsDao = FakeSettingsDao()
    ) = ImportExportRepositoryImpl(
        categoryDao = categoryDao,
        locationDao = locationDao,
        itemDao = itemDao,
        settingsDao = settingsDao,
        transaction = passThroughTransaction
    )

    @Test
    fun test_roundtrip_multipleItemsWithAllFields_dataIsPreservedLosslessly() = runBlocking {
        // Given
        val categories = listOf(
            CategoryEntity(id = 1, name = "Lebensmittel"),
            CategoryEntity(id = 2, name = "Hygiene")
        )
        val locations = listOf(
            LocationEntity(id = 1, name = "Keller"),
            LocationEntity(id = 2, name = "Garage")
        )
        val items = listOf(
            ItemEntity(
                id = "item-1",
                name = "Konserve",
                categoryId = 1,
                quantity = 10.0,
                unit = "Stk",
                unitPrice = 2.49,
                kcalPerKg = 180,
                expiryDate = LocalDate.of(2027, 6, 15),
                locationId = 1,
                notes = "Ravioli",
                lastUpdated = 1700000000L
            ),
            ItemEntity(
                id = "item-2",
                name = "Seife",
                categoryId = 2,
                quantity = 3.0,
                unit = "Stk",
                unitPrice = 0.99,
                kcalPerKg = null,
                expiryDate = null,
                locationId = 2,
                notes = "",
                lastUpdated = 1700000001L
            )
        )
        val settings = listOf(
            SettingsEntity(key = "theme", value = "dark"),
            SettingsEntity(key = "language", value = "de")
        )

        // Export-Repository mit vorhandenen Daten
        val exportCategoryDao = FakeCategoryDao()
        val exportLocationDao = FakeLocationDao()
        val exportItemDao = FakeItemDao()
        val exportSettingsDao = FakeSettingsDao()
        exportCategoryDao.upsertAll(categories)
        exportLocationDao.upsertAll(locations)
        exportItemDao.upsertAll(items)
        exportSettingsDao.upsertAll(settings)

        val exportRepo = buildRepository(exportCategoryDao, exportLocationDao, exportItemDao, exportSettingsDao)

        // When – Export
        val json = exportRepo.exportToJson()

        // Then – Import in leeres Repository
        val importCategoryDao = FakeCategoryDao()
        val importLocationDao = FakeLocationDao()
        val importItemDao = FakeItemDao()
        val importSettingsDao = FakeSettingsDao()
        val importRepo = buildRepository(importCategoryDao, importLocationDao, importItemDao, importSettingsDao)

        val result = importRepo.importFromJson(json)

        // Then – Roundtrip ist verlustfrei
        assertTrue(result.isSuccess)

        // Categories
        assertEquals(categories.size, importCategoryDao.getItems().size)
        categories.forEach { original ->
            val imported = importCategoryDao.getItems().find { it.id == original.id }
            assertEquals(original.name, imported?.name)
        }

        // Locations
        assertEquals(locations.size, importLocationDao.getItems().size)
        locations.forEach { original ->
            val imported = importLocationDao.getItems().find { it.id == original.id }
            assertEquals(original.name, imported?.name)
        }

        // Items – alle Felder prüfen
        assertEquals(items.size, importItemDao.getItems().size)
        items.forEach { original ->
            val imported = importItemDao.getItems().find { it.id == original.id }
            assertEquals(original.name, imported?.name)
            assertEquals(original.categoryId, imported?.categoryId)
            assertEquals(original.quantity, imported?.quantity)
            assertEquals(original.unit, imported?.unit)
            assertEquals(original.unitPrice, imported?.unitPrice)
            assertEquals(original.kcalPerKg, imported?.kcalPerKg)
            assertEquals(original.expiryDate, imported?.expiryDate)
            assertEquals(original.locationId, imported?.locationId)
            assertEquals(original.notes, imported?.notes)
            assertEquals(original.lastUpdated, imported?.lastUpdated)
        }

        // Settings
        assertEquals(settings.size, importSettingsDao.getItems().size)
        settings.forEach { original ->
            val imported = importSettingsDao.getItems().find { it.key == original.key }
            assertEquals(original.value, imported?.value)
        }
    }

    @Test
    fun test_roundtrip_itemWithNullableFields_nullsArePreserved() = runBlocking {
        // Given
        val categoryDao = FakeCategoryDao()
        val locationDao = FakeLocationDao()
        val itemDao = FakeItemDao()
        val settingsDao = FakeSettingsDao()
        categoryDao.upsertAll(listOf(CategoryEntity(id = 1, name = "Test")))
        locationDao.upsertAll(listOf(LocationEntity(id = 1, name = "Ort")))
        itemDao.upsertAll(listOf(
            ItemEntity(
                id = "null-item",
                name = "Ohne Extras",
                categoryId = 1,
                quantity = 1.0,
                unit = "Stk",
                unitPrice = 0.0,
                kcalPerKg = null,
                expiryDate = null,
                locationId = 1,
                notes = "",
                lastUpdated = 0L
            )
        ))
        val exportRepo = buildRepository(categoryDao, locationDao, itemDao, settingsDao)

        // When
        val json = exportRepo.exportToJson()
        val importItemDao = FakeItemDao()
        val importCatDao = FakeCategoryDao()
        val importLocDao = FakeLocationDao()
        val importSetDao = FakeSettingsDao()
        val importRepo = buildRepository(importCatDao, importLocDao, importItemDao, importSetDao)
        val result = importRepo.importFromJson(json)

        // Then
        assertTrue(result.isSuccess)
        val imported = importItemDao.getItems().first()
        assertEquals(null, imported.kcalPerKg)
        assertEquals(null, imported.expiryDate)
    }

    @Test
    fun test_roundtrip_emptyDatabase_producesEmptyCollections() = runBlocking {
        // Given
        val exportRepo = buildRepository()

        // When
        val json = exportRepo.exportToJson()
        val importCategoryDao = FakeCategoryDao()
        val importLocationDao = FakeLocationDao()
        val importItemDao = FakeItemDao()
        val importSettingsDao = FakeSettingsDao()
        val importRepo = buildRepository(importCategoryDao, importLocationDao, importItemDao, importSettingsDao)
        val result = importRepo.importFromJson(json)

        // Then
        assertTrue(result.isSuccess)
        assertEquals(0, importCategoryDao.getItems().size)
        assertEquals(0, importLocationDao.getItems().size)
        assertEquals(0, importItemDao.getItems().size)
        assertEquals(0, importSettingsDao.getItems().size)
    }
}
