#include "SettingsDialog.h"

#include "AccountDetailsManager.h"
#include "BugReportDialog.h"
#include "ChangePasswordComponent.h"
#include "CommonMessages.h"
#include "DialogOpener.h"
#include "FilePickerNodeSelectorSpecializations.h"
#include "FullName.h"
#include "MegaApplication.h"
#include "MessageDialogOpener.h"
#include "ParallelConnectionsValues.h"
#include "PermissionsDialog.h"
#include "Platform.h"
#include "PowerOptions.h"
#include "ProxySettings.h"
#include "qml/AccountStateQuickWidget.h"
#include "RequestListenerManager.h"
#include "ServiceUrls.h"
#include "StatsEventHandler.h"
#include "ThemeManager.h"
#include "ui_SettingsDialog.h"
#include "Utilities.h"

#include <QApplication>
#include <QButtonGroup>
#include <QDesktopServices>
#include <QFileDialog>
#include <QMenu>
#include <QMessageBox>
#include <QMouseEvent>
#include <QRect>
#include <QShortcut>
#include <QSignalBlocker>
#include <QtConcurrent/QtConcurrent>
#include <QTranslator>
#include <QUrl>

#include <memory>

using namespace mega;
using namespace std::chrono_literals;

static const QString SYNCS_TAB_MENU_LABEL_QSS =
    QString::fromLatin1("QLabel{ border-image: url(%1); }");
static constexpr int NUMBER_OF_CLICKS_TO_DEBUG{5};

long long calculateCacheSize()
{
    long long cacheSize = 0;
    auto model(SyncInfo::instance());
    for (auto syncType: SyncInfo::AllHandledSyncTypes)
    {
        for (int i = 0; i < model->getNumSyncedFolders(syncType); i++)
        {
            auto syncSetting = model->getSyncSetting(i, syncType);
            QString syncPath = syncSetting->getLocalFolder();
            if (!syncPath.isEmpty())
            {
                Utilities::getFolderSize(syncPath + QDir::separator() +
                                             QString::fromUtf8(MEGA_DEBRIS_FOLDER),
                                         &cacheSize);
            }
        }
    }
    return cacheSize;
}

long long calculateRemoteCacheSize(MegaApi* mMegaApi)
{
    MegaNode* n = mMegaApi->getNodeByPath("//bin/SyncDebris");
    long long size = mMegaApi->getSize(n);
    delete n;
    return size;
}

SettingsDialog::SettingsDialog(MegaApplication* app, bool proxyOnly, QWidget* parent):
    QDialog(parent),
    mUi(new Ui::SettingsDialog),
    mApp(app),
    mPreferences(Preferences::instance()),
    mModel(SyncInfo::instance()),
    mMegaApi(app->getMegaApi()),
    mLoadingSettings(0),
    mThreadPool(ThreadPoolSingleton::getInstance()),
    mCacheSize(-1),
    mRemoteCacheSize(-1),
    mDebugCounter(0),
    mKeepLogsCounter(0),
    usersUpdateListener(std::make_unique<UsersUpdateListener>()),
    mAccountStateQuickWidget(nullptr)
{
    mUi->setupUi(this);
    mUi->lEmail->installEventFilter(this);

    mAccountStateQuickWidget = mUi->accountStateQuickWidget;
    connect(mAccountStateQuickWidget,
            &AccountStateQuickWidget::upgradeRequested,
            this,
            &SettingsDialog::on_bUpgrade_clicked);

    connect(usersUpdateListener.get(),
            &UsersUpdateListener::userEmailUpdated,
            this,
            &SettingsDialog::onUserEmailChanged);
    mMegaApi->addListener(usersUpdateListener.get());

    // override whatever indexes might be set in .ui files (frequently checked in by mistake)
    mUi->wStack->setCurrentWidget(mUi->pGeneral);
    mUi->wStackFooter->setCurrentWidget(mUi->wGeneralFooter);
    // Add Ctrl+index keyboard shortcut for Settings tabs
    setShortCutsForToolBarItems();

    connect(mUi->wStack,
            &QStackedWidget::currentChanged,
            mUi->wStack,
            [=](const int& newValue)
            {
                if (newValue < mUi->wStackFooter->count())
                {
                    mUi->wStackFooter->setCurrentIndex(newValue);
                    // Setting new index in the stack widget cause the focus to be set to footer
                    // button avoid it, setting to main wStack to ease tab navigation among
                    // different controls.
                    mUi->wStack->setFocus();
                }
            });

    mUi->bGeneral->setChecked(true); // override whatever might be set in .ui
    mUi->lCacheTitle->setText(mUi->lCacheTitle->text().arg(QString::fromUtf8(MEGA_DEBRIS_FOLDER)));

#ifdef Q_OS_LINUX
    mUi->bUpdate->hide();
    mUi->wAutoUpdateContainer->hide();
#endif

    mUi->wDesktopIntegrationContainer->hide();

#ifdef Q_OS_WINDOWS
    mUi->wDesktopIntegrationContainer->show();
#endif

#ifdef Q_OS_MACOS
    this->setWindowTitle(tr("Settings"));
    mUi->cStartOnStartup->setText(tr("Launch at login"));
#endif
    mUi->vPermissionsWidget->setVisible(false);
#ifndef Q_OS_WINDOWS
    mUi->vPermissionsWidget->setVisible(true);
    connect(mUi->bChangePermissions,
            &QPushButton::clicked,
            this,
            &SettingsDialog::onPermissionsClicked);
#endif
    setProxyOnly(proxyOnly);

    AccountDetailsManager::instance()->attachStorageObserver(*this);
    AccountDetailsManager::instance()->attachBandwidthObserver(*this);
    AccountDetailsManager::instance()->attachAccountObserver(*this);

    connect(mApp,
            &MegaApplication::shellNotificationsProcessed,
            this,
            &SettingsDialog::onShellNotificationsProcessed);
    setOverlayCheckboxEnabled(!mApp->isShellNotificationProcessingOngoing(),
                              mUi->cOverlayIcons->isChecked());
    connect(mUi->bBackup, &QPushButton::clicked, this, &SettingsDialog::on_bBackup_clicked);
    connect(mUi->bSyncs, &QPushButton::clicked, this, &SettingsDialog::on_bSyncs_clicked);

    connect(mUi->bLearnMore, &QPushButton::clicked, this, &SettingsDialog::onBLearnMore);
    connect(mUi->bAboutMega, &QPushButton::clicked, this, &SettingsDialog::onBAboutMega);
    connect(mModel,
            &SyncInfo::syncDisabledListUpdated,
            this,
            &SettingsDialog::updateSyncTabToolbarIcon);
    connect(mModel,
            &SyncInfo::syncDisabledListUpdated,
            this,
            &SettingsDialog::updateBackupTabToolbarIcon);

    // React to AppState changes
    connect(AppState::instance().get(),
            &AppState::appStateChanged,
            this,
            &SettingsDialog::onAppStateChanged);

    startRequestTaskbarPinningTimer();
    updateSyncTabToolbarIcon();
    updateBackupTabToolbarIcon();
}

void SettingsDialog::updateSyncTabToolbarIcon()
{
    QString iconName;
    if (mModel->syncWithErrorExist(MegaSync::TYPE_TWOWAY))
    {
        iconName = Utilities::getPixmapName(QLatin1String("settings-sync-warn"),
                                            Utilities::AttributeType::NONE);
    }
    else
    {
        iconName = Utilities::getPixmapName(QLatin1String("settings-sync"),
                                            Utilities::AttributeType::NONE);
    }

    mUi->bSyncs->setIcon(QIcon(iconName));
}

void SettingsDialog::updateBackupTabToolbarIcon()
{
    QString iconName;
    if (mModel->syncWithErrorExist(MegaSync::TYPE_BACKUP))
    {
        iconName = Utilities::getPixmapName(QLatin1String("settings-backup-warn"),
                                            Utilities::AttributeType::NONE);
    }
    else
    {
        iconName = Utilities::getPixmapName(QLatin1String("settings-backup"),
                                            Utilities::AttributeType::NONE);
    }

    mUi->bBackup->setIcon(QIcon(iconName));
}

