/*
    SPDX-FileCopyrightText: 2014-2023 Anne Jan Brouwer <brouwer@annejan.com>
    SPDX-FileCopyrightText: 2018 Claudio Maradonna <penguyman@stronzi.org>
    SPDX-FileCopyrightText: 2023 g10 Code GmbH
    SPDX-FileContributor: Sune Stolborg Vuorela <sune@vuorela.dk>

    SPDX-License-Identifier: GPL-3.0-or-later
*/

#include "configdialog.h"
#include "mainwindow.h"
#include "settings.h"
#include "ui_configdialog.h"
#include "util.h"
#include <KLocalizedString>
#include <QDir>
#include <QFileDialog>
#include <QMessageBox>
#include <QPushButton>
#include <QTableWidgetItem>
#include <gpgpass_version.h>

/**
 * @brief ConfigDialog::ConfigDialog this sets up the configuration screen.
 * @param parent
 */
ConfigDialog::ConfigDialog(MainWindow *parent)
    : QDialog(parent)
    , ui(new Ui::ConfigDialog)
{
    mainWindow = parent;
    ui->setupUi(this);

    ui->profileStorePath->setText(Settings::getPassStore());
    ui->currentPathLineEdit->setText(Settings::getPassStore());

    ui->spinBoxAutoclearSeconds->setValue(Settings::getAutoclearSeconds());
    ui->spinBoxAutoclearPanelSeconds->setValue(Settings::getAutoclearPanelSeconds());
    ui->checkBoxDisplayAsIs->setChecked(Settings::isDisplayAsIs());
    ui->checkBoxNoLineWrapping->setChecked(Settings::isNoLineWrapping());

    ui->plainTextEditTemplate->setPlainText(Settings::getPassTemplate());
    ui->checkBoxTemplateAllFields->setChecked(Settings::isTemplateAllFields());

    setProfiles(Settings::getProfiles(), Settings::getProfile());
    setPasswordConfiguration(Settings::getPasswordConfiguration());

    useAutoclear(Settings::isUseAutoclear());
    useAutoclearPanel(Settings::isUseAutoclearPanel());

    validateNewProfile();

    useTemplate(Settings::isUseTemplate());

    ui->profileTable->verticalHeader()->hide();
    ui->profileTable->horizontalHeader()->setStretchLastSection(true);
    ui->label->setText(ui->label->text() + QString::fromUtf8(GPGPASS_VERSION_STRING));

    // Generic setup
    connect(this, &ConfigDialog::accepted, this, &ConfigDialog::on_accepted);

    // General tab
    connect(ui->toolButtonStore, &QAbstractButton::clicked, this, &ConfigDialog::selectStoreFolder);
    connect(ui->checkBoxAutoclearPanel, &QAbstractButton::toggled, this, &ConfigDialog::toggleAutoClearPanelSubentries);
    connect(ui->checkBoxAutoclear, &QAbstractButton::toggled, this, &ConfigDialog::toggleAutoClearSubentries);

    // Profiles tab
    connect(ui->addButton, &QAbstractButton::clicked, this, &ConfigDialog::addProfile);
    connect(ui->deleteButton, &QAbstractButton::clicked, this, &ConfigDialog::deleteSelectedProfile);
    connect(ui->createNewProfileFolder, &QAbstractButton::toggled, this, &ConfigDialog::validateNewProfile);
    connect(ui->newProfileNameEdit, &QLineEdit::textChanged, this, &ConfigDialog::validateNewProfile);
    connect(ui->profileStorePath, &QLineEdit::textChanged, this, &ConfigDialog::validateNewProfile);
    connect(ui->profileTable, &QTableWidget::itemSelectionChanged, this, [this]() {
        ui->deleteButton->setEnabled(!ui->profileTable->selectedItems().isEmpty());
    });

    // template tab
    connect(ui->checkBoxUseTemplate, &QAbstractButton::toggled, this, &ConfigDialog::toggleTemplateSubentries);
}

