/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

@file:Suppress("TooManyFunctions")

package org.mozilla.focus.state

import mozilla.components.feature.top.sites.TopSite
import mozilla.components.lib.state.Reducer

/**
 * Reducer creating a new [AppState] for dispatched [AppAction]s.
 */
@Suppress("ComplexMethod")
object AppReducer : Reducer<AppState, AppAction> {
    override fun invoke(state: AppState, action: AppAction): AppState {
        return when (action) {
            is AppAction.SelectionChanged -> selectionChanged(state, action)
            is AppAction.NoTabs -> noTabs(state)
            is AppAction.EditAction -> editAction(state, action)
            is AppAction.FinishEdit -> finishEditing(state, action)
            is AppAction.HideTabs -> hideTabs(state)
            is AppAction.ShowFirstRun -> showFirstRun(state)
            is AppAction.FinishFirstRun -> finishFirstRun(state, action)
            is AppAction.Lock -> lock(state, action)
            is AppAction.Unlock -> unlock(state, action)
            is AppAction.OpenSettings -> openSettings(state, action)
            is AppAction.NavigateUp -> navigateUp(state, action)
            is AppAction.OpenTab -> openTab(state, action)
            is AppAction.TopSitesChange -> topSitesChanged(state, action)
            is AppAction.SitePermissionOptionChange -> sitePermissionOptionChanged(state, action)
            is AppAction.SecretSettingsStateChange -> secretSettingsStateChanged(
                state,
                action,
            )
            is AppAction.ShowEraseTabsCfrChange -> showEraseTabsCfrChanged(state, action)
            is AppAction.ShowStartBrowsingCfrChange -> showStartBrowsingCfrChanged(state, action)
            is AppAction.ShowTrackingProtectionCfrChange -> showTrackingProtectionCfrChanged(
                state,
                action,
            )
            is AppAction.OpenSitePermissionOptionsScreen -> openSitePermissionOptionsScreen(
                state,
                action,
            )
            is AppAction.ShowHomeScreen -> showHomeScreen(state)
            is AppAction.ShowOnboardingSecondScreen -> showOnBoardingSecondScreen(state)
            is AppAction.ShowSearchWidgetSnackBar -> showSearchWidgetSnackBarChanged(state, action)
            is AppAction.ShowCookieBannerCfrChange -> showCookieBannerCfrChanged(state, action)
        }
    }
}

/**
 * The currently selected tab has changed.
 */
private fun selectionChanged(state: AppState, action: AppAction.SelectionChanged): AppState {
    if (state.screen is Screen.FirstRun || state.screen is Screen.Locked) {
        return state
    }

    return state.copy(
        screen = Screen.Browser(tabId = action.tabId, showTabs = false),
    )
}

/**
 * All tabs have been closed.
 */
private fun noTabs(state: AppState): AppState {
    if (state.screen is Screen.Home || state.screen is Screen.FirstRun || state.screen is Screen.Locked) {
        return state
    }
    return state.copy(screen = Screen.Home)
}

/**
 * The user wants to edit the URL of a tab.
 */
private fun editAction(state: AppState, action: AppAction.EditAction): AppState {
    return state.copy(
        screen = Screen.EditUrl(action.tabId),
    )
}

/**
 * The user finished editing the URL.
 */
private fun finishEditing(state: AppState, action: AppAction.FinishEdit): AppState {
    return state.copy(
        screen = Screen.Browser(tabId = action.tabId, showTabs = false),
    )
}

/**
 * Hide the tabs tray.
 */
private fun hideTabs(state: AppState): AppState {
    return if (state.screen is Screen.Browser) {
        state.copy(screen = state.screen.copy(showTabs = false))
    } else {
        state
    }
}

/**
 * The user finished the first run onboarding.
 */
private fun finishFirstRun(state: AppState, action: AppAction.FinishFirstRun): AppState {
    return if (action.tabId != null) {
        state.copy(screen = Screen.Browser(action.tabId, showTabs = false))
    } else {
        state.copy(screen = Screen.Home)
    }
}

/**
 * Force showing the first run screen (for testing).
 */
private fun showFirstRun(state: AppState): AppState {
    return state.copy(screen = Screen.FirstRun)
}

private fun showOnBoardingSecondScreen(state: AppState): AppState {
    return state.copy(screen = Screen.OnboardingSecondScreen)
}

/**
 * Force showing the home screen.
 */
private fun showHomeScreen(state: AppState): AppState {
    return state.copy(screen = Screen.Home)
}

/**
 * Lock the application.
 */
private fun lock(state: AppState, action: AppAction.Lock): AppState {
    return state.copy(screen = Screen.Locked(action.bundle))
}

/**
 * Unlock the application.
 */
private fun unlock(state: AppState, action: AppAction.Unlock): AppState {
    if (state.screen !is Screen.Locked) {
        return state
    }

    return if (action.tabId != null) {
        state.copy(screen = Screen.Browser(action.tabId, showTabs = false))
    } else {
        state.copy(screen = Screen.Home)
    }
}

private fun openSettings(state: AppState, action: AppAction.OpenSettings): AppState {
    return state.copy(
        screen = Screen.Settings(page = action.page),
    )
}

private fun openTab(state: AppState, action: AppAction.OpenTab): AppState {
    return state.copy(
        screen = Screen.Browser(tabId = action.tabId, showTabs = false),
    )
}

/**
 * The list of [TopSite] has changed.
 */
private fun topSitesChanged(state: AppState, action: AppAction.TopSitesChange): AppState {
    return state.copy(topSites = action.topSites)
}