SettingsDialog::~SettingsDialog()
{
    AccountDetailsManager::instance()->dettachStorageObserver(*this);
    AccountDetailsManager::instance()->dettachBandwidthObserver(*this);
    AccountDetailsManager::instance()->dettachAccountObserver(*this);

    mMegaApi->removeListener(usersUpdateListener.get());

    delete mUi;
}

void SettingsDialog::openSettingsTab(int tab)
{
    if (mProxyOnly) // do not switch tabs when in guest mode
        return;

    switch (tab)
    {
        case GENERAL_TAB:
            mUi->bGeneral->click();
            break;

        case ACCOUNT_TAB:
            mUi->bAccount->click();
            break;

        case SYNCS_TAB:
            mUi->bSyncs->click();
            break;

        case BACKUP_TAB:
            mUi->bBackup->click();
            break;

        case SECURITY_TAB:
            mUi->bSecurity->click();
            break;

        case FOLDERS_TAB:
            mUi->bFolders->click();
            break;

        case NETWORK_TAB:
            mUi->bNetwork->click();
            break;

        case NOTIFICATIONS_TAB:
            mUi->bNotifications->click();
            break;

        default:
            break;
    }
}

void SettingsDialog::setProxyOnly(bool proxyOnly)
{
    mProxyOnly = proxyOnly;

    mUi->bGeneral->setEnabled(!proxyOnly);
    mUi->bAccount->setEnabled(!proxyOnly);
    mUi->bSyncs->setEnabled(!proxyOnly);
    mUi->bBackup->setEnabled(!proxyOnly);
    mUi->bSecurity->setEnabled(!proxyOnly);
    mUi->bFolders->setEnabled(!proxyOnly);
    mUi->bNotifications->setEnabled(!proxyOnly);

    if (proxyOnly)
    {
        mUi->bNetwork->setEnabled(true);
        mUi->bNetwork->setChecked(true);
    }
    else
    {
        loadSettings();
    }
}

void SettingsDialog::onAppStateChanged(AppState::AppStates oldAppState,
                                       AppState::AppStates newAppState)
{
    setProxyOnly(newAppState != AppState::NOMINAL);
}

void SettingsDialog::showGuestMode()
{
    mUi->wStack->setCurrentWidget(mUi->pNetwork);
    QPointer<ProxySettings> proxySettingsDialog = new ProxySettings(mApp, this);
    proxySettingsDialog->setAttribute(Qt::WA_DeleteOnClose);
    DialogOpener::showDialog(proxySettingsDialog,
                             [proxySettingsDialog, this]()
                             {
                                 if (proxySettingsDialog->result() == QDialog::Accepted)
                                 {
                                     mApp->applyProxySettings();
                                     if (mProxyOnly)
                                         accept(); // close Settings in guest mode
                                 }
                                 else
                                 {
                                     if (mProxyOnly)
                                         reject(); // close Settings in guest mode
                                 }
                             });
}

void SettingsDialog::loadSettings()
{
    mLoadingSettings++;

    if (mPreferences->logged())
    {
        connect(&mCacheSizeWatcher,
                &QFutureWatcher<long long>::finished,
                this,
                &SettingsDialog::onLocalCacheSizeAvailable);
        QFuture<long long> futureCacheSize = QtConcurrent::run(calculateCacheSize);
        mCacheSizeWatcher.setFuture(futureCacheSize);

        connect(&mRemoteCacheSizeWatcher,
                &QFutureWatcher<long long>::finished,
                this,
                &SettingsDialog::onRemoteCacheSizeAvailable);
        QFuture<long long> futureRemoteCacheSize =
            QtConcurrent::run(calculateRemoteCacheSize, mMegaApi);
        mRemoteCacheSizeWatcher.setFuture(futureRemoteCacheSize);
    }

    // General
    mUi->cFileVersioning->setChecked(!mPreferences->fileVersioningDisabled());
    mUi->cbSleepMode->setChecked(mPreferences->awakeIfActiveEnabled());
    mUi->cOverlayIcons->setChecked(!mPreferences->overlayIconsDisabled());
    mUi->cCacheSchedulerEnabled->setChecked(mPreferences->cleanerDaysLimit());
    mUi->sCacheSchedulerDays->setEnabled(mPreferences->cleanerDaysLimit());
    mUi->sCacheSchedulerDays->setValue(mPreferences->cleanerDaysLimitValue());
    updateCacheSchedulerDaysLabel();

    if (!mPreferences->canUpdate(MegaApplication::applicationFilePath()))
    {
        mUi->bUpdate->setEnabled(false);
        mUi->cAutoUpdate->setEnabled(false);
        mUi->cAutoUpdate->setChecked(false);
    }
    else
    {
        mUi->bUpdate->setEnabled(true);
        mUi->cAutoUpdate->setEnabled(true);
        mUi->cAutoUpdate->setChecked(mPreferences->updateAutomatically());
    }

    // if checked: make sure both sources are true
    mUi->cStartOnStartup->setChecked(mPreferences->startOnStartup() &&
                                     Platform::getInstance()->isStartOnStartupActive());

    // Language
    mUi->cLanguage->clear();
    mLanguageCodes.clear();
    QString fullPrefix = Preferences::TRANSLATION_FOLDER + Preferences::TRANSLATION_PREFIX;
    QDirIterator it(Preferences::TRANSLATION_FOLDER);
    QStringList languages;
    qsizetype currentIndex = -1;
    QString currentLanguage = mPreferences->language();
    while (it.hasNext())
    {
        QString file = it.next();
        if (file.startsWith(fullPrefix))
        {
            auto extensionIndex = file.lastIndexOf(QString::fromUtf8("."));
            if ((extensionIndex - fullPrefix.size()) <= 0)
            {
                continue;
            }

            QString languageCode = file.mid(fullPrefix.size(), extensionIndex - fullPrefix.size());
            QString languageString = Utilities::languageCodeToString(languageCode);
            if (!languageString.isEmpty())
            {
                int i = 0;
                while (i < languages.size() && (languageString > languages[i]))
                {
                    i++;
                }
                languages.insert(i, languageString);
                mLanguageCodes.insert(i, languageCode);
            }
        }
    }

    for (auto i = mLanguageCodes.size() - 1; i >= 0; i--)
    {
        if (currentLanguage.startsWith(mLanguageCodes[i]))
        {
            currentIndex = i;
            break;
        }
    }

    if (currentIndex == -1)
    {
        currentIndex = mLanguageCodes.indexOf(QString::fromUtf8("en"));
    }

    mUi->cLanguage->addItems(languages);
    mUi->cLanguage->setCurrentIndex(static_cast<int>(currentIndex));

    // Color theme
    initColorTheme();

    // Account name
    mUi->lEmail->setText(mPreferences->email());
    auto fullName(
        (mPreferences->firstName() + QStringLiteral(" ") + mPreferences->lastName()).trimmed());

    // do we already have that value on the attribute request?
    auto fullNameRequest = UserAttributes::FullName::requestFullName();
    if (fullName.isEmpty() || fullNameRequest->isAttributeReady())
    {
        fullName = fullNameRequest->getFullName();
    }

    mUi->lName->setText(mUi->lName->fontMetrics().elidedText(fullName,
                                                             Qt::ElideMiddle,
                                                             mUi->lName->maximumWidth()) +
                        QString::fromUtf8(" • "));

    // Update name in case it changes
    connect(fullNameRequest.get(),
            &UserAttributes::FullName::fullNameReady,
            this,
            [this](const QString& fullName)
            {
                mUi->lName->setText(
                    mUi->lName->fontMetrics().elidedText(fullName,
                                                         Qt::ElideMiddle,
                                                         mUi->lName->maximumWidth()));
            });

    // Avatar
    mUi->wAvatar->setUserEmail();

    // account type and details
    SettingsDialog::updateAccountElements();
    SettingsDialog::updateStorageElements();
    SettingsDialog::updateBandwidthElements();

    updateUploadFolder();
    updateDownloadFolder();

#ifdef Q_OS_WINDOWS
    mUi->cDesktopIntegration->setChecked(!mPreferences->leftPaneIconsDisabled());
#endif

    // Init the network tab before updating network values
    initNetworkTab();
    updateNetworkTab();

    mLoadingSettings--;
}

