
package app.crossword.yourealwaysbe.forkyz.settings

import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain

import android.content.SharedPreferences
import android.os.SystemClock
import androidx.datastore.core.DataStore

import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.anyString
import org.mockito.Mockito.eq
import org.mockito.Mockito.mock

class TestDataStore(initialDataStoreSettings : Settings) : DataStore<Settings> {
    private var currentDataStoreSettings = initialDataStoreSettings

    override val data = flow {
        while (true)
            emit(currentDataStoreSettings)
    }

    override suspend fun updateData(
        transform : suspend (Settings) -> Settings,
    ) : Settings {
        currentDataStoreSettings = runBlocking {
            transform(currentDataStoreSettings)
        }
        return currentDataStoreSettings
    }
}

class ForkyzSettingsTest {
    val settingsValue1: Settings = Settings.newBuilder()
        .setAlwaysAnnounceBox(false)
        .setAlwaysAnnounceClue(false)
        .setApplicationTheme(Theme.T_STANDARD)
        .setArchiveCleanupAge(-1)
        .clearAutoDownloaders()
        .setBackgroundDownloadAllowRoaming(false)
        .clearBackgroundDownloadDays()
        .setBackgroundDownloadDaysTime(8)
        .setBackgroundDownloadHourly(false)
        .setBackgroundDownloadRequireCharging(false)
        .setBackgroundDownloadRequireUnmetered(true)
        .setBrowseAlwaysShowRating(false)
        .setBrowseIndicateIfSolution(false)
        .setBrowseNewPuzzle(false)
        .setBrowseShowPercentageCorrect(false)
        .setButtonActivatesVoice(false)
        .setButtonAnnounceClue(false)
        .setChatGPTAPIKey("")
        .setCleanupAge(-1)
        .setClueBelowGrid(false)
        .setClueHighlight(ClueHighlight.CH_RADIO_BUTTON)
        .setClueListNameInClueLine(ClueListClueLine.CLCL_FULL)
        .setClueTabsDouble(ClueTabsDouble.CTD_NEVER)
        .setCrosswordSolverEnabled(true)
        .setCustomDailyTitle("")
        .setCustomDailyUrl("")
        .setCycleUnfilledMode(CycleUnfilledMode.CU_NEVER)
        .setDeleteCrossingMode(DeleteCrossingModeSetting.DCMS_DELETE)
        .setDeleteOnCleanup(false)
        .setDisableRatings(false)
        .setDisableSwipe(false)
        .setDisplayScratch(false)
        .setDisplaySeparators(DisplaySeparators.DS_ALWAYS)
        .setDlLast(0)
        .setDlOnStartup(false)
        .setDontConfirmCleanup(true)
        .setDontConfirmBrowseDelete(false)
        .setDontSnapBoardToBorders(false)
        .setDoubleTap(false)
        .setDownload20Minutes(true)
        .setDownloadCustomDaily(false)
        .setDownloadDeStandaard(true)
        .setDownloadDeTelegraaf(true)
        .setDownloadGuardianDailyCryptic(true)
        .setDownloadGuardianWeeklyQuiptic(true)
        .setDownloadHamAbend(true)
        .setDownloadIndependentDailyCryptic(true)
        .setDownloadIrishNewsCryptic(true)
        .setDownloadJonesin(true)
        .setDownloadJoseph(true)
        .setDownloadLeParisienF1(true)
        .setDownloadLeParisienF2(true)
        .setDownloadLeParisienF3(true)
        .setDownloadLeParisienF4(true)
        .setDownloadMetroCryptic(true)
        .setDownloadMetroQuick(true)
        .setDownloadNewYorkTimesSyndicated(true)
        .setDownloadNewsday(true)
        .setDownloadPremier(true)
        .setDownloadSheffer(true)
        .setDownloadTimeout(30000)
        .setDownloadUSAToday(true)
        .setDownloadUniversal(true)
        .setDownloadWaPoSunday(true)
        .setDownloadWsj(true)
        .setDuckDuckGoEnabled(true)
        .setEnsureVisible(true)
        .setEnterChangesDirection(true)
        .setEqualsAnnounceClue(true)
        .setExpandableClueLine(false)
        .setExternalDictionary(ExternalDictionarySetting.EDS_FREE)
        .setFifteenSquaredEnabled(true)
        .setFitToScreenMode(FitToScreenMode.FTSM_NEVER)
        .setFullScreen(false)
        .setGridRatio(GridRatio.GR_PUZZLE_SHAPE)
        .setGridRatioLand(GridRatio.GR_PUZZLE_SHAPE)
        .setIndicateShowErrors(true)
        .setInferSeparators(false)
        .setKeyboardCompact(false)
        .setKeyboardForceCaps(true)
        .setKeyboardHaptic(true)
        .setKeyboardHideButton(false)
        .setKeyboardLayout(KeyboardLayout.KL_QWERTY)
        .setKeyboardRepeatDelay(300)
        .setKeyboardRepeatInterval(75)
        .setKeyboardShowHide(KeyboardMode.KM_HIDE_MANUAL)
        .setLastSeenVersion("")
        .setMovementStrategy(MovementStrategySetting.MSS_MOVE_NEXT_ON_AXIS)
        .setMovementStrategyClueList(MovementStrategySetting.MSS_MOVE_NEXT_CLUE)
        .setOrientationLock(Orientation.O_UNLOCKED)
        .setPlayActivityClueTabsPage(0)
        .setPlayActivityClueTabsPage1(1)
        .setPlayLetterUndoEnabled(false)
        .setPredictAnagramChars(true)
        .setPreserveCorrectLettersInShowErrors(false)
        .setRandomClueOnShake(false)
        .setSafArchiveFolderUri("")
        .setSafCrosswordsFolderUri("")
        .setSafRootUri("")
        .setSafToImportDoneFolderUri("")
        .setSafToImportFailedFolderUri("")
        .setSafToImportFolderUri("")
        .setScale(1.0F)
        .setScrapeCru(true)
        .setScrapeGuardianQuick(true)
        .setScrapeKegler(true)
        .setScrapePrivateEye(true)
        .setScrapePrzekroj(true)
        .setScratchMode(false)
        .setShowCluesOnPlayScreen(true)
        .setShowCount(false)
        .setShowErrors(false)
        .setShowErrorsClue(false)
        .setShowErrorsCursor(false)
        .setShowTimer(false)
        .setShowWordsInClueList(false)
        .setSkipFilled(false)
        .setSnapClue(false)
        .setSort(AccessorSetting.AS_DATE_DESC)
        .setSpaceChangesDirection(true)
        .setStorageLocation(StorageLocation.SL_INTERNAL)
        .setSuppressHints(false)
        .setSuppressMessages(false)
        .setSuppressSummaryMessages(false)
        .setSwipeAction(BrowseSwipeAction.BSA_DELETE)
        .setToggleBeforeMove(false)
        .setUiTheme(DayNightMode.DNM_DAY)
        .setUseNativeKeyboard(false)
        .setVolumeActivatesVoice(false)
        .build()