/**
 * The rules of site permissions autoplay has changed.
 */
private fun sitePermissionOptionChanged(
    state: AppState,
    action: AppAction.SitePermissionOptionChange,
): AppState {
    return state.copy(sitePermissionOptionChange = action.value)
}

/**
 * The state of secret settings has changed.
 */
private fun secretSettingsStateChanged(
    state: AppState,
    action: AppAction.SecretSettingsStateChange,
): AppState {
    return state.copy(secretSettingsEnabled = action.enabled)
}

/**
 * The state of erase tabs CFR changed
 */
private fun showEraseTabsCfrChanged(
    state: AppState,
    action: AppAction.ShowEraseTabsCfrChange,
): AppState {
    return state.copy(showEraseTabsCfr = action.value)
}

/**
 * Update whether the start browsing CFR should be shown or not
 */
private fun showStartBrowsingCfrChanged(
    state: AppState,
    action: AppAction.ShowStartBrowsingCfrChange,
): AppState {
    return state.copy(showStartBrowsingTabsCfr = action.value)
}

/**
 * The state of search widget snackBar changed
 */
private fun showSearchWidgetSnackBarChanged(
    state: AppState,
    action: AppAction.ShowSearchWidgetSnackBar,
): AppState {
    return state.copy(showSearchWidgetSnackbar = action.value)
}

/**
 * The state of tracking protection CFR changed
 */
private fun showTrackingProtectionCfrChanged(
    state: AppState,
    action: AppAction.ShowTrackingProtectionCfrChange,
): AppState {
    return state.copy(showTrackingProtectionCfrForTab = action.value)
}

/**
 * The state of cookie banner CFR changed
 */
private fun showCookieBannerCfrChanged(
    state: AppState,
    action: AppAction.ShowCookieBannerCfrChange,
): AppState {
    return state.copy(showCookieBannerCfr = action.value)
}

private fun openSitePermissionOptionsScreen(
    state: AppState,
    action: AppAction.OpenSitePermissionOptionsScreen,
): AppState {
    return state.copy(screen = Screen.SitePermissionOptionsScreen(sitePermission = action.sitePermission))
}

@Suppress("ComplexMethod", "ReturnCount")
private fun navigateUp(state: AppState, action: AppAction.NavigateUp): AppState {
    if (state.screen is Screen.Browser) {
        val screen = if (action.tabId != null) {
            Screen.Browser(tabId = action.tabId, showTabs = false)
        } else {
            Screen.Home
        }
        return state.copy(screen = screen)
    }

    if (state.screen is Screen.SitePermissionOptionsScreen) {
        return state.copy(screen = Screen.Settings(page = Screen.Settings.Page.SitePermissions))
    }

    if (state.screen !is Screen.Settings) {
        return state
    }

    val screen = when (state.screen.page) {
        Screen.Settings.Page.Start -> if (action.tabId != null) {
            Screen.Browser(tabId = action.tabId, showTabs = false)
        } else {
            Screen.Home
        }

        Screen.Settings.Page.General -> Screen.Settings(page = Screen.Settings.Page.Start)
        Screen.Settings.Page.Privacy -> Screen.Settings(page = Screen.Settings.Page.Start)
        Screen.Settings.Page.Search -> Screen.Settings(page = Screen.Settings.Page.Start)
        Screen.Settings.Page.Advanced -> Screen.Settings(page = Screen.Settings.Page.Start)
        Screen.Settings.Page.Mozilla -> Screen.Settings(page = Screen.Settings.Page.Start)

        Screen.Settings.Page.PrivacyExceptions -> Screen.Settings(page = Screen.Settings.Page.Privacy)
        Screen.Settings.Page.PrivacyExceptionsRemove -> Screen.Settings(page = Screen.Settings.Page.PrivacyExceptions)
        Screen.Settings.Page.SitePermissions -> Screen.Settings(page = Screen.Settings.Page.Privacy)
        Screen.Settings.Page.Studies -> Screen.Settings(page = Screen.Settings.Page.Privacy)
        Screen.Settings.Page.SecretSettings -> Screen.Settings(page = Screen.Settings.Page.Advanced)

        Screen.Settings.Page.SearchList -> Screen.Settings(page = Screen.Settings.Page.Search)
        Screen.Settings.Page.SearchRemove -> Screen.Settings(page = Screen.Settings.Page.SearchList)
        Screen.Settings.Page.SearchAdd -> Screen.Settings(page = Screen.Settings.Page.SearchList)
        Screen.Settings.Page.SearchAutocomplete -> Screen.Settings(page = Screen.Settings.Page.Search)
        Screen.Settings.Page.SearchAutocompleteList -> Screen.Settings(page = Screen.Settings.Page.SearchAutocomplete)

        Screen.Settings.Page.SearchAutocompleteAdd -> Screen.Settings(
            page = Screen.Settings.Page.SearchAutocompleteList,
        )
        Screen.Settings.Page.SearchAutocompleteRemove -> Screen.Settings(
            page = Screen.Settings.Page.SearchAutocompleteList,
        )
        Screen.Settings.Page.About -> Screen.Settings(page = Screen.Settings.Page.Mozilla)
        Screen.Settings.Page.Licenses -> Screen.Settings(page = Screen.Settings.Page.Mozilla)
        Screen.Settings.Page.Locale -> Screen.Settings(page = Screen.Settings.Page.General)
        Screen.Settings.Page.CookieBanner -> Screen.Settings(page = Screen.Settings.Page.Privacy)
    }

    return state.copy(screen = screen)
}
