package bou.amine.apps.readerforselfossv2.repository

import bou.amine.apps.readerforselfossv2.dao.ITEM
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
import bou.amine.apps.readerforselfossv2.dao.SOURCE
import bou.amine.apps.readerforselfossv2.dao.TAG
import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.rest.SelfossApi
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import bou.amine.apps.readerforselfossv2.utils.ItemType
import bou.amine.apps.readerforselfossv2.utils.toView
import io.mockk.*
import junit.framework.TestCase.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertNotEquals
import org.junit.Before
import org.junit.Test

class RepositoryTest {
    private val db = mockk<ReaderForSelfossDB>(relaxed = true)
    private val appSettingsService = mockk<AppSettingsService>()
    private val api = mockk<SelfossApi>()

    private val NUMBER_ARTICLES = 100
    private val NUMBER_UNREAD = 50
    private val NUMBER_STARRED = 20
    private lateinit var repository: Repository


    private fun initializeRepository(
        isConnectionAvailable: MutableStateFlow<Boolean> = MutableStateFlow(
            true
        )
    ) {
        repository = Repository(api, appSettingsService, isConnectionAvailable, db)

        runBlocking {
            repository.updateApiVersion()
        }
    }

    @Before
    fun setup() {
        clearAllMocks()
        every { appSettingsService.getApiVersion() } returns 4
        every { appSettingsService.getBaseUrl() } returns "https://test.com/selfoss/"
        every { appSettingsService.isItemCachingEnabled() } returns false
        every { appSettingsService.isUpdateSourcesEnabled() } returns false

        coEvery { api.version() } returns SelfossModel.StatusAndData(
            success = true,
            data = SelfossModel.ApiVersion("2.19-ba1e8e3", "4.0.0")
        )
        coEvery { api.stats() } returns SelfossModel.StatusAndData(
            success = true,
            data = SelfossModel.Stats(NUMBER_ARTICLES, NUMBER_UNREAD, NUMBER_STARRED)
        )

        every { db.itemsQueries.items().executeAsList() } returns generateTestDBItems()
        every { db.tagsQueries.deleteAllTags() } returns Unit
        every { db.tagsQueries.transaction(any(), any()) } returns Unit
        every { db.tagsQueries.insertTag(any()) } returns Unit
    }

    @Test
    fun Instantiate_repository() {
        initializeRepository()

        coVerify(exactly = 1) { api.version() }
    }

    @Test
    fun Instantiate_repository_without_api_version() {
        every { appSettingsService.getApiVersion() } returns -1

        initializeRepository(MutableStateFlow(false))

        coVerify(exactly = 0) { api.version() }
        coVerify(exactly = 0) { api.stats() }
    }

    @Test
    fun Get_api_4_date_with_api_1_version_stored() {
        every { appSettingsService.getApiVersion() } returns 1
        coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
                SelfossModel.StatusAndData(success = true, data = generateTestApiItem())
        every { appSettingsService.updateApiVersion(any()) } returns Unit

        initializeRepository()
        runBlocking {
            repository.getNewerItems()
        }

        assertSame(repository.items.size, 1)
        verify(exactly = 1) { appSettingsService.updateApiVersion(4) }
    }

    @Test
    fun Get_api_1_date_with_api_4_version_stored() {
        every { appSettingsService.getApiVersion() } returns 4
        coEvery { api.version() } returns SelfossModel.StatusAndData(success = false, null)
        val itemParameters = FakeItemParameters()
        itemParameters.datetime = "2021-04-23 11:45:32"
        coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
                SelfossModel.StatusAndData(
                    success = true,
                    data = generateTestApiItem(itemParameters)
                )

        initializeRepository()
        runBlocking {
            repository.getNewerItems()
        }

        assertSame(1, repository.items.size)
    }

    @Test
    fun Get_newer_items() {
        coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
                SelfossModel.StatusAndData(success = true, data = generateTestApiItem())

        initializeRepository()
        runBlocking {
            repository.getNewerItems()
        }

        assertSame(repository.items.size, 1)
        coVerify(exactly = 1) { api.getItems("unread", 0, null, null, null, null, any()) }
        verify(exactly = 0) { db.itemsQueries.items().executeAsList() }
    }

    @Test
    fun Get_all_newer_items() {
        coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
                SelfossModel.StatusAndData(success = true, data = generateTestApiItem())

        initializeRepository()
        repository.displayedItems = ItemType.ALL
        runBlocking {
            repository.getNewerItems()
        }

        assertSame(repository.items.size, 1)
        coVerify(exactly = 1) { api.getItems("all", 0, null, null, null, null, any()) }
        verify(exactly = 0) { db.itemsQueries.items().executeAsList() }
    }

    @Test
    fun Get_newer_starred_items() {
        coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
                SelfossModel.StatusAndData(success = true, data = generateTestApiItem())

        initializeRepository()
        repository.displayedItems = ItemType.STARRED
        runBlocking {
            repository.getNewerItems()
        }

        assertSame(repository.items.size, 1)
        coVerify(exactly = 1) { api.getItems("starred", 0, null, null, null, null, any()) }
        verify(exactly = 0) { db.itemsQueries.items().executeAsList() }
    }