// General -----------------------------------------------------------------------------------------

void deleteCache()
{
    MegaSyncApp->cleanLocalCaches(true);
}

void SettingsDialog::setUpdateAvailable(bool updateAvailable)
{
    if (updateAvailable)
    {
        mUi->bUpdate->setText(tr("Install Update"));
    }
    else
    {
        mUi->bUpdate->setText(tr("Check for Updates"));
    }
}

void SettingsDialog::storageChanged()
{
    onCacheSizeAvailable();
}

void SettingsDialog::onLocalCacheSizeAvailable()
{
    mCacheSize = mCacheSizeWatcher.result();
    onCacheSizeAvailable();
}

void SettingsDialog::onRemoteCacheSizeAvailable()
{
    mRemoteCacheSize = mRemoteCacheSizeWatcher.result();
    onCacheSizeAvailable();
}

void SettingsDialog::on_bHelp_clicked()
{
    Utilities::openUrl(ServiceUrls::getDesktopAppHelpUrl());
}

bool SettingsDialog::event(QEvent* event)
{
    if (event->type() == QEvent::LanguageChange)
    {
        mUi->retranslateUi(this);

        mUi->lCacheTitle->setText(
            mUi->lCacheTitle->text().arg(QString::fromUtf8(MEGA_DEBRIS_FOLDER)));

#ifdef Q_OS_MACOS
        mUi->cStartOnStartup->setText(tr("Launch at login"));
        this->setWindowTitle(tr("Settings"));
#endif

        onCacheSizeAvailable();
        updateNetworkTab();
        updateStorageElements();
        updateBandwidthElements();
        updateAccountElements();

        updateUploadFolder();
        updateDownloadFolder();

        initColorTheme();
    }

    return QDialog::event(event);
}

bool SettingsDialog::eventFilter(QObject* watched, QEvent* event)
{
    if (watched == mUi->lEmail && event->type() == QEvent::MouseButtonRelease)
    {
        auto mouseEvent = static_cast<QMouseEvent*>(event);
        if (mouseEvent->button() == Qt::LeftButton)
        {
            onEmailClicked();
        }
    }

    return QDialog::eventFilter(watched, event);
}

void SettingsDialog::on_bGeneral_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::SETTINGS_GENERAL_TAB_CLICKED);

    emit userActivity();

    if ((mUi->wStack->currentWidget() == mUi->pGeneral))
    {
        return;
    }

    mUi->wStack->setCurrentWidget(mUi->pGeneral);
}

void SettingsDialog::on_bClearCache_clicked()
{
    QString syncs;
    for (const auto& syncSetting: mModel->getAllSyncSettings())
    {
        QFileInfo fi(syncSetting->getLocalFolder() + QDir::separator() +
                     QString::fromUtf8(MEGA_DEBRIS_FOLDER));
        if (fi.exists() && fi.isDir())
        {
            syncs += QString::fromUtf8("<br/><a href=\"local://#%1\">%2</a>")
                         .arg(fi.absoluteFilePath() + QDir::separator())
                         .arg(syncSetting->name());
        }
    }

    MessageDialogInfo msgInfo;
    msgInfo.parent = this;
    msgInfo.titleText = tr("Clear local backup");
    msgInfo.descriptionText =
        tr("Backups of the previous versions of your synced files in your computer"
           " will be permanently deleted. Please, check your backup folders to see"
           " if you need to rescue something before continuing:") +
        QString::fromUtf8("<br/>") + syncs + QString::fromUtf8("<br/><br/>") +
        tr("Do you want to delete your local backup now?");
    msgInfo.textFormat = Qt::RichText;
    msgInfo.buttons = QMessageBox::Yes | QMessageBox::No;
    msgInfo.defaultButton = QMessageBox::No;
    msgInfo.finishFunc = [this](QPointer<MessageDialogResult> msg)
    {
        if (msg->result() == QMessageBox::Yes)
        {
            QThreadPool::globalInstance()->start(deleteCache);
            mCacheSize = 0;
            onCacheSizeAvailable();
        }
    };

    MessageDialogOpener::warning(msgInfo);
}

void SettingsDialog::on_bClearRemoteCache_clicked()
{
    std::shared_ptr<MegaNode> syncDebris(mMegaApi->getNodeByPath("//bin/SyncDebris"));
    if (!syncDebris)
    {
        mRemoteCacheSize = 0;
        return;
    }

    std::unique_ptr<const char[]> base64Handle(syncDebris->getBase64Handle());

    MessageDialogInfo msgInfo;
    msgInfo.parent = this;
    msgInfo.titleText = tr("Clear remote backup");
    msgInfo.descriptionText =
        tr("Backups of the previous versions of your synced files in MEGA will be"
           " permanently deleted. Please, check your [A] folder in the Rubbish Bin"
           " of your MEGA account to see if you need to rescue something"
           " before continuing.")
            .replace(QLatin1String("[A]"),
                     QString::fromUtf8("<a href=\"%1\">SyncDebris</a>")
                         .arg(ServiceUrls::getNodeUrl(QString::fromUtf8(base64Handle.get()))
                                  .toString())) +
        QString::fromUtf8("<br/><br/>") + tr("Do you want to delete your remote backup now?");
    msgInfo.textFormat = Qt::RichText;
    msgInfo.buttons = QMessageBox::Yes | QMessageBox::No;
    msgInfo.defaultButton = QMessageBox::No;
    msgInfo.finishFunc = [this](QPointer<MessageDialogResult> msg)
    {
        if (msg->result() == QMessageBox::Yes)
        {
            auto deleteRemoteCache = [this]()
            {
                std::unique_ptr<MegaNode> n(mMegaApi->getNodeByPath("//bin/SyncDebris"));
                mMegaApi->remove(n.get());
            };
            QThreadPool::globalInstance()->start(deleteRemoteCache);
            mRemoteCacheSize = 0;
            onCacheSizeAvailable();
        }
    };

    MessageDialogOpener::warning(msgInfo);
}

void SettingsDialog::on_bClearFileVersions_clicked()
{
    MessageDialogInfo msgInfo;
    msgInfo.parent = this;
    msgInfo.descriptionText = tr("You are about to permanently remove all file versions."
                                 " Would you like to proceed?");
    msgInfo.buttons = QMessageBox::Yes | QMessageBox::No;
    msgInfo.textFormat = Qt::RichText;
    msgInfo.defaultButton = QMessageBox::No;
    msgInfo.finishFunc = [this](QPointer<MessageDialogResult> msg)
    {
        if (msg->result() == QMessageBox::Yes)
        {
            mMegaApi->removeVersions(new MegaListenerFuncExecuter(
                true,
                [](MegaApi* api, MegaRequest* request, MegaError* e)
                {
                    Q_UNUSED(api)
                    Q_UNUSED(request)
                    if (e->getErrorCode() == MegaError::API_OK)
                    {
                        AccountDetailsManager::instance()->updateUserStats(
                            AccountDetailsManager::Flag::STORAGE,
                            true,
                            USERSTATS_REMOVEVERSIONS);
                    }
                }));
        }
    };

    MessageDialogOpener::warning(msgInfo);
}

