/* 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/. */

package mozilla.components.browser.state.reducer

import mozilla.components.browser.state.action.SearchAction
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.SearchState

internal object SearchReducer {
    /**
     * [SearchAction] Reducer function for modifying [SearchState].
     */
    fun reduce(state: BrowserState, action: SearchAction): BrowserState {
        return when (action) {
            is SearchAction.SetSearchEnginesAction -> state.setSearchEngines(action)
            is SearchAction.SetRegionAction -> state.setRegion(action)
            is SearchAction.UpdateCustomSearchEngineAction -> state.updateCustomSearchEngine(action)
            is SearchAction.RemoveCustomSearchEngineAction -> state.removeSearchEngine(action)
            is SearchAction.SelectSearchEngineAction -> state.selectSearchEngine(action)
            is SearchAction.ShowSearchEngineAction -> state.showSearchEngine(action)
            is SearchAction.HideSearchEngineAction -> state.hideSearchEngine(action)
            is SearchAction.AddAdditionalSearchEngineAction -> state.addAdditionalSearchEngine(action)
            is SearchAction.RemoveAdditionalSearchEngineAction -> state.removeAdditionalSearchEngine(action)
        }
    }
}

private fun BrowserState.setSearchEngines(
    action: SearchAction.SetSearchEnginesAction
): BrowserState {
    return copy(search = search.copy(
        regionSearchEngines = action.regionSearchEngines,
        customSearchEngines = action.customSearchEngines,
        userSelectedSearchEngineId = action.userSelectedSearchEngineId,
        userSelectedSearchEngineName = action.userSelectedSearchEngineName,
        regionDefaultSearchEngineId = action.regionDefaultSearchEngineId,
        hiddenSearchEngines = action.hiddenSearchEngines,
        additionalSearchEngines = action.additionalSearchEngines,
        additionalAvailableSearchEngines = action.additionalAvailableSearchEngines,
        complete = true
    ))
}

private fun BrowserState.setRegion(
    action: SearchAction.SetRegionAction
): BrowserState {
    return copy(search = search.copy(
        region = action.regionState
    ))
}

private fun BrowserState.updateCustomSearchEngine(
    action: SearchAction.UpdateCustomSearchEngineAction
): BrowserState {
    val searchEngines = search.customSearchEngines
    val index = searchEngines.indexOfFirst { searchEngine -> searchEngine.id == action.searchEngine.id }

    val updatedSearchEngines = if (index != -1) {
        searchEngines.subList(0, index) + action.searchEngine + searchEngines.subList(index + 1, searchEngines.size)
    } else {
        searchEngines + action.searchEngine
    }

    return copy(search = search.copy(
        customSearchEngines = updatedSearchEngines
    ))
}

private fun BrowserState.removeSearchEngine(
    action: SearchAction.RemoveCustomSearchEngineAction
): BrowserState {
    return copy(search = search.copy(
        customSearchEngines = search.customSearchEngines.filter { it.id != action.searchEngineId }
    ))
}

private fun BrowserState.selectSearchEngine(
    action: SearchAction.SelectSearchEngineAction
): BrowserState {
    // We allow setting an ID of a search engine that is not in the state since loading the search
    // engines may happen asynchronously and the search engine may not be loaded yet at this point.
    return copy(search = search.copy(
        userSelectedSearchEngineId = action.searchEngineId,
        userSelectedSearchEngineName = action.searchEngineName
    ))
}

private fun BrowserState.showSearchEngine(
    action: SearchAction.ShowSearchEngineAction
): BrowserState {
    val searchEngine = search.hiddenSearchEngines.find { searchEngine -> searchEngine.id == action.searchEngineId }

    return if (searchEngine != null) {
        copy(search = search.copy(
            hiddenSearchEngines = search.hiddenSearchEngines - searchEngine,
            regionSearchEngines = search.regionSearchEngines + searchEngine
        ))
    } else {
        this
    }
}

private fun BrowserState.hideSearchEngine(
    action: SearchAction.HideSearchEngineAction
): BrowserState {
    val searchEngine = search.regionSearchEngines.find { searchEngine -> searchEngine.id == action.searchEngineId }

    return if (searchEngine != null) {
        copy(search = search.copy(
            regionSearchEngines = search.regionSearchEngines - searchEngine,
            hiddenSearchEngines = search.hiddenSearchEngines + searchEngine
        ))
    } else {
        this
    }
}

private fun BrowserState.addAdditionalSearchEngine(
    action: SearchAction.AddAdditionalSearchEngineAction
): BrowserState {
    val searchEngine = search.additionalAvailableSearchEngines.find { searchEngine ->
        searchEngine.id == action.searchEngineId
    }

    return if (searchEngine != null) {
        copy(search = search.copy(
            additionalSearchEngines = search.additionalSearchEngines + searchEngine,
            additionalAvailableSearchEngines = search.additionalAvailableSearchEngines - searchEngine
        ))
    } else {
        this
    }
}

private fun BrowserState.removeAdditionalSearchEngine(
    action: SearchAction.RemoveAdditionalSearchEngineAction
): BrowserState {
    val searchEngine = search.additionalSearchEngines.find { searchEngine ->
        searchEngine.id == action.searchEngineId
    }

    return if (searchEngine != null) {
        copy(search = search.copy(
            additionalAvailableSearchEngines = search.additionalAvailableSearchEngines + searchEngine,
            additionalSearchEngines = search.additionalSearchEngines - searchEngine
        ))
    } else {
        this
    }
}