bool ConfigDialog::checkNewProfileData()
{
    QString name = ui->newProfileNameEdit->text();
    if (name.isEmpty()) {
        return false;
    }
    QString path = ui->profileStorePath->text();
    if (path.isEmpty()) {
        return false;
    }
    auto nameItem = ui->profileTable->findItems(name, Qt::MatchExactly);
    if (!nameItem.empty()) {
        return false;
    }
    auto pathItem = ui->profileTable->findItems(path, Qt::MatchExactly);
    if (!pathItem.empty()) {
        return false;
    }
    QFileInfo fi(path);
    if (fi.exists()) {
        return fi.isDir();
    } else {
        return ui->createNewProfileFolder->isChecked();
    }
}

void ConfigDialog::validateNewProfile()
{
    ui->addButton->setEnabled(checkNewProfileData());
}

ConfigDialog::~ConfigDialog() = default;

void ConfigDialog::on_accepted()
{
    Settings::setPassStore(Util::normalizeFolderPath(ui->currentPathLineEdit->text()));
    Settings::setUseAutoclear(ui->checkBoxAutoclear->isChecked());
    Settings::setAutoclearSeconds(ui->spinBoxAutoclearSeconds->value());
    Settings::setUseAutoclearPanel(ui->checkBoxAutoclearPanel->isChecked());
    Settings::setAutoclearPanelSeconds(ui->spinBoxAutoclearPanelSeconds->value());
    Settings::setDisplayAsIs(ui->checkBoxDisplayAsIs->isChecked());
    Settings::setNoLineWrapping(ui->checkBoxNoLineWrapping->isChecked());
    Settings::setProfiles(getProfiles());
    Settings::setPasswordConfiguration(getPasswordConfiguration());
    Settings::setUseTemplate(ui->checkBoxUseTemplate->isChecked());
    Settings::setPassTemplate(ui->plainTextEditTemplate->toPlainText());
    Settings::setTemplateAllFields(ui->checkBoxTemplateAllFields->isChecked());

    Settings::setVersion(QString::fromUtf8(GPGPASS_VERSION_STRING));
}

/**
 * @brief ConfigDialog::selectFolder pop-up to choose a folder.
 * @return
 */
QString ConfigDialog::selectFolder()
{
    QFileDialog dialog(this);
    dialog.setFileMode(QFileDialog::Directory);
    dialog.setFilter(QDir::NoFilter);
    dialog.setOption(QFileDialog::ShowDirsOnly);
    if (dialog.exec())
        return dialog.selectedFiles().constFirst();

    return QString();
}

/**
 * @brief ConfigDialog::on_toolButtonStore_clicked get .password-store
 * location.s
 */
void ConfigDialog::selectStoreFolder()
{
    QString store = selectFolder();
    if (!store.isEmpty())
        ui->profileStorePath->setText(store);
}

/**
 * @brief ConfigDialog::on_checkBoxAutoclearPanel_clicked enable and disable
 * options based on autoclear use.
 */
void ConfigDialog::toggleAutoClearPanelSubentries(bool state)
{
    ui->spinBoxAutoclearPanelSeconds->setEnabled(state);
    ui->labelPanelSeconds->setEnabled(state);
}

/**
 * @brief ConfigDialog::on_checkBoxAutoclearPanel_clicked enable and disable
 * options based on autoclear use.
 */
void ConfigDialog::toggleAutoClearSubentries(bool state)
{
    ui->spinBoxAutoclearSeconds->setEnabled(state);
}

/**
 * @brief ConfigDialog::useAutoclear set the clipboard autoclear use from
 * MainWindow.
 * @param useAutoclear
 */
void ConfigDialog::useAutoclear(bool useAutoclear)
{
    ui->checkBoxAutoclear->setChecked(useAutoclear);
    toggleAutoClearSubentries(useAutoclear);
}

/**
 * @brief ConfigDialog::useAutoclearPanel set the panel autoclear use from
 * MainWindow.
 * @param useAutoclearPanel
 */
void ConfigDialog::useAutoclearPanel(bool useAutoclearPanel)
{
    ui->checkBoxAutoclearPanel->setChecked(useAutoclearPanel);
    toggleAutoClearPanelSubentries(useAutoclearPanel);
}

static std::unique_ptr<QTableWidgetItem> createTableWidgetItem(const QString &str)
{
    auto item = std::make_unique<QTableWidgetItem>(str);
    item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    return item;
}

/**
 * @brief ConfigDialog::setProfiles set the profiles and chosen profile from
 * MainWindow.
 * @param profiles
 * @param profile
 */