    @Test
    fun Get_newer_items_without_connectivity() {
        every { appSettingsService.isItemCachingEnabled() } returns true

        initializeRepository(MutableStateFlow(false))
        runBlocking {
            repository.getNewerItems()
        }

        assertSame(repository.items.size, 1)
        coVerify(exactly = 0) { api.getItems("unread", 0, null, null, null, null, any()) }
        verify(atLeast = 1) { db.itemsQueries.items().executeAsList() }
    }

    @Test
    fun Get_newer_items_without_connectivity_and_tag_filter() {
        val itemParameter1 = FakeItemParameters()
        val itemParameter2 = FakeItemParameters()
        val itemParameter3 = FakeItemParameters()
        itemParameter2.tags = "Test, Stuff"
        itemParameter2.id = "2"
        itemParameter3.tags = "Other, Tag"
        itemParameter3.id = "3"
        coEvery { db.itemsQueries.items().executeAsList() } returns generateTestDBItems(
            itemParameter1
        ) +
                generateTestDBItems(itemParameter2) +
                generateTestDBItems(itemParameter3)

        every { appSettingsService.isItemCachingEnabled() } returns true

        initializeRepository(MutableStateFlow(false))
        repository.tagFilter = SelfossModel.Tag("Test", "red", 3)
        runBlocking {
            repository.getNewerItems()
        }

        assertSame(repository.items.size, 1)
        coVerify(exactly = 0) { api.getItems("unread", 0, null, null, null, null, any()) }
        verify(atLeast = 1) { db.itemsQueries.items().executeAsList() }
    }

    @Test
    fun Get_newer_items_without_connectivity_and_source_filter() {
        val itemParameter1 = FakeItemParameters()
        val itemParameter2 = FakeItemParameters()
        val itemParameter3 = FakeItemParameters()
        itemParameter2.sourcetitle = "Test"
        itemParameter2.id = "2"
        itemParameter3.sourcetitle = "Other"
        itemParameter3.id = "3"
        coEvery { db.itemsQueries.items().executeAsList() } returns generateTestDBItems(
            itemParameter1
        ) +
                generateTestDBItems(itemParameter2) +
                generateTestDBItems(itemParameter3)

        every { appSettingsService.isItemCachingEnabled() } returns true

        initializeRepository(MutableStateFlow(false))
        repository.sourceFilter = SelfossModel.Source(
            1,
            "Test",
            listOf("tags"),
            "spouts\\rss\\fulltextrss",
            "",
            "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
        )
        runBlocking {
            repository.getNewerItems()
        }

        assertSame(repository.items.size, 1)
        coVerify(exactly = 0) { api.getItems("unread", 0, null, null, null, null, any()) }
        verify(atLeast = 1) { db.itemsQueries.items().executeAsList() }
    }

    @Test
    fun Get_older_items() {
        coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
                SelfossModel.StatusAndData(success = true, data = generateTestApiItem())

        initializeRepository()
        repository.items = ArrayList(generateTestApiItem())
        runBlocking {
            repository.getOlderItems()
        }

        assertSame(repository.items.size, 2)
        coVerify(exactly = 1) { api.getItems("unread", 1, null, null, null, null, any()) }
        verify(exactly = 0) { db.itemsQueries.items().executeAsList() }
    }

    @Test
    fun Get_all_older_items() {
        coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
                SelfossModel.StatusAndData(success = true, data = generateTestApiItem())

        initializeRepository()
        repository.items = ArrayList(generateTestApiItem())
        repository.displayedItems = ItemType.ALL
        runBlocking {
            repository.getOlderItems()
        }

        assertSame(repository.items.size, 2)
        coVerify(exactly = 1) { api.getItems("all", 1, null, null, null, null, any()) }
        verify(exactly = 0) { db.itemsQueries.items().executeAsList() }
    }

    @Test
    fun Get_older_starred_items() {
        coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
                SelfossModel.StatusAndData(success = true, data = generateTestApiItem())

        initializeRepository()
        repository.displayedItems = ItemType.STARRED
        repository.items = ArrayList(generateTestApiItem())
        runBlocking {
            repository.getOlderItems()
        }

        assertSame(repository.items.size, 2)
        coVerify(exactly = 1) { api.getItems("starred", 1, null, null, null, null, any()) }
        verify(exactly = 0) { db.itemsQueries.items().executeAsList() }
    }

    @Test
    fun Reload_badges() {
        var success = false

        initializeRepository()
        runBlocking {
            success = repository.reloadBadges()
        }

        assertSame(true, success)
        assertSame(NUMBER_ARTICLES, repository.badgeAll)
        assertSame(NUMBER_UNREAD, repository.badgeUnread)
        assertSame(NUMBER_STARRED, repository.badgeStarred)
        coVerify(atLeast = 1) { api.stats() }
        verify(exactly = 0) { db.itemsQueries.items().executeAsList() }
    }

    @Test
    fun Reload_badges_without_response() {
        coEvery { api.stats() } returns SelfossModel.StatusAndData(success = false, data = null)

        var success = false

        initializeRepository()
        runBlocking {
            success = repository.reloadBadges()
        }

        assertSame(false, success)
        assertSame(0, repository.badgeAll)
        assertSame(0, repository.badgeUnread)
        assertSame(0, repository.badgeStarred)
        coVerify(atLeast = 1) { api.stats() }
        verify(exactly = 0) { db.itemsQueries.items().executeAsList() }
    }