void SettingsDialog::on_cCacheSchedulerEnabled_toggled()
{
    if (mLoadingSettings)
        return;
    bool isEnabled = mUi->cCacheSchedulerEnabled->isChecked();
    mUi->sCacheSchedulerDays->setEnabled(isEnabled);
    mPreferences->setCleanerDaysLimit(isEnabled);
    if (isEnabled)
    {
        mApp->cleanLocalCaches();
    }
}

void SettingsDialog::on_sCacheSchedulerDays_valueChanged(int i)
{
    if (mLoadingSettings)
        return;
    if (mUi->cCacheSchedulerEnabled->isChecked())
    {
        mPreferences->setCleanerDaysLimitValue(i);
        updateCacheSchedulerDaysLabel();
        mApp->cleanLocalCaches();
    }
}

void SettingsDialog::on_cAutoUpdate_toggled(bool checked)
{
    if (mLoadingSettings)
        return;
    if (mUi->cAutoUpdate->isEnabled() && (checked != mPreferences->updateAutomatically()))
    {
        mPreferences->setUpdateAutomatically(checked);
        if (checked)
        {
            on_bUpdate_clicked();
        }
    }
}

void SettingsDialog::on_cStartOnStartup_toggled(bool checked)
{
    if (mLoadingSettings)
        return;
    if (!Platform::getInstance()->startOnStartup(checked))
    {
        // in case of failure - make sure configuration keeps the right value
        // LOG_debug << "Failed to " << (checked ? "enable" : "disable") << " MEGASync on startup.";
        mPreferences->setStartOnStartup(!checked);
    }
    else
    {
        mPreferences->setStartOnStartup(checked);
    }
}

void SettingsDialog::on_cLanguage_currentIndexChanged(int index)
{
    if (mLoadingSettings)
        return;
    if (index < 0)
        return; // QComboBox can emit with index -1; do nothing in that case
    QString selectedLanguage = mLanguageCodes[index];
    if (mPreferences->language() != selectedLanguage)
    {
        mPreferences->setLanguage(selectedLanguage);
        mApp->changeLanguage(selectedLanguage);
        updateCacheSchedulerDaysLabel();
        QString currentLanguage = mApp->getCurrentLanguageCode();
        mThreadPool->push(
            [=]()
            {
                mMegaApi->setLanguage(currentLanguage.toUtf8().constData());
                mMegaApi->setLanguagePreference(currentLanguage.toUtf8().constData());
            });
    }
}

void SettingsDialog::on_cFileVersioning_toggled(bool checked)
{
    if (mLoadingSettings)
        return;

    if (checked)
    {
        // This is actually saved to Preferences after the MegaApi call succeeds;
        // Warning: the parameter is actually "disable".
        mMegaApi->setFileVersionsOption(false);
    }
    else
    {
        // Restore the check while the user has not answered the warning dialog
        mUi->cFileVersioning->blockSignals(true);
        mUi->cFileVersioning->setChecked(true);

        MessageDialogInfo msgInfo;
        msgInfo.descriptionText = tr("Disabling file versioning will prevent"
                                     " the creation and storage of new file versions."
                                     " Do you want to continue?");
        msgInfo.buttons = QMessageBox::Yes | QMessageBox::No;
        msgInfo.defaultButton = QMessageBox::No;
        msgInfo.parent = this;
        msgInfo.finishFunc = [this](QPointer<MessageDialogResult> msg)
        {
            // We want to make changes only if the answer is Yes.
            // If the user clicks No or closes the dialog with he X button, we don't want to disable
            // the feature.
            if (msg->result() == QMessageBox::Yes)
            {
                // This is actually saved to Preferences after the MegaApi call succeeds;
                // Warning: the parameter is actually "disable".
                mMegaApi->setFileVersionsOption(true);
                mUi->cFileVersioning->setChecked(false);
            }
            mUi->cFileVersioning->blockSignals(false);
        };

        MessageDialogOpener::warning(msgInfo);
    }
}

void SettingsDialog::on_cbSleepMode_toggled(bool checked)
{
    if (mLoadingSettings)
        return;

    // This is actually saved to Preferences before calling the keepAwake, as this method uses the
    // setting state;
    mPreferences->setAwakeIfActive(checked);

    PowerOptions options;
    auto result = options.keepAwake(checked);

    if (checked && !result)
    {
        MessageDialogInfo msgInfo;
        msgInfo.titleText = tr("Sleep mode can't be setup");
        msgInfo.descriptionText =
            tr("Your operating system doesn't allow its sleep setting to be overwritten.");
        msgInfo.buttons = QMessageBox::Ok;
        msgInfo.defaultButton = QMessageBox::Ok;
        msgInfo.parent = this;
        msgInfo.finishFunc = [this, checked](QPointer<MessageDialogResult> msg)
        {
            mUi->cbSleepMode->blockSignals(true);
            mUi->cbSleepMode->setChecked(!checked);
            mPreferences->setAwakeIfActive(!checked);
            mUi->cbSleepMode->blockSignals(false);
        };

        MessageDialogOpener::critical(msgInfo);
    }
}

void SettingsDialog::on_cOverlayIcons_toggled(bool checked)
{
    if (mLoadingSettings)
        return;

    mPreferences->disableOverlayIcons(!checked);

    const auto configuredSyncCount = mModel->getNumSyncedFolders(SyncInfo::AllHandledSyncTypes);
    if (configuredSyncCount <= 0)
        return;

    setOverlayCheckboxEnabled(false, checked);

#ifdef Q_OS_MACOS
    Platform::getInstance()->notifyRestartSyncFolders();
#endif
    mApp->notifyChangeToAllFolders();
}

#ifdef Q_OS_WINDOWS
// This settings toggles 2 integrations:
// 1. Shortcut to synced folders in the explorer (left pane)
// 2. Shell extension's context menu
void SettingsDialog::on_cDesktopIntegration_toggled(bool checked)
{
    if (mLoadingSettings)
        return;

    // 1. Toggle left pane sync shortcuts in the explorer
    if (checked)
    {
        for (const auto& syncSetting: mModel->getAllSyncSettings())
        {
            Platform::getInstance()->addSyncToLeftPane(syncSetting->getLocalFolder(),
                                                       syncSetting->name(),
                                                       syncSetting->getSyncID());
        }
    }
    else
    {
        Platform::getInstance()->removeAllSyncsFromLeftPane();
    }
    mPreferences->disableLeftPaneIcons(!checked);

    // 2. Toggle shell extension's context menu

    Platform::getInstance()->disableContextMenu(!checked);
    mPreferences->disableContextMenu(!checked);
}
#endif

void SettingsDialog::on_cbTheme_currentIndexChanged(int index)
{
    if (mLoadingSettings)
    {
        return;
    }

    ThemeManager::instance()->setTheme(mUi->cbTheme->currentData().value<Preferences::ThemeType>());
}

void SettingsDialog::on_bUpdate_clicked()
{
    if (mUi->bUpdate->text() == tr("Check for Updates"))
    {
        mApp->checkForUpdates();
    }
    else
    {
        mApp->triggerInstallUpdate();
    }
}

void SettingsDialog::on_bSendBug_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::SETTINGS_REPORT_ISSUE_CLICKED);

    QPointer<BugReportDialog> dialog = new BugReportDialog(nullptr, mApp->getLogger());
    // Center on Settings without parenting it: a Qt parent would make the dialog
    // modal and tie its visibility/lifetime to Settings. We only want the initial
    // position centered on this window.
    DialogOpener::setVisualParent(dialog, this);
    DialogOpener::showDialog(dialog);
}

void SettingsDialog::onCacheSizeAvailable()
{
    if (!mPreferences->logged())
        return;

    auto versionsStorage(mPreferences->versionsStorage());

    mUi->lFileVersionsSize->setText(Utilities::getSizeString(versionsStorage));

    if (mCacheSize != -1)
    {
        mUi->lCacheSize->setText(Utilities::getSizeString(mCacheSize));
    }

    if (mRemoteCacheSize != -1)
    {
        mUi->lRemoteCacheSize->setText(Utilities::getSizeString(mRemoteCacheSize));
    }

    mUi->bClearCache->setEnabled(mCacheSize > 0);
    mUi->bClearRemoteCache->setEnabled(mRemoteCacheSize > 0);
    mUi->bClearFileVersions->setEnabled(versionsStorage > 0);
}

