/* 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 org.mozilla.fenix.settings

import androidx.lifecycle.LifecycleOwner
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import mozilla.components.concept.sync.AccountObserver
import mozilla.components.concept.sync.AuthType
import mozilla.components.concept.sync.OAuthAccount
import mozilla.components.service.fxa.SyncEngine
import mozilla.components.service.fxa.manager.FxaAccountManager
import mozilla.components.service.fxa.manager.SyncEnginesStorage

/**
 * A view to help manage the sync preference in the "Logins and passwords" and "Credit cards"
 * settings. The provided [syncPreference] is used to navigate to the different fragments
 * that manages the sync account authentication. A toggle will be also added
 * depending on the sync account status.
 *
 * @param syncPreference The sync [SyncPreference] to update and handle navigation.
 * @param lifecycleOwner View lifecycle owner used to determine when to cancel UI jobs.
 * @param accountManager An instance of [FxaAccountManager].
 * @param syncEngine The sync engine that will be used for the sync status lookup.
 * @param loggedOffTitle Text label for the setting when user is not logged in.
 * @param loggedInTitle Text label for the setting when user is logged in.
 * @param onSignInToSyncClicked A callback executed when the [syncPreference] is clicked with a
 * preference status of "Sign in to Sync".
 * @param onReconnectClicked A callback executed when the [syncPreference] is clicked with a
 * preference status of "Reconnect".
 */
@Suppress("LongParameterList")
class SyncPreferenceView(
    private val syncPreference: SyncPreference,
    lifecycleOwner: LifecycleOwner,
    accountManager: FxaAccountManager,
    private val syncEngine: SyncEngine,
    private val loggedOffTitle: String,
    private val loggedInTitle: String,
    private val onSignInToSyncClicked: () -> Unit = {},
    private val onReconnectClicked: () -> Unit = {}
) {

    init {
        accountManager.register(
            object : AccountObserver {
                override fun onAuthenticated(account: OAuthAccount, authType: AuthType) {
                    MainScope().launch { updateSyncPreferenceStatus() }
                }

                override fun onLoggedOut() {
                    MainScope().launch { updateSyncPreferenceNeedsLogin() }
                }

                override fun onAuthenticationProblems() {
                    MainScope().launch { updateSyncPreferenceNeedsReauth() }
                }
            },
            owner = lifecycleOwner
        )

        val accountExists = accountManager.authenticatedAccount() != null
        val needsReauth = accountManager.accountNeedsReauth()

        when {
            needsReauth -> updateSyncPreferenceNeedsReauth()
            accountExists -> updateSyncPreferenceStatus()
            !accountExists -> updateSyncPreferenceNeedsLogin()
        }
    }

    /**
     * Shows a switch toggle for the sync preference when the user is logged in.
     */
    private fun updateSyncPreferenceStatus() {
        syncPreference.apply {
            isSwitchWidgetVisible = true

            val syncEnginesStatus = SyncEnginesStorage(context).getStatus()
            val syncStatus = syncEnginesStatus.getOrElse(syncEngine) { false }

            title = loggedInTitle
            isChecked = syncStatus

            setOnPreferenceChangeListener { _, newValue ->
                SyncEnginesStorage(context).setStatus(syncEngine, newValue as Boolean)
                setSwitchCheckedState(newValue)
                true
            }
        }
    }

    /**
     * Display that the user can sync across devices when the user is logged off.
     */
    private fun updateSyncPreferenceNeedsLogin() {
        syncPreference.apply {
            isSwitchWidgetVisible = false

            title = loggedOffTitle

            setOnPreferenceChangeListener { _, _ ->
                onSignInToSyncClicked()
                false
            }
        }
    }

    /**
     * Displays the logged off title to prompt the user to to re-authenticate their sync account.
     */
    private fun updateSyncPreferenceNeedsReauth() {
        syncPreference.apply {
            isSwitchWidgetVisible = false

            title = loggedOffTitle

            setOnPreferenceChangeListener { _, _ ->
                onReconnectClicked()
                false
            }
        }
    }
}