    @Test
    fun Reload_badges_without_connection() {
        every { appSettingsService.isItemCachingEnabled() } returns true
        every { db.itemsQueries.items().executeAsList() } returns generateTestDBItems()

        var success = false

        initializeRepository(MutableStateFlow(false))
        runBlocking {
            success = repository.reloadBadges()
        }

        assertTrue(success)
        assertSame(1, repository.badgeAll)
        assertSame(1, repository.badgeUnread)
        assertSame(1, repository.badgeStarred)
        coVerify(exactly = 0) { api.stats() }
        verify(atLeast = 1) { db.itemsQueries.items().executeAsList() }
    }

    @Test
    fun Reload_badges_without_connection_and_items_caching_disabled() {
        every { appSettingsService.isItemCachingEnabled() } returns false
        every { appSettingsService.isUpdateSourcesEnabled() } returns true

        var success = false

        initializeRepository(MutableStateFlow(false))
        runBlocking {
            success = repository.reloadBadges()
        }

        assertFalse(success)
        assertSame(0, repository.badgeAll)
        assertSame(0, repository.badgeUnread)
        assertSame(0, repository.badgeStarred)
        coVerify(exactly = 0) { api.stats() }
        verify(exactly = 0) { db.itemsQueries.items().executeAsList() }
    }

    @Test
    fun Get_tags() {
        val tags = listOf(
            SelfossModel.Tag("test", "red", 6),
            SelfossModel.Tag("second", "yellow", 0)
        )
        val tagsDB = listOf(
            TAG("test_DB", "red", 6),
            TAG("second_DB", "yellow", 0)
        )

        coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags)
        coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
        every { appSettingsService.isUpdateSourcesEnabled() } returns true
        every { appSettingsService.isItemCachingEnabled() } returns true

        initializeRepository()
        var testTags: List<SelfossModel.Tag>? = null
        runBlocking {
            testTags = repository.getTags()
        }

