/*
 * Copyright 2019 Jeremy Jamet / Kunzisoft.
 *
 * This file is part of KeePassDX.
 *
 *  KeePassDX is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  KeePassDX is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with KeePassDX.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package com.kunzisoft.keepass.settings

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.core.graphics.toColorInt
import androidx.core.view.MenuProvider
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.TwoStatePreference
import com.kunzisoft.androidclearchroma.ChromaUtil
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.SetMainCredentialDialogFragment
import com.kunzisoft.keepass.activities.legacy.DatabaseRetrieval
import com.kunzisoft.keepass.activities.legacy.resetAppTimeoutWhenViewTouchedOrFocused
import com.kunzisoft.keepass.credentialprovider.UserVerificationActionType
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.checkUserVerification
import com.kunzisoft.keepass.database.ContextualDatabase
import com.kunzisoft.keepass.database.crypto.EncryptionAlgorithm
import com.kunzisoft.keepass.database.crypto.kdf.KdfEngine
import com.kunzisoft.keepass.database.element.Group
import com.kunzisoft.keepass.database.element.database.CompressionAlgorithm
import com.kunzisoft.keepass.database.helper.getLocalizedName
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
import com.kunzisoft.keepass.settings.preference.DialogColorPreference
import com.kunzisoft.keepass.settings.preference.DialogListExplanationPreference
import com.kunzisoft.keepass.settings.preference.InputKdfNumberPreference
import com.kunzisoft.keepass.settings.preference.InputKdfSizePreference
import com.kunzisoft.keepass.settings.preference.InputNumberPreference
import com.kunzisoft.keepass.settings.preference.InputTextPreference
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseColorPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseDataCompressionPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseDefaultUsernamePreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseDescriptionPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseKeyDerivationPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseMaxHistoryItemsPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseMaxHistorySizePreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseMemoryUsagePreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseNamePreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseParallelismPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseRecycleBinGroupPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseRemoveUnlinkedDataPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseRoundsPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseTemplatesGroupPreferenceDialogFragmentCompat
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.utils.getParcelableCompat
import com.kunzisoft.keepass.utils.getSerializableCompat
import com.kunzisoft.keepass.viewmodels.DatabaseViewModel
import com.kunzisoft.keepass.viewmodels.SettingsViewModel
import com.kunzisoft.keepass.viewmodels.UserVerificationViewModel
import kotlinx.coroutines.launch

class NestedDatabaseSettingsFragment : NestedSettingsFragment(), DatabaseRetrieval {

    private val mSettingsViewModel: SettingsViewModel by activityViewModels()
    private val mDatabaseViewModel: DatabaseViewModel by activityViewModels()
    private val mUserVerificationViewModel: UserVerificationViewModel by activityViewModels()

    private val mDatabase: ContextualDatabase?
        get() = mDatabaseViewModel.database
    private var mDatabaseReadOnly: Boolean = false
    private var mDatabaseUserVerificationAllowed: Boolean = false
    private var mMergeDataAllowed: Boolean = false
    private var mDatabaseAutoSaveEnabled: Boolean = true

    private var mScreen: Screen? = null

    private var dbNamePref: InputTextPreference? = null
    private var dbDescriptionPref: InputTextPreference? = null
    private var dbDefaultUsernamePref: InputTextPreference? = null
    private var dbCustomColorPref: DialogColorPreference? = null
    private var dbDataCompressionPref: Preference? = null
    private var recycleBinGroupPref: DialogListExplanationPreference? = null
    private var templatesGroupPref: DialogListExplanationPreference? = null
    private var dbMaxHistoryItemsPref: InputNumberPreference? = null
    private var dbMaxHistorySizePref: InputNumberPreference? = null
    private var mEncryptionAlgorithmPref: DialogListExplanationPreference? = null
    private var mKeyDerivationPref: DialogListExplanationPreference? = null
    private var mRoundPref: InputKdfNumberPreference? = null
    private var mMemoryPref: InputKdfSizePreference? = null
    private var mParallelismPref: InputKdfNumberPreference? = null

    private val menuProvider: MenuProvider = object: MenuProvider {
        override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
            menuInflater.inflate(R.menu.database, menu)
            if (mDatabaseReadOnly) {
                menu.findItem(R.id.menu_save_database)?.isVisible = false
                menu.findItem(R.id.menu_merge_database)?.isVisible = false
            }
            if (!mMergeDataAllowed) {
                menu.findItem(R.id.menu_merge_database)?.isVisible = false
            }
        }

        override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
            return when (menuItem.itemId) {
                R.id.menu_save_database -> {
                    saveDatabase(!mDatabaseReadOnly)
                    true
                }
                R.id.menu_merge_database -> {
                    mergeDatabase(!mDatabaseReadOnly)
                    true
                }
                R.id.menu_reload_database -> {
                    reloadDatabase()
                    true
                }
                R.id.menu_app_settings -> {
                    // Check the time lock before launching settings
                    // TODO activity menu
                    (activity as SettingsActivity?)?.let {
                        SettingsActivity.launch(it, true)
                    }
                    true
                }
                else -> {
                    false
                }
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                mDatabaseViewModel.actionState.collect { uiState ->
                    when (uiState) {
                        is DatabaseViewModel.ActionState.OnDatabaseActionFinished -> {
                            onDatabaseActionFinished(
                                uiState.database,
                                uiState.actionTask,
                                uiState.result
                            )
                        }
                        else -> {}
                    }
                }
            }
        }
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.RESUMED) {
                mDatabaseViewModel.databaseState.collect { database ->
                    database?.let {
                        onDatabaseRetrieved(database)
                    }
                }
            }
        }
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.RESUMED) {
                mUserVerificationViewModel.userVerificationState.collect { state ->
                    when (state) {
                        is UserVerificationViewModel.UVState.Loading -> {}
                        is UserVerificationViewModel.UVState.OnUserVerificationCanceled -> {
                            mSettingsViewModel.showError(state.error)
                            mUserVerificationViewModel.onUserVerificationReceived()
                        }
                        is UserVerificationViewModel.UVState.OnUserVerificationSucceeded -> {
                            val data = state.dataToVerify
                            when (data.actionType) {
                                UserVerificationActionType.EDIT_DATABASE_SETTING -> {
                                    val database = data.database
                                    val preferenceKey = data.preferenceKey
                                    if (database != null && preferenceKey != null) {
                                        // Main Preferences
                                        when (preferenceKey) {
                                            // Master Key
                                            getString(R.string.settings_database_change_credentials_key) -> {
                                                SetMainCredentialDialogFragment
                                                    .getInstance(database.allowNoMasterKey)
                                                    .show(parentFragmentManager, "passwordDialog")
                                            }
                                            else -> {}
                                        }
                                        // TODO Settings in compose
                                        @Suppress("DEPRECATION")
                                        mSettingsViewModel.dialogFragment?.let { dialogFragment ->
                                            dialogFragment.setTargetFragment(
                                                this@NestedDatabaseSettingsFragment, 0
                                            )
                                            dialogFragment.show(parentFragmentManager, TAG_PREF_FRAGMENT)
                                        }
                                        mSettingsViewModel.dialogFragment = null
                                    }
                                }
                                else -> {}
                            }
                            mUserVerificationViewModel.onUserVerificationReceived()
                        }
                    }
                }
            }
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        activity?.addMenuProvider(menuProvider, viewLifecycleOwner)
        viewLifecycleOwner.lifecycleScope.launch {
            mDatabaseViewModel.databaseState.collect { database ->
                view.resetAppTimeoutWhenViewTouchedOrFocused(
                    context = requireContext(),
                    databaseLoaded = database?.loaded
                )
            }
        }
    }

    override fun onCreateScreenPreference(screen: Screen, savedInstanceState: Bundle?, rootKey: String?) {
        mScreen = screen
        val database = mDatabase
        // Load the preferences from an XML resource
        when (screen) {
            Screen.DATABASE -> {
                setPreferencesFromResource(R.xml.preferences_database, rootKey)
                if (database?.loaded == true)
                    onCreateDatabasePreference(database)
            }
            Screen.DATABASE_SECURITY -> {
                setPreferencesFromResource(R.xml.preferences_database_security, rootKey)
                if (database?.loaded == true)
                    onCreateDatabaseSecurityPreference(database)
            }
            Screen.DATABASE_MASTER_KEY -> {
                setPreferencesFromResource(R.xml.preferences_database_master_key, rootKey)
                if (database?.loaded == true)
                    onCreateDatabaseMasterKeyPreference(database)
            }
            else -> {
            }
        }
    }

    private fun saveDatabase(save: Boolean) {
        mDatabaseViewModel.saveDatabase(save)
    }

    private fun mergeDatabase(save: Boolean) {
        mDatabaseViewModel.mergeDatabase(save)
    }

    private fun reloadDatabase() {
        mDatabaseViewModel.reloadDatabase(fixDuplicateUuid = false)
    }

    override fun onDatabaseRetrieved(database: ContextualDatabase) {
        mDatabaseReadOnly = database.isReadOnly
        mDatabaseUserVerificationAllowed = database.allowUserVerification
        mMergeDataAllowed = database.isMergeDataAllowed()

        if (database.loaded) {
            when (mScreen) {
                Screen.DATABASE -> {
                    onCreateDatabasePreference(database)
                }
                Screen.DATABASE_SECURITY -> {
                    onCreateDatabaseSecurityPreference(database)
                }
                Screen.DATABASE_MASTER_KEY -> {
                    onCreateDatabaseMasterKeyPreference(database)
                }
                else -> {
                }
            }
        } else {
            Log.e(javaClass.name, "Database isn't ready")
        }
    }

    private fun onCreateDatabasePreference(database: ContextualDatabase) {
        val dbGeneralPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_general_key))

        // Database name
        dbNamePref = findPreference(getString(R.string.database_name_key))
        dbNamePref?.let { namePreference ->
            if (database.allowName) {
                namePreference.summary = database.name
            } else {
                dbGeneralPrefCategory?.removePreference(namePreference)
            }
        }

        // Database description
        dbDescriptionPref = findPreference(getString(R.string.database_description_key))
        dbDescriptionPref?.let { descriptionPreference ->
            if (database.allowDescription) {
                dbDescriptionPref?.summary = database.description
            } else {
                dbGeneralPrefCategory?.removePreference(descriptionPreference)
            }
        }

        // Database default username
        dbDefaultUsernamePref = findPreference(getString(R.string.database_default_username_key))
        dbDefaultUsernamePref?.summary = database.defaultUsername

        // Database custom color
        dbCustomColorPref = findPreference(getString(R.string.database_custom_color_key))
        dbCustomColorPref?.apply {
            val customColor = database.customColor
            if (customColor != null) {
                color = customColor
                summary = ChromaUtil.getFormattedColorString(customColor, false)
            } else{
                color = DialogColorPreference.DISABLE_COLOR
                summary = ""
            }
        }

        // Version
        findPreference<Preference>(getString(R.string.database_version_key))
            ?.summary = database.version

        val dbCompressionPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_data_key))

        // Database compression
        dbDataCompressionPref = findPreference(getString(R.string.database_data_compression_key))
        if (database.allowDataCompression) {
            dbDataCompressionPref?.summary = (database.compressionAlgorithm
                ?: CompressionAlgorithm.NONE).getLocalizedName(resources)
        } else {
            dbCompressionPrefCategory?.isVisible = false
        }

        val dbRecycleBinPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_recycle_bin_key))
        recycleBinGroupPref = findPreference(getString(R.string.recycle_bin_group_key))

        // Recycle bin
        if (database.allowConfigurableRecycleBin) {
            val recycleBinEnablePref: TwoStatePreference? = findPreference(getString(R.string.recycle_bin_enable_key))
            recycleBinEnablePref?.apply {
                isChecked = database.isRecycleBinEnabled
                isEnabled = if (!mDatabaseReadOnly) {
                    setOnPreferenceChangeListener { _, newValue ->
                        val recycleBinEnabled = newValue as Boolean
                        database.enableRecycleBin(recycleBinEnabled, resources.getString(R.string.recycle_bin))
                        refreshRecycleBinGroup(database)
                        // Save the database if not in readonly mode
                        saveDatabase(mDatabaseAutoSaveEnabled)
                        true
                    }
                    true
                } else {
                    false
                }
            }
            // Change the recycle bin group
            recycleBinGroupPref?.setOnPreferenceClickListener {
                true
            }
            // Recycle Bin group
            refreshRecycleBinGroup(database)
        } else {
            recycleBinGroupPref?.onPreferenceClickListener = null
            dbRecycleBinPrefCategory?.isVisible = false
        }

        // Templates
        val templatesGroupPrefCategory: PreferenceCategory? = findPreference(getString(R.string.database_category_templates_key))
        templatesGroupPref = findPreference(getString(R.string.templates_group_uuid_key))
        if (database.allowTemplatesGroup) {
            val templatesEnablePref: TwoStatePreference? = findPreference(getString(R.string.templates_group_enable_key))
            templatesEnablePref?.apply {
                isChecked = database.isTemplatesEnabled
                isEnabled = if (!mDatabaseReadOnly) {
                    setOnPreferenceChangeListener { _, newValue ->
                        val templatesEnabled = newValue as Boolean
                        database.enableTemplates(templatesEnabled,
                            resources.getString(R.string.templates)
                        )
                        refreshTemplatesGroup(database)
                        // Save the database if not in readonly mode
                        saveDatabase(mDatabaseAutoSaveEnabled)
                        true
                    }
                    true
                } else {
                    false
                }
            }
            // Refresh templates group
            refreshTemplatesGroup(database)
        } else {
            templatesGroupPrefCategory?.isVisible = false
        }

        // History
        findPreference<PreferenceCategory>(getString(R.string.database_category_history_key))
            ?.isVisible = database.manageHistory == true

        // Max history items
        dbMaxHistoryItemsPref = findPreference<InputNumberPreference>(getString(R.string.max_history_items_key))?.apply {
            summary = database.historyMaxItems.toString()
        }

        // Max history size
        dbMaxHistorySizePref = findPreference<InputNumberPreference>(getString(R.string.max_history_size_key))?.apply {
            summary = database.historyMaxSize.toString()
        }
    }

    private fun refreshRecycleBinGroup(database: ContextualDatabase?) {
        recycleBinGroupPref?.apply {
            if (database?.isRecycleBinEnabled == true) {
                summary = database.recycleBin?.toString()
                isEnabled = true
            } else {
                summary = null
                isEnabled = false
            }
        }
    }

    private fun refreshTemplatesGroup(database: ContextualDatabase?) {
        templatesGroupPref?.apply {
            if (database?.isTemplatesEnabled == true) {
                summary = database.templatesGroup?.toString()
                isEnabled = true
            } else {
                summary = null
                isEnabled = false
            }
        }
    }

    private fun onCreateDatabaseSecurityPreference(database: ContextualDatabase) {
        // Encryption Algorithm
        mEncryptionAlgorithmPref = findPreference<DialogListExplanationPreference>(getString(R.string.encryption_algorithm_key))?.apply {
            summary = database.getEncryptionAlgorithmName()
        }

        // Key derivation function
        mKeyDerivationPref = findPreference<DialogListExplanationPreference>(getString(R.string.key_derivation_function_key))?.apply {
            summary = database.getKeyDerivationName()
        }

        // Round encryption
        mRoundPref = findPreference<InputKdfNumberPreference>(getString(R.string.transform_rounds_key))?.apply {
            summary = database.numberKeyEncryptionRounds.toString()
        }

        // Memory Usage
        mMemoryPref = findPreference<InputKdfSizePreference>(getString(R.string.memory_usage_key))?.apply {
            summary = database.memoryUsage.toString()
        }

        // Parallelism
        mParallelismPref = findPreference<InputKdfNumberPreference>(getString(R.string.parallelism_key))?.apply {
            summary = database.parallelism.toString()
        }
    }

    private fun onCreateDatabaseMasterKeyPreference(database: ContextualDatabase) {
        val changeCredentialKey = getString(R.string.settings_database_change_credentials_key)
        findPreference<Preference>(changeCredentialKey)?.apply {
            isEnabled = if (!mDatabaseReadOnly) {
                onPreferenceClickListener = Preference.OnPreferenceClickListener {
                    if (mDatabaseUserVerificationAllowed) {
                        checkUserVerification(
                            mUserVerificationViewModel,
                            UserVerificationData(
                                actionType = UserVerificationActionType.EDIT_DATABASE_SETTING,
                                database = database,
                                preferenceKey = changeCredentialKey
                            )
                        )
                    } else {
                        // Show credential dialog directly without user verification
                        SetMainCredentialDialogFragment
                            .getInstance(database.allowNoMasterKey)
                            .show(parentFragmentManager, "passwordDialog")
                    }
                    false
                }
                true
            } else {
                false
            }
        }
    }

    private val colorSelectedListener: ((Int?)-> Unit) = { color ->
        if (color != null) {
            dbCustomColorPref?.color = color
            dbCustomColorPref?.summary = ChromaUtil.getFormattedColorString(color, false)
        } else {
            dbCustomColorPref?.color = DialogColorPreference.DISABLE_COLOR
            dbCustomColorPref?.summary = ""
        }
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        val view = super.onCreateView(inflater, container, savedInstanceState)

        try {
            // To reassign color listener after orientation change
            val chromaDialog = parentFragmentManager.findFragmentByTag(TAG_PREF_FRAGMENT) as DatabaseColorPreferenceDialogFragmentCompat?
            chromaDialog?.onColorSelectedListener = colorSelectedListener
        } catch (_: Exception) {}

        return view
    }

    // TODO check error
    override fun onDatabaseActionFinished(database: ContextualDatabase,
                                          actionTask: String,
                                          result: ActionRunnable.Result) {
        result.data?.let { data ->
            if (data.containsKey(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
                    && data.containsKey(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)) {
                when (actionTask) {
                    /*
                    --------
                    Main preferences
                    --------
                    */
                    DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_NAME_TASK -> {
                        val oldName = data.getString(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)!!
                        val newName = data.getString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)!!
                        val nameToShow =
                                if (result.isSuccess) {
                                    newName
                                } else {
                                    database.name = oldName
                                    oldName
                                }
                        dbNamePref?.summary = nameToShow
                    }
                    DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_DESCRIPTION_TASK -> {
                        val oldDescription = data.getString(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)!!
                        val newDescription = data.getString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)!!
                        val descriptionToShow =
                                if (result.isSuccess) {
                                    newDescription
                                } else {
                                    database.description = oldDescription
                                    oldDescription
                                }
                        dbDescriptionPref?.summary = descriptionToShow
                    }
                    DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_DEFAULT_USERNAME_TASK -> {
                        val oldDefaultUsername = data.getString(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)!!
                        val newDefaultUsername = data.getString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)!!
                        val defaultUsernameToShow =
                                if (result.isSuccess) {
                                    newDefaultUsername
                                } else {
                                    database.defaultUsername = oldDefaultUsername
                                    oldDefaultUsername
                                }
                        dbDefaultUsernamePref?.summary = defaultUsernameToShow
                    }
                    DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_COLOR_TASK -> {
                        val oldColor = data.getString(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)!!
                        val newColor = data.getString(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)!!

                        val defaultColorToShow =
                                if (result.isSuccess) {
                                    newColor
                                } else {
                                    database.customColor = oldColor.toColorInt()
                                    oldColor
                                }
                        dbCustomColorPref?.summary = defaultColorToShow
                    }
                    DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_COMPRESSION_TASK -> {
                        val oldCompression = data.getSerializableCompat<CompressionAlgorithm>(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
                        val newCompression = data.getSerializableCompat<CompressionAlgorithm>(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)
                        val algorithmToShow =
                                if (result.isSuccess) {
                                    newCompression
                                } else {
                                    database.compressionAlgorithm = oldCompression
                                    oldCompression
                                }
                        dbDataCompressionPref?.summary = algorithmToShow?.getLocalizedName(resources)
                    }
                    DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_RECYCLE_BIN_TASK -> {
                        val oldRecycleBin = data.getParcelableCompat<Group>(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
                        val newRecycleBin = data.getParcelableCompat<Group>(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)
                        val recycleBinToShow =
                                if (result.isSuccess) {
                                    newRecycleBin
                                } else {
                                    oldRecycleBin
                                }
                        database.setRecycleBin(recycleBinToShow)
                        refreshRecycleBinGroup(database)
                    }
                    DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_TEMPLATES_GROUP_TASK -> {
                        val oldTemplatesGroup = data.getParcelableCompat<Group>(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
                        val newTemplatesGroup = data.getParcelableCompat<Group>(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)
                        val templatesGroupToShow =
                            if (result.isSuccess) {
                                newTemplatesGroup
                            } else {
                                oldTemplatesGroup
                            }
                        database.setTemplatesGroup(templatesGroupToShow)
                        refreshTemplatesGroup(database)
                    }
                    DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_MAX_HISTORY_ITEMS_TASK -> {
                        val oldMaxHistoryItems = data.getInt(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
                        val newMaxHistoryItems = data.getInt(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)
                        val maxHistoryItemsToShow =
                                if (result.isSuccess) {
                                    newMaxHistoryItems
                                } else {
                                    database.historyMaxItems = oldMaxHistoryItems
                                    oldMaxHistoryItems
                                }
                        dbMaxHistoryItemsPref?.summary = maxHistoryItemsToShow.toString()
                    }
                    DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_MAX_HISTORY_SIZE_TASK -> {
                        val oldMaxHistorySize = data.getLong(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
                        val newMaxHistorySize = data.getLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)
                        val maxHistorySizeToShow =
                                if (result.isSuccess) {
                                    newMaxHistorySize
                                } else {
                                    database.historyMaxSize = oldMaxHistorySize
                                    oldMaxHistorySize
                                }
                        dbMaxHistorySizePref?.summary = maxHistorySizeToShow.toString()
                    }

                    /*
                    --------
                    Security
                    --------
                     */
                    DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_ENCRYPTION_TASK -> {
                        val oldEncryption = data.getSerializableCompat<EncryptionAlgorithm>(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
                        val newEncryption = data.getSerializableCompat<EncryptionAlgorithm>(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)
                        val algorithmToShow =
                                if (result.isSuccess) {
                                    newEncryption
                                } else {
                                    database.encryptionAlgorithm = oldEncryption
                                    oldEncryption
                                }
                        mEncryptionAlgorithmPref?.summary = algorithmToShow.toString()
                    }
                    DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_KEY_DERIVATION_TASK -> {
                        val oldKeyDerivationEngine = data.getSerializableCompat<KdfEngine>(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
                        val newKeyDerivationEngine = data.getSerializableCompat<KdfEngine>(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)
                        val kdfEngineToShow =
                                if (result.isSuccess) {
                                    newKeyDerivationEngine
                                } else {
                                    database.kdfEngine = oldKeyDerivationEngine
                                    oldKeyDerivationEngine
                                }
                        mKeyDerivationPref?.summary = kdfEngineToShow.toString()

                        mRoundPref?.summary = kdfEngineToShow?.defaultKeyRounds.toString()
                        // Disable memory and parallelism if not available
                        mMemoryPref?.summary = kdfEngineToShow?.defaultMemoryUsage.toString()
                        mParallelismPref?.summary = kdfEngineToShow?.defaultParallelism.toString()
                    }
                    DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_ITERATIONS_TASK -> {
                        val oldIterations = data.getLong(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
                        val newIterations = data.getLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)
                        val roundsToShow =
                                if (result.isSuccess) {
                                    newIterations
                                } else {
                                    database.numberKeyEncryptionRounds = oldIterations
                                    oldIterations
                                }
                        mRoundPref?.summary = roundsToShow.toString()
                    }
                    DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_MEMORY_USAGE_TASK -> {
                        val oldMemoryUsage = data.getLong(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
                        val newMemoryUsage = data.getLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)
                        val memoryToShow =
                                if (result.isSuccess) {
                                    newMemoryUsage
                                } else {
                                    database.memoryUsage = oldMemoryUsage
                                    oldMemoryUsage
                                }
                        mMemoryPref?.summary = memoryToShow.toString()
                    }
                    DatabaseTaskNotificationService.ACTION_DATABASE_UPDATE_PARALLELISM_TASK -> {
                        val oldParallelism = data.getLong(DatabaseTaskNotificationService.OLD_ELEMENT_KEY)
                        val newParallelism = data.getLong(DatabaseTaskNotificationService.NEW_ELEMENT_KEY)
                        val parallelismToShow =
                                if (result.isSuccess) {
                                    newParallelism
                                } else {
                                    database.parallelism = oldParallelism
                                    oldParallelism
                                }
                        mParallelismPref?.summary = parallelismToShow.toString()
                    }
                }
            }
        }
    }

    override fun onDisplayPreferenceDialog(preference: Preference) {

        var otherDialogFragment = false

        var dialogFragment: DialogFragment? = null
        // Main Preferences
        when (preference.key) {
            getString(R.string.database_name_key) -> {
                dialogFragment = DatabaseNamePreferenceDialogFragmentCompat.newInstance(preference.key)
            }
            getString(R.string.database_description_key) -> {
                dialogFragment = DatabaseDescriptionPreferenceDialogFragmentCompat.newInstance(preference.key)
            }
            getString(R.string.database_default_username_key) -> {
                dialogFragment = DatabaseDefaultUsernamePreferenceDialogFragmentCompat.newInstance(preference.key)
            }
            getString(R.string.database_custom_color_key) -> {
                dialogFragment = DatabaseColorPreferenceDialogFragmentCompat.newInstance(preference.key).apply {
                    onColorSelectedListener = colorSelectedListener
                }
            }
            getString(R.string.database_data_compression_key) -> {
                dialogFragment = DatabaseDataCompressionPreferenceDialogFragmentCompat.newInstance(preference.key)
            }
            getString(R.string.database_data_remove_unlinked_attachments_key) -> {
                dialogFragment = DatabaseRemoveUnlinkedDataPreferenceDialogFragmentCompat.newInstance(preference.key)
            }
            getString(R.string.recycle_bin_group_key) -> {
                dialogFragment = DatabaseRecycleBinGroupPreferenceDialogFragmentCompat.newInstance(preference.key)
            }
            getString(R.string.templates_group_uuid_key) -> {
                dialogFragment = DatabaseTemplatesGroupPreferenceDialogFragmentCompat.newInstance(preference.key)
            }
            getString(R.string.max_history_items_key) -> {
                dialogFragment = DatabaseMaxHistoryItemsPreferenceDialogFragmentCompat.newInstance(preference.key)
            }
            getString(R.string.max_history_size_key) -> {
                dialogFragment = DatabaseMaxHistorySizePreferenceDialogFragmentCompat.newInstance(preference.key)
            }

            // Security
            getString(R.string.encryption_algorithm_key) -> {
                dialogFragment = DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat.newInstance(preference.key)
            }
            getString(R.string.key_derivation_function_key) -> {
                val keyDerivationDialogFragment = DatabaseKeyDerivationPreferenceDialogFragmentCompat.newInstance(preference.key)
                // Add other prefs to manage
                keyDerivationDialogFragment.setRoundPreference(mRoundPref)
                keyDerivationDialogFragment.setMemoryPreference(mMemoryPref)
                keyDerivationDialogFragment.setParallelismPreference(mParallelismPref)
                dialogFragment = keyDerivationDialogFragment
            }
            getString(R.string.transform_rounds_key) -> {
                dialogFragment = DatabaseRoundsPreferenceDialogFragmentCompat.newInstance(preference.key)
            }
            getString(R.string.memory_usage_key) -> {
                dialogFragment = DatabaseMemoryUsagePreferenceDialogFragmentCompat.newInstance(preference.key)
            }
            getString(R.string.parallelism_key) -> {
                dialogFragment = DatabaseParallelismPreferenceDialogFragmentCompat.newInstance(preference.key)
            }
            else -> otherDialogFragment = true
        }

        if (dialogFragment != null && !mDatabaseReadOnly) {
            mSettingsViewModel.dialogFragment = dialogFragment
            if (mDatabaseUserVerificationAllowed) {
                checkUserVerification(
                    mUserVerificationViewModel,
                    UserVerificationData(
                        actionType = UserVerificationActionType.EDIT_DATABASE_SETTING,
                        database = mDatabase,
                        preferenceKey = preference.key
                    )
                )
            } else {
                // Show dialog directly without user verification
                @Suppress("DEPRECATION")
                dialogFragment.setTargetFragment(this@NestedDatabaseSettingsFragment, 0)
                dialogFragment.show(parentFragmentManager, TAG_PREF_FRAGMENT)
            }
        }
        // Could not be handled here. Try with the super method.
        else if (otherDialogFragment) {
            super.onDisplayPreferenceDialog(preference)
        }
    }

    override fun onResume() {
        super.onResume()
        context?.let { context ->
            mDatabaseAutoSaveEnabled = PreferencesUtil.isAutoSaveDatabaseEnabled(context)
        }
    }

    override fun onPreferenceTreeClick(preference: Preference): Boolean {
        // To reload group when database settings are modified
        when (preference.key) {
            getString(R.string.database_name_key),
            getString(R.string.database_description_key),
            getString(R.string.database_default_username_key),
            getString(R.string.database_custom_color_key),
            getString(R.string.database_data_compression_key),
            getString(R.string.database_data_remove_unlinked_attachments_key),
            getString(R.string.recycle_bin_enable_key),
            getString(R.string.recycle_bin_group_key),
            getString(R.string.templates_group_enable_key),
            getString(R.string.templates_group_uuid_key),
            getString(R.string.max_history_items_key),
            getString(R.string.max_history_size_key) -> {
                NestedAppSettingsFragment.DATABASE_PREFERENCE_CHANGED = true
            }
        }
        return super.onPreferenceTreeClick(preference)
    }

    companion object {
        private const val TAG_PREF_FRAGMENT = "TAG_PREF_FRAGMENT"
    }
}