    val settingsValue2: Settings = Settings.newBuilder()
        .setAlwaysAnnounceBox(true)
        .setAlwaysAnnounceClue(true)
        .setApplicationTheme(Theme.T_DYNAMIC)
        .setArchiveCleanupAge(10)
        .clearAutoDownloaders()
        .addAutoDownloaders("autos")
        .setBackgroundDownloadAllowRoaming(true)
        .clearBackgroundDownloadDays()
        .addBackgroundDownloadDays("1")
        .setBackgroundDownloadDaysTime(9)
        .setBackgroundDownloadHourly(true)
        .setBackgroundDownloadRequireCharging(true)
        .setBackgroundDownloadRequireUnmetered(false)
        .setBrowseAlwaysShowRating(true)
        .setBrowseIndicateIfSolution(true)
        .setBrowseNewPuzzle(true)
        .setBrowseShowPercentageCorrect(true)
        .setButtonActivatesVoice(true)
        .setButtonAnnounceClue(true)
        .setChatGPTAPIKey("xx")
        .setCleanupAge(10)
        .setClueBelowGrid(true)
        .setClueHighlight(ClueHighlight.CH_BOTH)
        .setClueListNameInClueLine(ClueListClueLine.CLCL_NONE)
        .setClueTabsDouble(ClueTabsDouble.CTD_WIDE)
        .setCrosswordSolverEnabled(false)
        .setCustomDailyTitle("xx")
        .setCustomDailyUrl("xx")
        .setCycleUnfilledMode(CycleUnfilledMode.CU_ALWAYS)
        .setDeleteCrossingMode(
            DeleteCrossingModeSetting.DCMS_PRESERVE_FILLED_CELLS
        )
        .setDeleteOnCleanup(true)
        .setDisableRatings(true)
        .setDisableSwipe(true)
        .setDisplayScratch(true)
        .setDisplaySeparators(DisplaySeparators.DS_NEVER)
        .setDlLast(0)
        .setDlOnStartup(true)
        .setDontConfirmCleanup(false)
        .setDontConfirmBrowseDelete(true)
        .setDontSnapBoardToBorders(true)
        .setDoubleTap(true)
        .setDownload20Minutes(false)
        .setDownloadCustomDaily(true)
        .setDownloadDeStandaard(false)
        .setDownloadDeTelegraaf(false)
        .setDownloadGuardianDailyCryptic(false)
        .setDownloadGuardianWeeklyQuiptic(false)
        .setDownloadHamAbend(false)
        .setDownloadIndependentDailyCryptic(false)
        .setDownloadIrishNewsCryptic(false)
        .setDownloadJonesin(false)
        .setDownloadJoseph(false)
        .setDownloadLeParisienF1(false)
        .setDownloadLeParisienF2(false)
        .setDownloadLeParisienF3(false)
        .setDownloadLeParisienF4(false)
        .setDownloadMetroCryptic(false)
        .setDownloadMetroQuick(false)
        .setDownloadNewYorkTimesSyndicated(false)
        .setDownloadNewsday(false)
        .setDownloadPremier(false)
        .setDownloadSheffer(false)
        .setDownloadTimeout(40000)
        .setDownloadUSAToday(false)
        .setDownloadUniversal(false)
        .setDownloadWaPoSunday(false)
        .setDownloadWsj(false)
        .setDuckDuckGoEnabled(false)
        .setEnsureVisible(false)
        .setEnterChangesDirection(false)
        .setEqualsAnnounceClue(false)
        .setExpandableClueLine(true)
        .setExternalDictionary(ExternalDictionarySetting.EDS_NONE)
        .setFifteenSquaredEnabled(false)
        .setFitToScreenMode(FitToScreenMode.FTSM_START)
        .setFullScreen(true)
        .setGridRatio(GridRatio.GR_ONE_TO_ONE)
        .setGridRatioLand(GridRatio.GR_FIFTY_PCNT)
        .setIndicateShowErrors(false)
        .setInferSeparators(true)
        .setKeyboardCompact(true)
        .setKeyboardForceCaps(false)
        .setKeyboardHaptic(false)
        .setKeyboardHideButton(true)
        .setKeyboardLayout(KeyboardLayout.KL_DVORAK)
        .setKeyboardRepeatDelay(301)
        .setKeyboardRepeatInterval(85)
        .setKeyboardShowHide(KeyboardMode.KM_NEVER_SHOW)
        .setLastSeenVersion("xx")
        .setMovementStrategy(MovementStrategySetting.MSS_STOP_ON_END)
        .setMovementStrategyClueList(MovementStrategySetting.MSS_STOP_ON_END)
        .setOrientationLock(Orientation.O_PORTRAIT)
        .setPlayActivityClueTabsPage(1)
        .setPlayActivityClueTabsPage1(0)
        .setPlayLetterUndoEnabled(true)
        .setPredictAnagramChars(false)
        .setPreserveCorrectLettersInShowErrors(true)
        .setRandomClueOnShake(true)
        .setSafArchiveFolderUri("xx")
        .setSafCrosswordsFolderUri("xx")
        .setSafRootUri("xx")
        .setSafToImportDoneFolderUri("xx")
        .setSafToImportFailedFolderUri("xx")
        .setSafToImportFolderUri("xx")
        .setScale(1.2F)
        .setScrapeCru(false)
        .setScrapeGuardianQuick(false)
        .setScrapeKegler(false)
        .setScrapePrivateEye(false)
        .setScrapePrzekroj(false)
        .setScratchMode(true)
        .setShowCluesOnPlayScreen(false)
        .setShowCount(true)
        .setShowErrors(true)
        .setShowErrorsClue(true)
        .setShowErrorsCursor(true)
        .setShowTimer(true)
        .setShowWordsInClueList(true)
        .setSkipFilled(true)
        .setSnapClue(true)
        .setSort(AccessorSetting.AS_DATE_ASC)
        .setSpaceChangesDirection(false)
        .setStorageLocation(StorageLocation.SL_EXTERNAL_SAF)
        .setSuppressHints(true)
        .setSuppressMessages(true)
        .setSuppressSummaryMessages(true)
        .setSwipeAction(BrowseSwipeAction.BSA_ARCHIVE)
        .setToggleBeforeMove(true)
        .setUiTheme(DayNightMode.DNM_NIGHT)
        .setUseNativeKeyboard(true)
        .setVolumeActivatesVoice(true)
        .build()