        assertSame(tags, testTags)
        assertNotSame(tagsDB.map { it.toView() }, testTags)
        coVerify(exactly = 1) { api.tags() }
    }

    @Test
    fun Get_tags_with_sources_update_disabled() {
        val tags = listOf(
            SelfossModel.Tag("test", "red", 6),
            SelfossModel.Tag("second", "yellow", 0)
        )
        val tagsDB = listOf(
            TAG("test_DB", "red", 6),
            TAG("second_DB", "yellow", 0)
        )

        coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags)
        coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
        every { appSettingsService.isUpdateSourcesEnabled() } returns false
        every { appSettingsService.isItemCachingEnabled() } returns true

        initializeRepository()
        var testTags: List<SelfossModel.Tag> = emptyList()
        runBlocking {
            testTags = repository.getTags()
            // Tags will be fetched from the database on the second call, thus testTags != tags
            testTags = repository.getTags()
        }

        coVerify(exactly = 1) { api.tags() }
        assertNotSame(tags, testTags)
        assertEquals(tagsDB.map { it.toView() }, testTags)
        verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() }
    }

    @Test
    fun Get_tags_with_items_caching_disabled() {
        val tags = listOf(
            SelfossModel.Tag("test", "red", 6),
            SelfossModel.Tag("second", "yellow", 0)
        )
        val tagsDB = listOf(
            TAG("test_DB", "red", 6),
            TAG("second_DB", "yellow", 0)
        )

        coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags)
        coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
        every { appSettingsService.isUpdateSourcesEnabled() } returns true
        every { appSettingsService.isItemCachingEnabled() } returns false

        initializeRepository()
        var testTags: List<SelfossModel.Tag> = emptyList()
        runBlocking {
            testTags = repository.getTags()
        }

        assertSame(tags, testTags)
        coVerify(exactly = 1) { api.tags() }
        verify(exactly = 0) { db.tagsQueries.tags().executeAsList() }
    }

    @Test
    fun Get_tags_with_sources_update_and_items_caching_disabled() {
        val tags = listOf(
            SelfossModel.Tag("test", "red", 6),
            SelfossModel.Tag("second", "yellow", 0)
        )
        val tagsDB = listOf(
            TAG("test_DB", "red", 6),
            TAG("second_DB", "yellow", 0)
        )

        coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags)
        coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
        every { appSettingsService.isUpdateSourcesEnabled() } returns false
        every { appSettingsService.isItemCachingEnabled() } returns false

        initializeRepository()
        var testTags: List<SelfossModel.Tag> = emptyList()
        runBlocking {
            testTags = repository.getTags()
            testTags = repository.getTags()
        }

        coVerify(exactly = 1) { api.tags() }
        assertNotSame(tags, testTags)
        assertEquals(tagsDB.map { it.toView() }, testTags)
        verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() }
    }

    @Test
    fun Get_tags_without_connection() {
        val tags = listOf(
            SelfossModel.Tag("test", "red", 6),
            SelfossModel.Tag("second", "yellow", 0)
        )
        val tagsDB = listOf(
            TAG("test_DB", "red", 6),
            TAG("second_DB", "yellow", 0)
        )

        coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags)
        coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
        every { appSettingsService.isUpdateSourcesEnabled() } returns true
        every { appSettingsService.isItemCachingEnabled() } returns true

        initializeRepository(MutableStateFlow(false))
        var testTags: List<SelfossModel.Tag> = emptyList()
        runBlocking {
            testTags = repository.getTags()
        }

        assertNotSame(tags, testTags)
        assertEquals(tagsDB.map { it.toView() }, testTags)
        coVerify(exactly = 0) { api.tags() }
        verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() }
    }

    @Test
    fun Get_tags_without_connection_and_items_caching_disabled() {
        val tags = listOf(
            SelfossModel.Tag("test", "red", 6),
            SelfossModel.Tag("second", "yellow", 0)
        )
        val tagsDB = listOf(
            TAG("test_DB", "red", 6),
            TAG("second_DB", "yellow", 0)
        )

        coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags)
        coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
        every { appSettingsService.isItemCachingEnabled() } returns false
        every { appSettingsService.isUpdateSourcesEnabled() } returns true

        initializeRepository(MutableStateFlow(false))
        var testTags: List<SelfossModel.Tag> = emptyList()
        runBlocking {
            testTags = repository.getTags()
        }

        assertSame(emptyList<SelfossModel.Tag>(), testTags)
        coVerify(exactly = 0) { api.tags() }
        verify(exactly = 0) { db.tagsQueries.tags().executeAsList() }
    }

    @Test
    fun Get_tags_without_connection_and_sources_update_disabled() {
        val tags = listOf(
            SelfossModel.Tag("test", "red", 6),
            SelfossModel.Tag("second", "yellow", 0)
        )
        val tagsDB = listOf(
            TAG("test_DB", "red", 6),
            TAG("second_DB", "yellow", 0)
        )

        coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags)
        coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
        every { appSettingsService.isUpdateSourcesEnabled() } returns false
        every { appSettingsService.isItemCachingEnabled() } returns true

        initializeRepository(MutableStateFlow(false))
        var testTags: List<SelfossModel.Tag> = emptyList()
        runBlocking {
            testTags = repository.getTags()
        }

        assertNotSame(tags, testTags)
        assertEquals(tagsDB.map { it.toView() }, testTags)
        coVerify(exactly = 0) { api.tags() }
        verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() }
    }

    @Test
    fun Get_tags_without_connection_and_sources_update_and_items_caching_disabled() {
        val tags = listOf(
            SelfossModel.Tag("test", "red", 6),
            SelfossModel.Tag("second", "yellow", 0)
        )
        val tagsDB = listOf(
            TAG("test_DB", "red", 6),
            TAG("second_DB", "yellow", 0)
        )

        coEvery { api.tags() } returns SelfossModel.StatusAndData(success = true, data = tags)
        coEvery { db.tagsQueries.tags().executeAsList() } returns tagsDB
        every { appSettingsService.isUpdateSourcesEnabled() } returns false
        every { appSettingsService.isItemCachingEnabled() } returns false

        initializeRepository(MutableStateFlow(false))
        var testTags: List<SelfossModel.Tag> = emptyList()
        runBlocking {
            testTags = repository.getTags()
        }

        assertEquals(tagsDB.map { it.toView() }, testTags)
        coVerify(exactly = 0) { api.tags() }
        verify(atLeast = 1) { db.tagsQueries.tags().executeAsList() }
    }

    @Test
    fun get_sources() {
        val sources = arrayListOf(
            SelfossModel.Source(
                1,
                "First source",
                listOf("Test", "second"),
                "spouts\\rss\\fulltextrss",
                "",
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
            ),
            SelfossModel.Source(
                2,
                "Second source",
                listOf("second"),
                "spouts\\rss\\fulltextrss",
                "",
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
            )
        )
        val sourcesDB = listOf(
            SOURCE(
                "1",
                "First DB source",
                "Test,second",
                "spouts\\rss\\fulltextrss",
                "",
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
            ),
            SOURCE(
                "2",
                "Second source",
                "second",
                "spouts\\rss\\fulltextrss",
                "",
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
            )
        )

        coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
        every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
        initializeRepository()
        var testSources: List<SelfossModel.Source>? = null
        runBlocking {
            testSources = repository.getSources()
        }

        assertSame(sources, testSources)
        assertNotEquals(sourcesDB.map { it.toView() }, testSources)
        coVerify(exactly = 1) { api.sources() }
    }

    @Test
    fun get_sources_with_sources_update_disabled() {
        val sources = arrayListOf(
            SelfossModel.Source(
                1,
                "First source",
                listOf("Test", "second"),
                "spouts\\rss\\fulltextrss",
                "",
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
            ),
            SelfossModel.Source(
                2,
                "Second DB source",
                listOf("second"),
                "spouts\\rss\\fulltextrss",
                "",
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
            )
        )
        val sourcesDB = listOf(
            SOURCE(
                "1",
                "First source",
                "Test,second",
                "spouts\\rss\\fulltextrss",
                "",
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
            ),
            SOURCE(
                "2",
                "Second source",
                "second",
                "spouts\\rss\\fulltextrss",
                "",
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
            )
        )

        every { appSettingsService.isUpdateSourcesEnabled() } returns false
        every { appSettingsService.isItemCachingEnabled() } returns true
        coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
        every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
        initializeRepository()
        var testSources: List<SelfossModel.Source>? = null
        runBlocking {
            testSources = repository.getSources()
            // Sources will be fetched from the database on the second call, thus testSources != sources
            testSources = repository.getSources()
        }

        coVerify(exactly = 1) { api.sources() }
        assertNotSame(sources, testSources)
        assertEquals(sourcesDB.map { it.toView() }, testSources)
        verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
    }

    @Test
    fun get_sources_with_items_caching_disabled() {
        val sources = arrayListOf(
            SelfossModel.Source(
                1,
                "First source",
                listOf("Test", "second"),
                "spouts\\rss\\fulltextrss",
                "",
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
            ),
            SelfossModel.Source(
                2,
                "Second source",
                listOf("second"),
                "spouts\\rss\\fulltextrss",
                "",
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
            )
        )
        val sourcesDB = listOf(
            SOURCE(
                "1",
                "First source",
                "Test,second",
                "spouts\\rss\\fulltextrss",
                "",
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
            ),
            SOURCE(
                "2",
                "Second source",
                "second",
                "spouts\\rss\\fulltextrss",
                "",
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
            )
        )

        every { appSettingsService.isUpdateSourcesEnabled() } returns true
        every { appSettingsService.isItemCachingEnabled() } returns false
        coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
        every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
        initializeRepository()
        var testSources: List<SelfossModel.Source>? = null
        runBlocking {
            testSources = repository.getSources()
        }

        assertSame(sources, testSources)
        coVerify(exactly = 1) { api.sources() }
        verify(exactly = 0) { db.sourcesQueries }
    }

    @Test
    fun get_sources_with_sources_update_and_items_caching_disabled() {
        val sources = arrayListOf(
            SelfossModel.Source(
                1,
                "First source",
                listOf("Test", "second"),
                "spouts\\rss\\fulltextrss",
                "",
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
            ),
            SelfossModel.Source(
                2,
                "Second source",
                listOf("second"),
                "spouts\\rss\\fulltextrss",
                "",
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
            )
        )
        val sourcesDB = listOf(
            SOURCE(
                "1",
                "First source",
                "Test,second",
                "spouts\\rss\\fulltextrss",
                "",
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
            ),
            SOURCE(
                "2",
                "Second source",
                "second",
                "spouts\\rss\\fulltextrss",
                "",
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
            )
        )

        every { appSettingsService.isUpdateSourcesEnabled() } returns false
        every { appSettingsService.isItemCachingEnabled() } returns false
        coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
        every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
        initializeRepository()
        var testSources: List<SelfossModel.Source>? = null
        runBlocking {
            testSources = repository.getSources()
        }

        assertSame(sources, testSources)
        coVerify(exactly = 1) { api.sources() }
        verify(atLeast = 1) { db.sourcesQueries }
    }

    @Test
    fun get_sources_without_connection() {
        val sources = arrayListOf(
            SelfossModel.Source(
                1,
                "First source",
                listOf("Test", "second"),
                "spouts\\rss\\fulltextrss",
                "",
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
            ),
            SelfossModel.Source(
                2,
                "Second source",
                listOf("second"),
                "spouts\\rss\\fulltextrss",
                "",
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
            )
        )
        val sourcesDB = listOf(
            SOURCE(
                "1",
                "First DB source",
                "Test,second",
                "spouts\\rss\\fulltextrss",
                "",
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
            ),
            SOURCE(
                "2",
                "Second source",
                "second",
                "spouts\\rss\\fulltextrss",
                "",
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
            )
        )

        coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
        every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
        initializeRepository(MutableStateFlow(false))
        var testSources: List<SelfossModel.Source>? = null
        runBlocking {
            testSources = repository.getSources()
        }

        assertEquals(sourcesDB.map { it.toView() }, testSources)
        coVerify(exactly = 0) { api.sources() }
        verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
    }

    @Test
    fun get_sources_without_connection_and_items_caching_disabled() {
        val sources = arrayListOf(
            SelfossModel.Source(
                1,
                "First source",
                listOf("Test", "second"),
                "spouts\\rss\\fulltextrss",
                "",
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
            ),
            SelfossModel.Source(
                2,
                "Second source",
                listOf("second"),
                "spouts\\rss\\fulltextrss",
                "",
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
            )
        )
        val sourcesDB = listOf(
            SOURCE(
                "1",
                "First DB source",
                "Test,second",
                "spouts\\rss\\fulltextrss",
                "",
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
            ),
            SOURCE(
                "2",
                "Second source",
                "second",
                "spouts\\rss\\fulltextrss",
                "",
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
            )
        )

        every { appSettingsService.isItemCachingEnabled() } returns false
        every { appSettingsService.isUpdateSourcesEnabled() } returns true
        coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
        every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
        initializeRepository(MutableStateFlow(false))
        var testSources: List<SelfossModel.Source>? = null
        runBlocking {
            testSources = repository.getSources()
        }

        assertEquals(emptyList<SelfossModel.Source>(), testSources)
        coVerify(exactly = 0) { api.sources() }
        verify(exactly = 0) { db.sourcesQueries.sources().executeAsList() }
    }

    @Test
    fun get_sources_without_connection_and_sources_update_disabled() {
        val sources = arrayListOf(
            SelfossModel.Source(
                1,
                "First source",
                listOf("Test", "second"),
                "spouts\\rss\\fulltextrss",
                "",
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
            ),
            SelfossModel.Source(
                2,
                "Second source",
                listOf("second"),
                "spouts\\rss\\fulltextrss",
                "",
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
            )
        )
        val sourcesDB = listOf(
            SOURCE(
                "1",
                "First DB source",
                "Test,second",
                "spouts\\rss\\fulltextrss",
                "",
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
            ),
            SOURCE(
                "2",
                "Second source",
                "second",
                "spouts\\rss\\fulltextrss",
                "",
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
            )
        )

        every { appSettingsService.isItemCachingEnabled() } returns true
        every { appSettingsService.isUpdateSourcesEnabled() } returns false
        coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
        every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
        initializeRepository(MutableStateFlow(false))
        var testSources: List<SelfossModel.Source>? = null
        runBlocking {
            testSources = repository.getSources()
        }

        assertEquals(sourcesDB.map { it.toView() }, testSources)
        coVerify(exactly = 0) { api.sources() }
        verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
    }

    @Test
    fun get_sources_without_connection_and_items_caching_and_sources_update_disabled() {
        val sources = arrayListOf(
            SelfossModel.Source(
                1,
                "First source",
                listOf("Test", "second"),
                "spouts\\rss\\fulltextrss",
                "",
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
            ),
            SelfossModel.Source(
                2,
                "Second source",
                listOf("second"),
                "spouts\\rss\\fulltextrss",
                "",
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
            )
        )
        val sourcesDB = listOf(
            SOURCE(
                "1",
                "First DB source",
                "Test,second",
                "spouts\\rss\\fulltextrss",
                "",
                "d8c92cdb1ef119ea85c4b9205c879ca7.png"
            ),
            SOURCE(
                "2",
                "Second source",
                "second",
                "spouts\\rss\\fulltextrss",
                "",
                "b3aa8a664d08eb15d6ff1db2fa83e0d9.png"
            )
        )

        every { appSettingsService.isItemCachingEnabled() } returns false
        every { appSettingsService.isUpdateSourcesEnabled() } returns false
        coEvery { api.sources() } returns SelfossModel.StatusAndData(success = true, data = sources)
        every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
        initializeRepository(MutableStateFlow(false))
        var testSources: List<SelfossModel.Source>? = null
        runBlocking {
            testSources = repository.getSources()
        }

        assertEquals(sourcesDB.map { it.toView() }, testSources)
        coVerify(exactly = 0) { api.sources() }
        verify(atLeast = 1) { db.sourcesQueries.sources().executeAsList() }
    }

    @Test
    fun create_source() {
        coEvery { api.createSourceForVersion(any(), any(), any(), any(), any(), any()) } returns
                SelfossModel.SuccessResponse(true)

        initializeRepository()
        var response = false
        runBlocking {
            response = repository.createSource(
                "test",
                "https://test.com/feed",
                "spouts\\rss\\fulltextrss",
                "Test, New",
                ""
            )
        }

        coVerify(exactly = 1) {
            api.createSourceForVersion(
                any(),
                any(),
                any(),
                any(),
                any(),
                any()
            )
        }
        assertSame(true, response)
    }

    @Test
    fun create_source_but_response_fails() {
        coEvery { api.createSourceForVersion(any(), any(), any(), any(), any(), any()) } returns
                SelfossModel.SuccessResponse(false)

        initializeRepository()
        var response = false
        runBlocking {
            response = repository.createSource(
                "test",
                "https://test.com/feed",
                "spouts\\rss\\fulltextrss",
                "Test, New",
                ""
            )
        }

        coVerify(exactly = 1) {
            api.createSourceForVersion(
                any(),
                any(),
                any(),
                any(),
                any(),
                any()
            )
        }
        assertSame(false, response)
    }

    @Test
    fun create_source_without_connection() {
        coEvery { api.createSourceForVersion(any(), any(), any(), any(), any(), any()) } returns
                SelfossModel.SuccessResponse(true)

        initializeRepository(MutableStateFlow(false))
        var response = false
        runBlocking {
            response = repository.createSource(
                "test",
                "https://test.com/feed",
                "spouts\\rss\\fulltextrss",
                "Test, New",
                ""
            )
        }

        coVerify(exactly = 0) {
            api.createSourceForVersion(
                any(),
                any(),
                any(),
                any(),
                any(),
                any()
            )
        }
        assertSame(false, response)
    }

    @Test
    fun delete_source() {
        coEvery { api.deleteSource(any()) } returns SelfossModel.SuccessResponse(true)

        initializeRepository()
        var response = false
        runBlocking {
            response = repository.deleteSource(5)
        }

        coVerify(exactly = 1) { api.deleteSource(5) }
        assertSame(true, response)
    }

    @Test
    fun delete_source_but_response_fails() {
        coEvery { api.deleteSource(any()) } returns SelfossModel.SuccessResponse(false)

        initializeRepository()
        var response = false
        runBlocking {
            response = repository.deleteSource(5)
        }

        coVerify(exactly = 1) { api.deleteSource(5) }
        assertSame(false, response)
    }

    @Test
    fun delete_source_without_connection() {
        coEvery { api.deleteSource(any()) } returns SelfossModel.SuccessResponse(false)

        initializeRepository(MutableStateFlow(false))
        var response = false
        runBlocking {
            response = repository.deleteSource(5)
        }

        coVerify(exactly = 0) { api.deleteSource(5) }
        assertSame(false, response)
    }

    @Test
    fun update_remote() {
        coEvery { api.update() } returns SelfossModel.StatusAndData(
            success = true,
            data = "finished"
        )

        initializeRepository()
        var response = false
        runBlocking {
            response = repository.updateRemote()
        }

        coVerify(exactly = 1) { api.update() }
        assertTrue(response)
    }

    @Test
    fun update_remote_but_response_fails() {
        coEvery { api.update() } returns SelfossModel.StatusAndData(
            success = false,
            data = "unallowed access"
        )

        initializeRepository()
        var response = false
        runBlocking {
            response = repository.updateRemote()
        }

        coVerify(exactly = 1) { api.update() }
        assertSame(false, response)
    }

    @Test
    fun update_remote_with_unallowed_access() {
        coEvery { api.update() } returns SelfossModel.StatusAndData(
            success = true,
            data = "unallowed access"
        )

        initializeRepository()
        var response = false
        runBlocking {
            response = repository.updateRemote()
        }

        coVerify(exactly = 1) { api.update() }
        assertSame(false, response)
    }

    @Test
    fun update_remote_without_connection() {
        coEvery { api.update() } returns SelfossModel.StatusAndData(
            success = true,
            data = "undocumented..."
        )

        initializeRepository(MutableStateFlow(false))
        var response = false
        runBlocking {
            response = repository.updateRemote()
        }

        coVerify(exactly = 0) { api.update() }
        assertSame(false, response)
    }

    @Test
    fun login() {
        coEvery { api.login() } returns SelfossModel.SuccessResponse(success = true)

        initializeRepository()
        var response = false
        runBlocking {
            response = repository.login()
        }

        coVerify(exactly = 1) { api.login() }
        assertSame(true, response)
    }

    @Test
    fun login_but_response_fails() {
        coEvery { api.login() } returns SelfossModel.SuccessResponse(success = false)

        initializeRepository()
        var response = false
        runBlocking {
            response = repository.login()
        }

        coVerify(exactly = 1) { api.login() }
        assertSame(false, response)
    }

    @Test
    fun login_but_without_connection() {
        coEvery { api.login() } returns SelfossModel.SuccessResponse(success = true)

        initializeRepository(MutableStateFlow(false))
        var response = false
        runBlocking {
            response = repository.login()
        }

        coVerify(exactly = 0) { api.login() }
        assertSame(false, response)
    }

    @Test
    fun refresh_login_information() {
        coEvery { api.refreshLoginInformation() } returns Unit
        coEvery { appSettingsService.refreshLoginInformation(any(), any(), any()) } returns Unit

        initializeRepository()
        repository.refreshLoginInformation("https://test.com/selfoss/", "login", "password")

        coVerify(exactly = 1) { api.refreshLoginInformation() }
        coVerify(exactly = 1) {
            appSettingsService.refreshLoginInformation(
                "https://test.com/selfoss/",
                "login",
                "password"
            )
        }
    }

    @Test
    fun cache_items() {
        val itemParameter1 = FakeItemParameters()
        val itemParameter2 = FakeItemParameters()
        val itemParameter3 = FakeItemParameters()
        itemParameter2.id = "2"
        itemParameter3.id = "3"
        coEvery {
            api.getItems(
                any(),
                any(),
                any(),
                any(),
                any(),
                any(),
                any()
            )
        } returnsMany listOf(
            SelfossModel.StatusAndData(success = true, data = generateTestApiItem(itemParameter1)),
            SelfossModel.StatusAndData(success = true, data = generateTestApiItem(itemParameter2)),
            SelfossModel.StatusAndData(success = true, data = generateTestApiItem(itemParameter1)),
        )

        initializeRepository()
        repository.tagFilter = SelfossModel.Tag("Tag", "read", 0)
        repository.sourceFilter = SelfossModel.Source(
            1,
            "First source",
            listOf("Test", "second"),
            "spouts\\rss\\fulltextrss",
            "",
            "d8c92cdb1ef119ea85c4b9205c879ca7.png"
        )
        repository.searchFilter = "search"
        runBlocking {
            repository.tryToCacheItemsAndGetNewOnes()
        }

        coVerify(exactly = 3) { api.getItems(any(), 0, null, null, null, null, 200) }
    }

    @Test
    fun cache_items_but_response_fails() {
        coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
                SelfossModel.StatusAndData(success = false, data = generateTestApiItem())

        initializeRepository()
        repository.tagFilter = SelfossModel.Tag("Tag", "read", 0)
        repository.sourceFilter = SelfossModel.Source(
            1,
            "First source",
            listOf("Test", "second"),
            "spouts\\rss\\fulltextrss",
            "",
            "d8c92cdb1ef119ea85c4b9205c879ca7.png"
        )
        repository.searchFilter = "search"
        runBlocking {
            repository.tryToCacheItemsAndGetNewOnes()
        }

        coVerify(exactly = 3) { api.getItems(any(), 0, null, null, null, null, 200) }
    }

    @Test
    fun cache_items_without_connection() {
        coEvery { api.getItems(any(), any(), any(), any(), any(), any(), any()) } returns
                SelfossModel.StatusAndData(success = false, data = generateTestApiItem())

        initializeRepository(MutableStateFlow(false))
        repository.tagFilter = SelfossModel.Tag("Tag", "read", 0)
        repository.sourceFilter = SelfossModel.Source(
            1,
            "First source",
            listOf("Test", "second"),
            "spouts\\rss\\fulltextrss",
            "",
            "d8c92cdb1ef119ea85c4b9205c879ca7.png"
        )
        repository.searchFilter = "search"
        runBlocking {
            repository.tryToCacheItemsAndGetNewOnes()
        }

        coVerify(exactly = 0) { api.getItems(any(), 0, null, null, null, null, 200) }
    }
}