// Account -----------------------------------------------------------------------------------------
void SettingsDialog::updateStorageElements()
{
    mAccountStateQuickWidget->updateStorageData();
}

void SettingsDialog::updateBandwidthElements()
{
    const auto quotaState = MegaSyncApp->getTransferQuota()->quotaState();
    mAccountStateQuickWidget->updateTransferData(quotaState);
}

void SettingsDialog::updateAccountElements()
{
    mUi->lAccountType->setText(Utilities::getReadablePlanFromId(mPreferences->accountType()));
    mUi->bUpgrade->show();
    mAccountStateQuickWidget->setShowStorageCard(true);
    mAccountStateQuickWidget->setShowTransferCard(true);

    switch (mPreferences->accountType())
    {
        case Preferences::ACCOUNT_TYPE_FREE:
            mUi->bUpgrade->show();
            mAccountStateQuickWidget->setShowTransferCard(false);
            break;
        case Preferences::ACCOUNT_TYPE_PROI:
            break;
        case Preferences::ACCOUNT_TYPE_PROII:
            break;
        case Preferences::ACCOUNT_TYPE_PROIII:
            break;
        case Preferences::ACCOUNT_TYPE_LITE:
            break;
        case Preferences::ACCOUNT_TYPE_BUSINESS:
            mUi->bUpgrade->hide();
            break;
        case Preferences::ACCOUNT_TYPE_PRO_FLEXI:
            break;
        case Preferences::ACCOUNT_TYPE_STARTER:
            break;
        case Preferences::ACCOUNT_TYPE_BASIC:
            break;
        case Preferences::ACCOUNT_TYPE_ESSENTIAL:
            break;
        default:
            mUi->lAccountType->setText(QString());
            break;
    }
}

void SettingsDialog::on_bAccount_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::SETTINGS_ACCOUNT_TAB_CLICKED);

    emit userActivity();

    if ((mUi->wStack->currentWidget() == mUi->pAccount))
    {
        return;
    }

    mUi->wStack->setCurrentWidget(mUi->pAccount);
}

void SettingsDialog::on_lAccountType_clicked()
{
    mDebugCounter++;
    if (mDebugCounter == NUMBER_OF_CLICKS_TO_DEBUG)
    {
        mApp->toggleLogging();
        mDebugCounter = 0;
    }
}

void SettingsDialog::onEmailClicked()
{
    mKeepLogsCounter++;
    if (mKeepLogsCounter == NUMBER_OF_CLICKS_TO_DEBUG)
    {
        mApp->toggleKeepLogsOnLogout();
        mKeepLogsCounter = 0;
    }
}

void SettingsDialog::on_bUpgrade_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::UPGRADE_CLICKED_SETTINGS);
    Utilities::upgradeClicked();
}

void SettingsDialog::on_bMyAccount_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::SETTINGS_MY_ACCOUNT_CLICKED);
    Utilities::openUrl(ServiceUrls::getAccountUrl());
}

void SettingsDialog::on_bLogout_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::LOGOUT_CLICKED);
    QString text;
    bool haveSyncs(false);
    bool haveBackups(false);

    // Check if we have syncs and backups
    if (mModel)
    {
        haveSyncs = !mModel->getSyncSettingsByType(mega::MegaSync::TYPE_TWOWAY).isEmpty();
        haveBackups = !mModel->getSyncSettingsByType(mega::MegaSync::TYPE_BACKUP).isEmpty();
    }

    // Set text according to situation
    if (haveSyncs && haveBackups)
    {
        text = tr("Synchronizations and backups will stop working.");
    }
    else if (haveBackups)
    {
        text = tr("Backups will stop working.");
    }
    else if (haveSyncs)
    {
        text = tr("Synchronizations will stop working.");
    }

    if (text.isEmpty())
    {
        mApp->unlink();
    }
    else
    {
        MessageDialogInfo msgInfo;
        msgInfo.titleText = tr("Log out");
        msgInfo.descriptionText = text + QLatin1Char(' ') + tr("Are you sure?");
        msgInfo.buttons = QMessageBox::Yes | QMessageBox::No;
        msgInfo.defaultButton = QMessageBox::Yes;
        msgInfo.parent = this;
        msgInfo.finishFunc = [](QPointer<MessageDialogResult> msg)
        {
            if (msg->result() == QMessageBox::Yes)
            {
                Utilities::queueFunctionInAppThread(
                    []()
                    {
                        MegaSyncApp->unlink();
                    });
            }
        };
        MessageDialogOpener::question(msgInfo);
    }
}

// Syncs -------------------------------------------------------------------------------------------
void SettingsDialog::setEnabledAllControls(const bool enabled)
{
    setGeneralTabEnabled(enabled);
    mUi->pAccount->setEnabled(enabled);
    mUi->pSecurity->setEnabled(enabled);
    mUi->pFolders->setEnabled(enabled);
    mUi->pNetwork->setEnabled(enabled);
    mUi->pNotifications->setEnabled(enabled);

    mUi->wStackFooter->setEnabled(enabled);
}

void SettingsDialog::setChangePasswordEnabled(bool enabled)
{
    mUi->bChangePassword->setEnabled(enabled);
}

void SettingsDialog::setGeneralTabEnabled(const bool enabled)
{
    // We want to keep only the "Send bug report" button enabled.
    // If we call setEnable() on the whole SettingsDialog, it will be
    // disabled and can't be enabled without enabling everything.
    // Another approach is to loop through all child widgets of SettingsDialog,
    // but we need to take care to skip all parents of BugReport button.
    // Experimentally it didn't work, so the last solution is to
    // call setEnable() manually for all controls and leave
    // Bug report controls as they are.

    mUi->gGeneral->setEnabled(enabled);
    mUi->cLanguage->setEnabled(enabled);
    mUi->wCacheSection->setEnabled(enabled);
    mUi->wRemoteCacheSection->setEnabled(enabled);
    mUi->wFileVersionsSection->setEnabled(enabled);

#ifdef Q_OS_LINUX
    mUi->cbSleepMode->setEnabled(enabled);
#endif
}

void SettingsDialog::setOverlayCheckboxEnabled(const bool enabled, const bool checked)
{
    const int emptyIndex = 0;
    const int processingIndex = 1;
    mUi->cOverlayIcons->setEnabled(enabled);
    mUi->sOverlayMessageWidget->setCurrentIndex(enabled ? emptyIndex : processingIndex);
    if (enabled)
    {
        mUi->wWaitingSpinner->stop();
    }
    else
    {
        QString message =
            checked ? tr("Enabling sync status icons") : tr("Disabling sync status icons");
        mUi->lOverlayProcessing->setText(message);
        mUi->wWaitingSpinner->start();
    }
}

// Backup ----------------------------------------------------------------------------------------
void SettingsDialog::on_bBackup_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::SETTINGS_BACKUP_TAB_CLICKED);

    emit userActivity();

    if (mUi->wStack->currentWidget() == mUi->pBackup)
    {
        return;
    }

    mUi->wStack->setCurrentWidget(mUi->pBackup);

    SyncInfo::instance()->dismissUnattendedDisabledSyncs(MegaSync::TYPE_BACKUP);
}

void SettingsDialog::on_bBackupCenter_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::SETTINGS_DEVICE_CENTER_CLICKED);
    Utilities::openBackupCenter();
}

void SettingsDialog::on_bSyncs_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::SETTINGS_SYNC_TAB_CLICKED);

    emit userActivity();

    if (mUi->wStack->currentWidget() == mUi->pSyncs)
    {
        return;
    }

    mUi->wStack->setCurrentWidget(mUi->pSyncs);

    SyncInfo::instance()->dismissUnattendedDisabledSyncs(MegaSync::TYPE_TWOWAY);
}