    val settingsValue3 = Settings.newBuilder()
        .mergeFrom(settingsValue2)
        .setClueListNameInClueLine(ClueListClueLine.CLCL_FULL)
        .setDontConfirmCleanup(true)
        .setDontConfirmBrowseDelete(false)
        .setDontSnapBoardToBorders(false)
        .setExpandableClueLine(false)
        .setMovementStrategyClueList(MovementStrategySetting.MSS_MOVE_NEXT_CLUE)
        .build()

    @ExperimentalCoroutinesApi
    private val testDispatcher = UnconfinedTestDispatcher()

    @ExperimentalCoroutinesApi
    @Before
    fun setup() {
        Dispatchers.setMain(testDispatcher)
    }

    @ExperimentalCoroutinesApi
    @After
    fun tearDown() {
        Dispatchers.resetMain()
    }

    @Mock
    val prefs = mock(SharedPreferences::class.java)
    val prefsEdit = mock(SharedPreferences.Editor::class.java)

    @Before
    fun setupSharedPrefs() {
        `when`(prefs.contains(anyString())).thenReturn(true)
        `when`(prefs.getBoolean(eq("alwaysAnnounceBox"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("alwaysAnnounceClue"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getString(eq("applicationTheme"), eq(null)))
            .thenReturn("D")
        `when`(prefs.getInt(eq("archiveCleanupAge"), anyInt()))
            .thenReturn(10)
        `when`(prefs.getStringSet(eq("autoDownloaders"), eq(null)))
            .thenReturn(setOf("autos"))
        `when`(prefs.getBoolean(eq("backgroundDownloadAllowRoaming"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getStringSet(eq("backgroundDownloadDays"), eq(null)))
            .thenReturn(setOf("1"))
        `when`(prefs.getString(eq("backgroundDownloadDaysTime"), eq(null)))
            .thenReturn("9")
        `when`(prefs.getBoolean(eq("backgroundDownloadHourly"), anyBoolean()))
            .thenReturn(true)
        `when`(
            prefs.getBoolean(eq("backgroundDownloadRequireCharging"), anyBoolean())
        ).thenReturn(true)
        `when`(
            prefs.getBoolean(eq("backgroundDownloadRequireUnmetered"), anyBoolean())
        ).thenReturn(false)
        `when`(prefs.getBoolean(eq("browseAlwaysShowRating"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("browseIndicateIfSolution"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("browseNewPuzzle"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("browseShowPercentageCorrect"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("buttonActivatesVoice"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("buttonAnnounceClue"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getString(eq("chatGPTAPIKey"), eq(null)))
            .thenReturn("xx")
        `when`(prefs.getInt(eq("cleanupAge"), anyInt()))
            .thenReturn(10)
        `when`(prefs.getBoolean(eq("clueBelowGrid"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getString(eq("clueHighlight"), eq(null)))
            .thenReturn("BOTH")
        `when`(prefs.getString(eq("clueTabsDouble"), eq(null)))
            .thenReturn("WIDE")
        `when`(prefs.getBoolean(eq("crosswordSolverEnabled"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getString(eq("customDailyTitle"), eq(null)))
            .thenReturn("xx")
        `when`(prefs.getString(eq("customDailyUrl"), eq(null)))
            .thenReturn("xx")
        `when`(
            prefs.getString(eq("cycleUnfilledMode"), eq(null))
        ).thenReturn("ALWAYS")
        `when`(
            prefs.getString(eq("deleteCrossingMode"), eq(null))
        ).thenReturn("PRESERVE_FILLED_CELLS")
        `when`(prefs.getBoolean(eq("deleteOnCleanup"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("disableRatings"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("disableSwipe"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("displayScratch"), anyBoolean()))
            .thenReturn(true)
        `when`(
            prefs.getString(eq("displaySeparators"), eq(null))
        ).thenReturn("NEVER")
        `when`(prefs.getLong(eq("dlLast"), anyLong()))
            .thenReturn(0)
        `when`(prefs.getBoolean(eq("dlOnStartup"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("doubleTap"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("download20Minutes"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadCustomDaily"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("downloadDeStandaard"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadDeTelegraaf"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadGuardianDailyCryptic"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadGuardianWeeklyQuiptic"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadHamAbend"), anyBoolean()))
            .thenReturn(false)
        `when`(
            prefs.getBoolean(eq("downloadIndependentDailyCryptic"), anyBoolean())
        ).thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadIrishNewsCryptic"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadJonesin"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadJoseph"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadLeParisienF1"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadLeParisienF2"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadLeParisienF3"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadLeParisienF4"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadMetroCryptic"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadMetroQuick"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadNewYorkTimesSyndicated"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadNewsday"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadPremier"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadSheffer"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getString(eq("downloadTimeout"), eq(null)))
            .thenReturn("40000")
        `when`(prefs.getBoolean(eq("downloadUSAToday"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadUniversal"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadWaPoSunday"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("downloadWsj"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("duckDuckGoEnabled"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("ensureVisible"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("enterChangesDirection"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("equalsAnnounceClue"), anyBoolean()))
            .thenReturn(false)
        `when`(
            prefs.getString(eq("externalDictionary"), eq(null))
        ).thenReturn("NONE")
        `when`(prefs.getBoolean(eq("fifteenSquaredEnabled"), anyBoolean()))
            .thenReturn(false)
        `when`(
            prefs.getString(eq("fitToScreenMode"), eq(null))
        ).thenReturn("START")
        `when`(prefs.getBoolean(eq("fullScreen"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getString(eq("gridRatio"), eq(null)))
            .thenReturn("0")
        `when`(prefs.getString(eq("gridRatioLand"), eq(null)))
            .thenReturn("3")
        `when`(prefs.getBoolean(eq("indicateShowErrors"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("inferSeparators"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("keyboardCompact"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("keyboardForceCaps"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("keyboardHaptic"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("keyboardHideButton"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getString(eq("keyboardLayout"), eq(null)))
            .thenReturn("2")
        `when`(prefs.getString(eq("keyboardRepeatDelay"), eq(null)))
            .thenReturn("301")
        `when`(prefs.getString(eq("keyboardRepeatInterval"), eq(null)))
            .thenReturn("85")
        `when`(prefs.getString(eq("keyboardShowHide"), eq(null)))
            .thenReturn("Never show")
        `when`(prefs.getString(eq("lastSeenVersion"), eq(null)))
            .thenReturn("xx")
        `when`(
            prefs.getString(eq("movementStrategy"), eq(null))
        ).thenReturn("STOP_ON_END")
        `when`(
            prefs.getString(eq("movementStrategyClueList"), eq(null))
        ).thenReturn("STOP_ON_END")
        `when`(prefs.getString(eq("orientationLock"), eq(null)))
            .thenReturn("PORT")
        `when`(prefs.getInt(eq("setPlayActivityClueTabsPage"), anyInt()))
            .thenReturn(1)
        `when`(prefs.getInt(eq("setPlayActivityClueTabsPage1"), anyInt()))
            .thenReturn(0)
        `when`(prefs.getBoolean(eq("playLetterUndoEnabled"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("predictAnagramChars"), anyBoolean()))
            .thenReturn(false)
        `when`(
            prefs.getBoolean(eq("preserveCorrectLettersInShowErrors"), anyBoolean())
        ).thenReturn(true)
        `when`(prefs.getBoolean(eq("randomClueOnShake"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getString(eq("safArchiveFolderUri"), eq(null)))
            .thenReturn("xx")
        `when`(prefs.getString(eq("safCrosswordsFolderUri"), eq(null)))
            .thenReturn("xx")
        `when`(prefs.getString(eq("safRootUri"), eq(null)))
            .thenReturn("xx")
        `when`(prefs.getString(eq("safToImportDoneFolderUri"), eq(null)))
            .thenReturn("xx")
        `when`(prefs.getString(eq("safToImportFailedFolderUri"), eq(null)))
            .thenReturn("xx")
        `when`(prefs.getString(eq("safToImportFolderUri"), eq(null)))
            .thenReturn("xx")
        `when`(prefs.getFloat(eq("scale"), anyFloat()))
            .thenReturn(1.2F)
        `when`(prefs.getBoolean(eq("scrapeCru"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("scrapeGuardianQuick"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("scrapeKegler"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("scrapePrivateEye"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("scrapePrzekroj"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("scratchMode"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("showCluesOnPlayScreen"), anyBoolean()))
            .thenReturn(false)
        `when`(prefs.getBoolean(eq("showCount"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("showErrors"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("showErrorsClue"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("showErrorsCursor"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("showTimer"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("showWordsInClueList"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("skipFilled"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("snapClue"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getInt(eq("sort"), anyInt())).thenReturn(1)
        `when`(prefs.getBoolean(eq("spaceChangesDirection"), anyBoolean()))
            .thenReturn(false)
        `when`(
            prefs.getString(eq("storageLocation"), eq(null))
        ).thenReturn("External directory")
        `when`(prefs.getBoolean(eq("supressHints"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("supressMessages"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("supressSummaryMessages"), anyBoolean()))
            .thenReturn(true)
        `when`(
            prefs.getString(eq("swipeAction"), eq(null))
        ).thenReturn("ARCHIVE")
        `when`(prefs.getBoolean(eq("toggleBeforeMove"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getString(eq("uiTheme"), eq(null)))
            .thenReturn("NIGHT")
        `when`(prefs.getBoolean(eq("useDynamicColors"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("useNativeKeyboard"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.getBoolean(eq("volumeActivatesVoice"), anyBoolean()))
            .thenReturn(true)
        `when`(prefs.edit()).thenReturn(prefsEdit)
    }

    @Test
    fun testExportImport() {
        // export settingsValue2 to a buffer
        // import it to a second data store
        // check end up with same values

        val datastore1 = TestDataStore(settingsValue1)
        val settings1 = ForkyzSettings(datastore1)
        val settings2 = ForkyzSettings(TestDataStore(settingsValue2))

        val baos = ByteArrayOutputStream()
        settings2.exportSettings(baos)
        baos.close();

        val bais = ByteArrayInputStream(baos.toByteArray())
        settings1.importSettings(bais)
        bais.close()

        val importedSettings = runBlocking { datastore1.data.first() }
        assertEquals(importedSettings, settingsValue2)
    }

    @Test
    fun testMigrateSharedPreferences() {
        val datastore = TestDataStore(settingsValue1)
        val settings = ForkyzSettings(datastore)

        migratePreferences(prefs, settings)

        val migratedSettings = runBlocking { datastore.data.first() }
        assertEquals(migratedSettings, settingsValue3)
    }

    // test import
    // careful: background days, volume voice, play activity clue tabs page
}