fun generateTestDBItems(item: FakeItemParameters = FakeItemParameters()): List<ITEM> {
    return listOf(
        ITEM(
            id = item.id,
            datetime = item.datetime,
            title = item.title,
            content = item.content,
            unread = item.unread,
            starred = item.starred,
            thumbnail = item.thumbnail,
            icon = item.icon,
            link = item.link,
            sourcetitle = item.sourcetitle,
            tags = item.tags
        )
    )
}

fun generateTestApiItem(item: FakeItemParameters = FakeItemParameters()): List<SelfossModel.Item> {
    return listOf(
        SelfossModel.Item(
            id = item.id.toInt(),
            datetime = item.datetime,
            title = item.title,
            content = item.content,
            unread = item.unread,
            starred = item.starred,
            thumbnail = item.thumbnail,
            icon = item.icon,
            link = item.link,
            sourcetitle = item.sourcetitle,
            tags = item.tags.split(',')
        )
    )
}

class FakeItemParameters {
    var id = "20"
    var datetime = "2022-09-09T03:32:01-04:00"
    val title = "Etica della ricerca sotto i riflettori."
    val content =
        "<p><strong>Luigi Campanella, già Presidente SCI</strong></p>\n<p>L’etica della scienza è di certo ambito di cui continuiamo a scoprire nuovi aspetti e risvolti.</p>\n<p>L’ultimo è quello delle intelligenze artificiali capaci di creare opere complesse basate su immagini e parole memorizzate con il rischio di fake news e di contenuti disturbanti.</p>\n<p>Per evitare che ciò accada si sta procedendo filtrando secondo criteri di autocensura i dati da cui l’intelligenza artificiale parte.</p>\n<p>Comincia ad intravedersi un futuro prossimo di competizione fra autori umani ed artificiali nel quale sarà importante, quando i loro prodotti saranno indistinguibili, dichiararne l’origine.</p>\n<p>Come si comprende, si conferma che gli aspetti etici dell’innovazione e della ricerca si diversificato sempre di più.</p>\n<p>La biologia molecolare e la genetica già in passato hanno posto all’attenzione comune aspetti di etica della scienza che hanno indotto a nuove riflessioni circa i limiti delle ricerche.</p>\n<p>L’argomento, sempre attuale, torna sulle prime pagine a seguito della pubblicazione di una ricerca della Università di Cambridge che ha sviluppato una struttura cellulare di un topo con un cuore che batte regolarmente.</p>\n<img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image002-1.png?w=481\" alt=\"\" width=\"697\" height=\"430\" /><img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image003-1.png?w=906\" alt=\"\" /><p>Magdalena Zernicka-Goetz</p>\n<img src=\"https://ilblogdellasci.files.wordpress.com/2022/09/image004.jpg?w=474\" alt=\"\" width=\"622\" height=\"465\" /><p>Gianluca Amadei</p>\n<p>Del gruppo fa parte anche uno scienziato italiano Gianluca Amadei,che dinnanzi alle obiezioni di natura etica sulla realizzazione della vita artificiale si è affrettato a sostenere che non è creare nuove vite il fine primario della ricerca, ma quello di salvare quelle esistenti, di dare contributi essenziali alla medicina citando il caso del fallimento tuttora non interpretato di alcune gravidanze e di superare la sperimentazione animale, così contribuendo positivamente alla soluzione di un altro dilemma etico.</p>\n<p>L’embrione sintetico ha ovviamente come primo traguardo il contributo ai trapianti oggi drammaticamente carenti nell’offerta rispetto alla domanda, con attese fino a 4 anni per i trapianti di cuore ed a 2 anni per quelli di fegato. Il lavoro dovrebbe adesso continuare presso l’Ateneo di Padova per creare nuovi organi e nuovi farmaci.</p>"
    var unread = true
    var starred = true
    val thumbnail = null
    val icon = "ba79e238383ce83c23a169929c8906ef.png"
    val link =
        "https://ilblogdellasci.wordpress.com/2022/09/09/etica-della-ricerca-sotto-i-riflettori/"
    var sourcetitle = "La Chimica e la Società"
    var tags = "Chimica, Testing"
}