void ConfigDialog::setProfiles(const QHash<QString, QString> &profiles, const QString &profile)
{
    ui->profileTable->clear();
    ui->profileTable->setSortingEnabled(false);
    QHashIterator<QString, QString> i(profiles);
    while (i.hasNext()) {
        i.next();
        if (!i.value().isEmpty() && !i.key().isEmpty()) {
            ui->profileTable->insertRow(0);
            ui->profileTable->setItem(0, 0, createTableWidgetItem(i.key()).release());
            ui->profileTable->setItem(0, 1, createTableWidgetItem(i.value()).release());
        }
    }
    auto selected = ui->profileTable->findItems(profile, Qt::MatchExactly);
    if (!selected.empty()) {
        ui->profileTable->selectRow(selected.front()->row());
    }
    ui->profileTable->setSortingEnabled(true);
}

/**
 * @brief ConfigDialog::getProfiles return profile list.
 * @return
 */
QHash<QString, QString> ConfigDialog::getProfiles()
{
    QHash<QString, QString> profiles;
    // Check?
    for (int i = 0; i < ui->profileTable->rowCount(); ++i) {
        QTableWidgetItem *pathItem = ui->profileTable->item(i, 1);
        if (nullptr != pathItem) {
            QTableWidgetItem *item = ui->profileTable->item(i, 0);
            if (item == nullptr) {
                continue;
            }
            profiles.insert(item->text(), pathItem->text());
        }
    }
    return profiles;
}

/**
 * @brief ConfigDialog::on_addButton_clicked add a profile row.
 */
void ConfigDialog::addProfile()
{
    if (!checkNewProfileData()) {
        return;
    }
    ui->profileTable->setSortingEnabled(false);
    int n = ui->profileTable->rowCount();
    ui->profileTable->insertRow(n);
    ui->profileTable->setItem(n, 0, createTableWidgetItem(ui->newProfileNameEdit->text()).release());
    ui->profileTable->setItem(n, 1, createTableWidgetItem(ui->profileStorePath->text()).release());
    ui->profileTable->selectRow(n);
    ui->deleteButton->setEnabled(true);
    ui->profileTable->setSortingEnabled(true);
}

/**
 * @brief ConfigDialog::on_deleteButton_clicked remove a profile row.
 */
void ConfigDialog::deleteSelectedProfile()
{
    QSet<int> selectedRows; // we use a set to prevent doubles
    const QList<QTableWidgetItem *> itemList = ui->profileTable->selectedItems();
    if (itemList.count() == 0) {
        QMessageBox::warning(this, i18n("No profile selected"), i18n("No profile selected to delete"));
        return;
    }
    ui->profileTable->setSortingEnabled(false);
    for (const auto *item : itemList)
        selectedRows.insert(item->row());
    // get a list, and sort it big to small
    QList<int> rows = selectedRows.values();
    std::sort(rows.begin(), rows.end());
    // now actually do the removing:
    for (int row : std::as_const(rows))
        ui->profileTable->removeRow(row);
    if (ui->profileTable->rowCount() < 1)
        ui->deleteButton->setEnabled(false);
    ui->profileTable->setSortingEnabled(true);
}

void ConfigDialog::setPasswordConfiguration(const PasswordConfiguration &config)
{
    ui->spinBoxPasswordLength->setValue(config.length);
    ui->passwordCharTemplateSelector->setCurrentIndex(config.selected);
}

PasswordConfiguration ConfigDialog::getPasswordConfiguration()
{
    PasswordConfiguration config;
    config.length = ui->spinBoxPasswordLength->value();
    config.selected = static_cast<PasswordConfiguration::characterSet>(ui->passwordCharTemplateSelector->currentIndex());
    return config;
}

/**
 * @brief ConfigDialog::on_checkBoxUseTemplate_clicked enable or disable the
 * template field and options.
 */
void ConfigDialog::toggleTemplateSubentries(bool enable)
{
    ui->plainTextEditTemplate->setEnabled(enable);
    ui->checkBoxTemplateAllFields->setEnabled(enable);
}

/**
 * @brief ConfigDialog::useTemplate set preference for using templates.
 * @param useTemplate
 */
void ConfigDialog::useTemplate(bool useTemplate)
{
    ui->checkBoxUseTemplate->setChecked(useTemplate);
    toggleTemplateSubentries(useTemplate);
}