// Security ----------------------------------------------------------------------------------------
void SettingsDialog::on_bSecurity_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::SETTINGS_SECURITY_TAB_CLICKED);

    emit userActivity();

    if (mUi->wStack->currentWidget() == mUi->pSecurity)
    {
        return;
    }

    mUi->wStack->setCurrentWidget(mUi->pSecurity);
}

void SettingsDialog::on_bExportMasterKey_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::SETTINGS_EXPORT_KEY_CLICKED);

    QString defaultPath = QDir::toNativeSeparators(Utilities::getDefaultBasePath());
#ifndef _WIN32
    if (defaultPath.isEmpty())
    {
        defaultPath = QString::fromUtf8("/");
    }
#endif

    QDir dir(defaultPath);

    QPointer<QFileDialog> dialog = new QFileDialog(this);
    dialog->setWindowModality(Qt::WindowModal);
    dialog->setFileMode(QFileDialog::AnyFile);
    dialog->setOptions(QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
    dialog->selectFile(dir.filePath(tr("MEGA-RECOVERYKEY")));
    dialog->setWindowTitle(tr("Export Master key"));
    dialog->setNameFilter(QString::fromUtf8("Txt file (*.txt)"));
    const QStringList schemes = QStringList(QStringLiteral("file"));
    dialog->setSupportedSchemes(schemes);
    dialog->setAcceptMode(QFileDialog::AcceptSave);

    // Style
    TokenParserWidgetManager::styleQFileDialog(dialog);

    DialogOpener::showDialog<QFileDialog>(
        dialog,
        [this, dialog]
        {
            if (dialog->result() == QDialog::Accepted)
            {
                auto fileNames = dialog->selectedFiles();

                if (fileNames.isEmpty())
                {
                    return;
                }

                QFile file(fileNames.first());
                if (!file.open(QIODevice::WriteOnly | QFile::Truncate))
                {
                    MessageDialogInfo msgInfo;
                    msgInfo.parent = this;
                    msgInfo.titleText = tr("Unable to write file");
                    msgInfo.descriptionText = file.errorString();
                    MessageDialogOpener::warning(msgInfo);
                }
                else
                {
                    QTextStream out(&file);
                    out << mMegaApi->exportMasterKey();

                    file.close();

                    mMegaApi->masterKeyExported();

                    MessageDialogInfo msgInfo;
                    msgInfo.parent = this;
                    msgInfo.descriptionText =
                        tr("Exporting the master key and keeping it in a secure location"
                           " enables you to set a new password without data loss.") +
                        QString::fromUtf8("\n") +
                        tr("Always keep physical control of your master key (e.g. on a"
                           " client device, external storage, or print).");
                    MessageDialogOpener::information(msgInfo);
                }
            }
        });
}

void SettingsDialog::on_bChangePassword_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::SETTINGS_CHANGE_PASSWORD_CLICKED);

    ChangePasswordComponent::registerQmlModules();
    QPointer<QmlDialogWrapper<ChangePasswordComponent>> changePasswordDialog;
    if (auto dialog = DialogOpener::findDialog<QmlDialogWrapper<ChangePasswordComponent>>())
    {
        changePasswordDialog = dialog->getDialog();
    }
    else
    {
        changePasswordDialog = new QmlDialogWrapper<ChangePasswordComponent>(this);
    }

    DialogOpener::showDialog(changePasswordDialog,
                             [changePasswordDialog]()
                             {
                                 changePasswordDialog->deleteLater();
                             });
}

void SettingsDialog::on_bSessionHistory_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::SETTINGS_SESSION_HISTORY_CLICKED);
    Utilities::openUrl(ServiceUrls::getSessionHistoryUrl());
}

// File management -----------------------------------------------------------------------------
void SettingsDialog::updateUploadFolder()
{
    const QString defaultFolderName =
        QLatin1Char('/') + CommonMessages::getDefaultUploadFolderName();
    std::unique_ptr<MegaNode> node(
        mMegaApi->getNodeByHandle(static_cast<uint64_t>(mPreferences->uploadFolder())));
    if (!node)
    {
        mHasDefaultUploadOption = false;
        mUi->eUploadFolder->setText(defaultFolderName);
    }
    else
    {
        std::unique_ptr<const char[]> nPath(mMegaApi->getNodePath(node.get()));
        if (!nPath)
        {
            mHasDefaultUploadOption = false;
            mUi->eUploadFolder->setText(defaultFolderName);
        }
        else
        {
            mHasDefaultUploadOption = mPreferences->hasDefaultUploadFolder();
            mUi->eUploadFolder->setText(QString::fromUtf8(nPath.get()));
        }
    }
}

void SettingsDialog::updateDownloadFolder()
{
    QString downloadPath = mPreferences->downloadFolder();
    if (!downloadPath.size())
    {
        downloadPath = Utilities::getDefaultBasePath() + QLatin1Char('/') +
                       CommonMessages::getDefaultDownloadFolderName();
    }
    downloadPath = QDir::toNativeSeparators(downloadPath);
    mUi->eDownloadFolder->setText(downloadPath);
    mHasDefaultDownloadOption = mPreferences->hasDefaultDownloadFolder();
}

void SettingsDialog::on_bFolders_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::SETTINGS_FOLDERS_TAB_CLICKED);

    emit userActivity();

    if (mUi->wStack->currentWidget() == mUi->pFolders)
    {
        return;
    }
    mUi->wStack->setCurrentWidget(mUi->pFolders);
}

void SettingsDialog::on_bUploadFolder_clicked()
{
    UploadNodeSelector* nodeSelector = new UploadNodeSelector(this);
    nodeSelector->init();
    std::shared_ptr<mega::MegaNode> defaultNode(
        mMegaApi->getNodeByPath(mUi->eUploadFolder->text().toStdString().c_str()));
    nodeSelector->setSelectedNodeHandle(defaultNode);
    nodeSelector->setDefaultUploadOption(mHasDefaultUploadOption);
    nodeSelector->showDefaultUploadOption();

    DialogOpener::showDialog<NodeSelector>(
        nodeSelector,
        [nodeSelector, this]()
        {
            if (nodeSelector->result() == QDialog::Accepted)
            {
                MegaHandle selectedMegaFolderHandle = nodeSelector->getSelectedNodeHandle();
                std::shared_ptr<MegaNode> node(mMegaApi->getNodeByHandle(selectedMegaFolderHandle));
                if (node)
                {
                    std::unique_ptr<const char[]> nPath(mMegaApi->getNodePath(node.get()));
                    if (nPath && strlen(nPath.get()))
                    {
                        mHasDefaultUploadOption = nodeSelector->getDefaultUploadOption();
                        mUi->eUploadFolder->setText(QString::fromUtf8(nPath.get()));
                        mPreferences->setHasDefaultUploadFolder(mHasDefaultUploadOption);
                        mPreferences->setUploadFolder(node->getHandle());
                    }
                }
            }
        });
}

void SettingsDialog::on_bDownloadFolder_clicked()
{
    QPointer<DownloadFromMegaDialog> dialog =
        new DownloadFromMegaDialog(mPreferences->downloadFolder(), this);
    dialog->setDefaultDownloadOption(mHasDefaultDownloadOption);
    DialogOpener::showDialog<DownloadFromMegaDialog>(
        dialog,
        [dialog, this]()
        {
            if (dialog->result() == QDialog::Accepted)
            {
                QString fPath = dialog->getPath();
                if (!fPath.isEmpty())
                {
                    QTemporaryFile test(fPath + QDir::separator());
                    if (test.open())
                    {
                        mHasDefaultDownloadOption = dialog->isDefaultDownloadOption();
                        mUi->eDownloadFolder->setText(fPath);
                        mPreferences->setDownloadFolder(fPath);
                        mPreferences->setHasDefaultDownloadFolder(mHasDefaultDownloadOption);
                    }
                    else
                    {
                        MessageDialogInfo msgInfo;
                        msgInfo.parent = this;
                        msgInfo.descriptionText = tr("You don't have write permissions"
                                                     " in this local folder.");
                        MessageDialogOpener::critical(msgInfo);
                    }
                }
            }
        });
}

