package bou.amine.apps.readerforselfossv2.repository

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.model.StatusAndData
import bou.amine.apps.readerforselfossv2.model.SuccessResponse
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 StatusAndData(
            success = true,
            data = SelfossModel.ApiVersion("2.19-ba1e8e3", "4.0.0")
        )
        coEvery { api.stats() } returns 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
                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 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
                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
                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
                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
                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
                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
                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
                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: Boolean

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

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

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

        var success: Boolean

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

        assertSame(false, success)
        assertSame(0, repository.badgeAll.value)
        assertSame(0, repository.badgeUnread.value)
        assertSame(0, repository.badgeStarred.value)
        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: Boolean

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

        assertTrue(success)
        assertEquals(1, repository.badgeAll.value)
        assertEquals(1, repository.badgeUnread.value)
        assertEquals(1, repository.badgeStarred.value)
        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: Boolean

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

        assertFalse(success)
        assertSame(0, repository.badgeAll.value)
        assertSame(0, repository.badgeUnread.value)
        assertSame(0, repository.badgeStarred.value)
        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 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>?
        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 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>
        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 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>
        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 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>
        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 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>
        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 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>
        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 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>
        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 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>
        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 StatusAndData(success = true, data = sources)
        every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
        initializeRepository()
        var testSources: List<SelfossModel.Source>?
        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 StatusAndData(success = true, data = sources)
        every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
        initializeRepository()
        var testSources: List<SelfossModel.Source>?
        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 StatusAndData(success = true, data = sources)
        every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
        initializeRepository()
        var testSources: List<SelfossModel.Source>?
        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 StatusAndData(success = true, data = sources)
        every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
        initializeRepository()
        var testSources: List<SelfossModel.Source>?
        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 StatusAndData(success = true, data = sources)
        every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
        initializeRepository(MutableStateFlow(false))
        var testSources: List<SelfossModel.Source>?
        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 StatusAndData(success = true, data = sources)
        every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
        initializeRepository(MutableStateFlow(false))
        var testSources: List<SelfossModel.Source>?
        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 StatusAndData(success = true, data = sources)
        every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
        initializeRepository(MutableStateFlow(false))
        var testSources: List<SelfossModel.Source>?
        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 StatusAndData(success = true, data = sources)
        every { db.sourcesQueries.sources().executeAsList() } returns sourcesDB
        initializeRepository(MutableStateFlow(false))
        var testSources: List<SelfossModel.Source>?
        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()) } returns
                SuccessResponse(true)

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

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

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

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

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

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

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

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

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

        initializeRepository()
        var response: Boolean
        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 SuccessResponse(false)

        initializeRepository()
        var response: Boolean
        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 SuccessResponse(false)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        initializeRepository(MutableStateFlow(false))
        var response: Boolean
        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(
            StatusAndData(success = true, data = generateTestApiItem(itemParameter1)),
            StatusAndData(success = true, data = generateTestApiItem(itemParameter2)),
            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
                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
                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) }
    }
}