#ifndef Q_OS_WINDOWS
void SettingsDialog::onPermissionsClicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::SETTINGS_PERMISSIONS_CLICKED);
    MegaSyncApp->getMegaApi()->setDefaultFolderPermissions(
        Preferences::instance()->folderPermissionsValue());
    int folderPermissions = MegaSyncApp->getMegaApi()->getDefaultFolderPermissions();
    MegaSyncApp->getMegaApi()->setDefaultFilePermissions(
        Preferences::instance()->filePermissionsValue());
    int filePermissions = MegaSyncApp->getMegaApi()->getDefaultFilePermissions();

    QPointer<PermissionsDialog> dialog = new PermissionsDialog(this);
    dialog->setFolderPermissions(folderPermissions);
    dialog->setFilePermissions(filePermissions);
    DialogOpener::showDialog<PermissionsDialog>(
        dialog,
        [dialog]()
        {
            if (dialog->result() == QDialog::Accepted)
            {
                const auto filePermissions = dialog->filePermissions();
                const auto folderPermissions = dialog->folderPermissions();

                if (filePermissions != Preferences::instance()->filePermissionsValue() ||
                    folderPermissions != Preferences::instance()->folderPermissionsValue())
                {
                    Preferences::instance()->setFilePermissionsValue(filePermissions);
                    MegaSyncApp->getMegaApi()->setDefaultFilePermissions(filePermissions);

                    Preferences::instance()->setFolderPermissionsValue(folderPermissions);
                    MegaSyncApp->getMegaApi()->setDefaultFolderPermissions(folderPermissions);
                }
            }
        });
}
#endif

void SettingsDialog::onShellNotificationsProcessed()
{
    setOverlayCheckboxEnabled(true, mUi->cOverlayIcons->isChecked());
}

void SettingsDialog::onUserEmailChanged(mega::MegaHandle userHandle, const QString& newEmail)
{
    if (!mPreferences->logged())
    {
        return;
    }

    MegaHandle myHandle = mMegaApi->getMyUserHandleBinary();
    if (userHandle == myHandle)
    {
        mPreferences->setEmail(newEmail);
        mUi->lEmail->setText(newEmail);
        mUi->wAvatar->setUserEmail(newEmail.toUtf8().constData());
    }
}

// Network -----------------------------------------------------------------------------------------
void SettingsDialog::on_bNetwork_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::SETTINGS_NETWORK_TAB_CLICKED);

    emit userActivity();

    if (mUi->wStack->currentWidget() == mUi->pNetwork)
    {
        return;
    }

    mUi->wStack->setCurrentWidget(mUi->pNetwork);
}

void SettingsDialog::on_bOpenProxySettings_clicked()
{
    QPointer<ProxySettings> proxySettingsDialog(new ProxySettings(mApp, this));
    DialogOpener::showDialog<ProxySettings>(proxySettingsDialog,
                                            [proxySettingsDialog, this]()
                                            {
                                                if (proxySettingsDialog->result() ==
                                                    QDialog::Accepted)
                                                {
                                                    mApp->applyProxySettings();
                                                    updateNetworkTab();
                                                }
                                            });
}

void SettingsDialog::on_bNotifications_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::SETTINGS_NOTIFICATIONS_TAB_CLICKED);

    emit userActivity();

    if (mUi->wStack->currentWidget() == mUi->pNotifications)
    {
        return;
    }

    mUi->wStack->setCurrentWidget(mUi->pNotifications);
}

void SettingsDialog::initNetworkTab()
{
    mUi->eUploadLimit->setValidator(new QIntValidator(0, 1000000000, this));
    mUi->eDownloadLimit->setValidator(new QIntValidator(0, 1000000000, this));

    auto* uploadButtonGroup = new QButtonGroup(this);
    uploadButtonGroup->addButton(mUi->rUploadAutoLimit);
    uploadButtonGroup->addButton(mUi->rUploadNoLimit);
    uploadButtonGroup->addButton(mUi->rUploadLimit);

    auto* downloadButtonGroup = new QButtonGroup(this);
    downloadButtonGroup->addButton(mUi->rDownloadNoLimit);
    downloadButtonGroup->addButton(mUi->rDownloadLimit);

    mUi->eMaxDownloadConnections->setRange(ParallelConnectionsValues::getMinValue(),
                                           ParallelConnectionsValues::getMaxValue());
    mUi->eMaxUploadConnections->setRange(ParallelConnectionsValues::getMinValue(),
                                         ParallelConnectionsValues::getMaxValue());

    connect(mUi->rUploadAutoLimit,
            &QRadioButton::toggled,
            this,
            &SettingsDialog::onUploadLimitOptionChanged,
            Qt::UniqueConnection);
    connect(mUi->rUploadNoLimit,
            &QRadioButton::toggled,
            this,
            &SettingsDialog::onUploadLimitOptionChanged,
            Qt::UniqueConnection);
    connect(mUi->rUploadLimit,
            &QRadioButton::toggled,
            this,
            &SettingsDialog::onUploadLimitOptionChanged,
            Qt::UniqueConnection);
    connect(mUi->rDownloadNoLimit,
            &QRadioButton::toggled,
            this,
            &SettingsDialog::onDownloadLimitOptionChanged,
            Qt::UniqueConnection);
    connect(mUi->rDownloadLimit,
            &QRadioButton::toggled,
            this,
            &SettingsDialog::onDownloadLimitOptionChanged,
            Qt::UniqueConnection);
    connect(mUi->eUploadLimit,
            &QLineEdit::editingFinished,
            this,
            &SettingsDialog::onUploadLimitValueChanged,
            Qt::UniqueConnection);
    connect(mUi->eDownloadLimit,
            &QLineEdit::editingFinished,
            this,
            &SettingsDialog::onDownloadLimitValueChanged,
            Qt::UniqueConnection);
    connect(mUi->eMaxDownloadConnections,
            QOverload<int>::of(&QSpinBox::valueChanged),
            this,
            &SettingsDialog::onMaxDownloadConnectionsChanged,
            Qt::UniqueConnection);
    connect(mUi->eMaxUploadConnections,
            QOverload<int>::of(&QSpinBox::valueChanged),
            this,
            &SettingsDialog::onMaxUploadConnectionsChanged,
            Qt::UniqueConnection);
}

void SettingsDialog::updateNetworkTab()
{
    // Upload rate limit
    int uploadLimitKB = mPreferences->uploadLimitKB();
    mUi->rUploadAutoLimit->setChecked(uploadLimitKB < 0);
    mUi->rUploadNoLimit->setChecked(uploadLimitKB == 0);
    mUi->rUploadLimit->setChecked(uploadLimitKB > 0);
    mUi->eUploadLimit->setText(uploadLimitKB > 0 ? QString::number(uploadLimitKB) :
                                                   QString::fromUtf8("0"));
    mUi->eUploadLimit->setEnabled(uploadLimitKB > 0);

    // Download rate limit
    int downloadLimitKB = mPreferences->downloadLimitKB();
    mUi->rDownloadNoLimit->setChecked(downloadLimitKB <= 0);
    mUi->rDownloadLimit->setChecked(downloadLimitKB > 0);
    mUi->eDownloadLimit->setText(downloadLimitKB > 0 ? QString::number(downloadLimitKB) :
                                                       QString::fromUtf8("0"));
    mUi->eDownloadLimit->setEnabled(downloadLimitKB > 0);

    // Connections are owned by the SDK now (persisted per base path). Fetch the
    // live values asynchronously so the dialog is not blocked. The spin boxes are
    // disabled until the reply lands, so the user never sees or edits a stale
    // placeholder value (and cannot write back a misleading default). Signals are
    // blocked while applying a reply so seeding does not trigger a write to the
    // SDK, and the box is re-enabled on both success and error.
    mUi->eMaxUploadConnections->setEnabled(false);
    mUi->eMaxDownloadConnections->setEnabled(false);

    auto uploadConnListener = RequestListenerManager::instance().registerAndGetCustomFinishListener(
        this,
        [this](MegaRequest* request, MegaError* e)
        {
            if (e->getErrorCode() == MegaError::API_OK)
            {
                const QSignalBlocker blocker(mUi->eMaxUploadConnections);
                mUi->eMaxUploadConnections->setValue(static_cast<int>(request->getNumber()));
            }
            mUi->eMaxUploadConnections->setEnabled(true);
        });
    mMegaApi->getMaxUploadConnections(uploadConnListener.get());

    auto downloadConnListener =
        RequestListenerManager::instance().registerAndGetCustomFinishListener(
            this,
            [this](MegaRequest* request, MegaError* e)
            {
                if (e->getErrorCode() == MegaError::API_OK)
                {
                    const QSignalBlocker blocker(mUi->eMaxDownloadConnections);
                    mUi->eMaxDownloadConnections->setValue(static_cast<int>(request->getNumber()));
                }
                mUi->eMaxDownloadConnections->setEnabled(true);
            });
    mMegaApi->getMaxDownloadConnections(downloadConnListener.get());

    // Proxy
    switch (mPreferences->proxyType())
    {
        case Preferences::PROXY_TYPE_NONE:
            mUi->lProxySettings->setText(tr("No Proxy"));
            break;
        case Preferences::PROXY_TYPE_AUTO:
            mUi->lProxySettings->setText(
                QCoreApplication::translate("SettingsDialog_Proxies", "Auto"));
            break;
        case Preferences::PROXY_TYPE_CUSTOM:
            mUi->lProxySettings->setText(tr("Manual"));
            break;
    }
}

void SettingsDialog::onUploadLimitOptionChanged()
{
    if (mLoadingSettings)
        return;

    if (mUi->rUploadAutoLimit->isChecked())
    {
        mPreferences->setUploadLimitKB(-1);
        mApp->setMaxUploadSpeed(-1);
        mUi->eUploadLimit->setEnabled(false);
    }
    else if (mUi->rUploadNoLimit->isChecked())
    {
        mPreferences->setUploadLimitKB(0);
        mApp->setMaxUploadSpeed(0);
        mUi->eUploadLimit->setEnabled(false);
    }
    else if (mUi->rUploadLimit->isChecked())
    {
        mUi->eUploadLimit->setEnabled(true);
        int limit = mUi->eUploadLimit->text().toInt();
        if (limit > 0)
        {
            mPreferences->setUploadLimitKB(limit);
            mApp->setMaxUploadSpeed(limit);
        }
    }
}

void SettingsDialog::onUploadLimitValueChanged()
{
    if (mLoadingSettings || !mUi->rUploadLimit->isChecked())
        return;

    int limit = mUi->eUploadLimit->text().toInt();
    if (limit > 0)
    {
        mPreferences->setUploadLimitKB(limit);
        mApp->setMaxUploadSpeed(limit);
    }
}

void SettingsDialog::onDownloadLimitOptionChanged()
{
    if (mLoadingSettings)
        return;

    if (mUi->rDownloadNoLimit->isChecked())
    {
        mPreferences->setDownloadLimitKB(0);
        mApp->setMaxDownloadSpeed(0);
        mUi->eDownloadLimit->setEnabled(false);
    }
    else if (mUi->rDownloadLimit->isChecked())
    {
        mUi->eDownloadLimit->setEnabled(true);
        int limit = mUi->eDownloadLimit->text().toInt();
        if (limit > 0)
        {
            mPreferences->setDownloadLimitKB(limit);
            mApp->setMaxDownloadSpeed(limit);
        }
    }
}

void SettingsDialog::onDownloadLimitValueChanged()
{
    if (mLoadingSettings || !mUi->rDownloadLimit->isChecked())
        return;

    int limit = mUi->eDownloadLimit->text().toInt();
    if (limit > 0)
    {
        mPreferences->setDownloadLimitKB(limit);
        mApp->setMaxDownloadSpeed(limit);
    }
}

void SettingsDialog::onMaxDownloadConnectionsChanged(int value)
{
    if (mLoadingSettings)
        return;

    mMegaApi->setMaxConnections(MegaTransfer::TYPE_DOWNLOAD, value);
}

void SettingsDialog::onMaxUploadConnectionsChanged(int value)
{
    if (mLoadingSettings)
        return;

    mMegaApi->setMaxConnections(MegaTransfer::TYPE_UPLOAD, value);
}

void SettingsDialog::setShortCutsForToolBarItems()
{
    // Provide quick access shortcuts for Settings panes via Ctrl+1,2,3..
    // Ctrl is auto-magically translated to CMD key by Qt on macOS
    for (int i = 0; i < mUi->wStack->count(); ++i)
    {
        QShortcut* scGeneral =
            new QShortcut(QKeySequence(QString::fromLatin1("Ctrl+%1").arg(i + 1)), this);
        QObject::connect(scGeneral,
                         &QShortcut::activated,
                         this,
                         [=]()
                         {
                             openSettingsTab(i);
                         });
    }
}

void SettingsDialog::updateCacheSchedulerDaysLabel()
{
    mUi->lCacheSchedulerSuffix->setText(tr("day", "", mPreferences->cleanerDaysLimitValue()));
}

void SettingsDialog::startRequestTaskbarPinningTimer()
{
    auto preferences = Preferences::instance();
    if (preferences->logged() &&
        !preferences->isOneTimeActionUserDone(Preferences::ONE_TIME_ACTION_REQUEST_PIN_TASKBAR))
    {
        mTaskbarPinningRequestTimer = new QTimer(this);
        connect(mTaskbarPinningRequestTimer,
                &QTimer::timeout,
                this,
                &SettingsDialog::onRequestTaskbarPinningTimeout);

        mTaskbarPinningRequestTimer->start(500ms);
    }
}

void SettingsDialog::initColorTheme()
{
    const auto themes = ThemeManager::instance()->getAvailableThemes();

    // Init
    if (mUi->cbTheme->count() == 0)
    {
        // Add available themes
        for (auto theme = themes.constKeyValueBegin(); theme != themes.constKeyValueEnd(); ++theme)
        {
            mUi->cbTheme->addItem(theme->second,
                                  QVariant::fromValue<Preferences::ThemeType>(theme->first));
        }

        // Set current index to currently used theme
        const auto currentTheme = ThemeManager::instance()->getCurrentTheme();
        mUi->cbTheme->setCurrentIndex(
            mUi->cbTheme->findData(QVariant::fromValue<Preferences::ThemeType>(currentTheme)));
    }
    // Re-translate
    else if (mUi->cbTheme->count() == themes.size())
    {
        // Update entries with new translation
        for (auto theme = themes.constKeyValueBegin(); theme != themes.constKeyValueEnd(); ++theme)
        {
            const auto themeIndex =
                mUi->cbTheme->findData(QVariant::fromValue<Preferences::ThemeType>(theme->first));
            mUi->cbTheme->setItemText(themeIndex, theme->second);
        }
    }
}

void SettingsDialog::onRequestTaskbarPinningTimeout()
{
    mTaskbarPinningRequestTimer->stop();
    Platform::getInstance()->pinOnTaskbar();
}

void SettingsDialog::onBLearnMore()
{
    Utilities::openUrl(ServiceUrls::getSyncDebrisHelpLink());
}

void SettingsDialog::onBAboutMega()
{
    MegaSyncApp->showChangeLog();
}
