// SPDX-FileCopyrightText: 2024 g10 Code GmbH
// SPDX-FileContributor: Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: GPL-2.0-or-later

#include "rootfoldersconfigurationpage.h"

#include "models/rootfoldersmodel.h"
#include "rootfolderconfig.h"
#include "rootfoldersmanager.h"
#include "ui_rootfoldersconfigurationpage.h"

#include <QAction>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QFileInfo>
#include <QLabel>
#include <QMessageBox>
#include <QPainter>
#include <QPushButton>
#include <QTimer>
#include <QToolButton>

#include <KMessageBox>
#include <KSelectionProxyModel>
#include <KUrlRequester>
#include <KWidgetItemDelegate>
#include <Libkleo/DefaultKeyFilter>
#include <Libkleo/KeySelectionCombo>

#include "kwidgetsaddons_version.h"

using namespace GpgPass::Config;

class DummyButton : public QToolButton
{
public:
    int buttonSize(QAbstractItemView *view)
    {
        constexpr int iconSize = 16;
        QStyleOptionToolButton styleOption;
        initStyleOption(&styleOption);
        const auto size = view->style()->sizeFromContents(QStyle::CT_ToolButton, &styleOption, QSize(iconSize, iconSize));
        return qMax(size.width(), size.height());
    }
};

class RootFolderItemDelegate : public KWidgetItemDelegate
{
    Q_OBJECT

public:
    explicit RootFolderItemDelegate(QAbstractItemView *view)
        : KWidgetItemDelegate(view)
    {
        mMargin = 4;
        mSpacing = 4;
        DummyButton dummyButton;
        mButtonSize = dummyButton.buttonSize(view);
    }

protected:
    QList<QWidget *> createItemWidgets(const QModelIndex &index) const override
    {
        if (index.column() != 0 && qobject_cast<KSelectionProxyModel const *>(index.model()) != nullptr) {
            return {};
        }
        auto removeButton = new QToolButton;
        removeButton->setAccessibleName(i18nc("@action:button", "Delete Folder"));
        removeButton->setToolTip(i18nc("@info:tooltip", "Delete folder"));
        removeButton->setAutoRaise(true);
        setBlockedEventTypes(removeButton, {QEvent::MouseButtonPress, QEvent::MouseButtonRelease, QEvent::MouseButtonDblClick});
        removeButton->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
        connect(removeButton, &QToolButton::clicked, this, &RootFolderItemDelegate::slotRemoveButtonClicked);

        return {removeButton};
    }

#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
    void updateItemWidgets(const QList<QWidget *> &widgets, const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const override
#else
    void updateItemWidgets(const QList<QWidget *> widgets, const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const override
#endif
    {
        if (widgets.count() == 0) {
            return;
        }
        auto removeButton = static_cast<QToolButton *>(widgets[0]);
        QSize buttonSize(mButtonSize, option.rect.height() - 2 * mMargin);
        removeButton->resize(buttonSize);
        removeButton->move(option.rect.width() - mButtonSize - mMargin, mMargin);
        removeButton->setVisible(itemView()->model()->rowCount() > 1);
    }

    void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
    {
        if (!index.isValid()) {
            return;
        }
        const bool hasFocus = option.state & QStyle::State_HasFocus;

        itemView()->style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, nullptr);

        QRect textRect = option.rect;
        textRect.setLeft(textRect.left() + mMargin * 2);
        textRect.setWidth(textRect.width() - mButtonSize - mMargin - mSpacing);

        itemView()->style()->drawItemText(painter,
                                          textRect,
                                          Qt::AlignLeft | Qt::AlignVCenter,
                                          option.palette,
                                          true,
                                          index.data().toString(),
                                          hasFocus ? QPalette::HighlightedText : QPalette::Text);
    }

    QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
    {
        const int width = option.fontMetrics.boundingRect(index.data().toString()).width();
        const int height = qMax(mButtonSize, option.fontMetrics.height());
        return QSize(width + 2 * mMargin, height + 2 * mMargin);
    }

Q_SIGNALS:
    void removeFolder(const QModelIndex &index);

private:
    void slotRemoveButtonClicked()
    {
        const QModelIndex index = focusedIndex();
        if (!index.isValid()) {
            qWarning() << "!index.isValid()";
            return;
        }

        Q_EMIT removeFolder(index);
    }

    int mMargin;
    int mSpacing;
    int mButtonSize;
};

RootFoldersConfigurationPage::RootFoldersConfigurationPage(RootFoldersManager *rootFoldersManager, QAction *addStoreAction, QWidget *parent)
    : GpgPassConfigModule(parent)
    , ui(new Ui::RootFoldersConfigurationPage)
    , m_rootFoldersManager(rootFoldersManager)
    , m_rootFoldersModel(new RootFoldersModel(m_rootFoldersManager, this))
    , m_addStoreAction(addStoreAction)
{
    ui->setupUi(this);

    ui->rootFoldersView->setModel(m_rootFoldersModel);
    auto delegate = new RootFolderItemDelegate(ui->rootFoldersView);
    ui->rootFoldersView->setItemDelegate(delegate);

    ui->splitter->setStretchFactor(1, 3);

    connect(ui->rootFoldersView->selectionModel(), &QItemSelectionModel::currentChanged, this, &RootFoldersConfigurationPage::selectTreeItem);
    connect(m_addStoreAction, &QAction::triggered, this, [this]() {
        const auto idx = m_rootFoldersModel->addNewFolder();
        ui->rootFoldersView->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
    });

    // Name
    m_nameInput = Kleo::FormTextInput<QLineEdit>::create(this);
    m_nameInput->setLabelText(i18nc("@label", "Name:"));
    m_nameInput->setValueRequiredErrorMessage(i18n("Enter a valid path."));

    ui->folderInfoLayout->addWidget(m_nameInput->label());
    ui->folderInfoLayout->addWidget(m_nameInput->hintLabel());
    ui->folderInfoLayout->addWidget(m_nameInput->errorLabel());
    ui->folderInfoLayout->addWidget(m_nameInput->widget());
    ui->folderInfoLayout->addSpacing(style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing));

    // Location
    m_locationInput = Kleo::FormTextInput<KUrlRequester>::create(this);
    m_locationInput->setLabelText(i18nc("@label", "Path:"));
    m_locationInput->setValueRequiredErrorMessage(i18n("Enter a valid path."));
    m_locationInput->setInvalidEntryErrorMessage(i18n("Enter a valid path."));
    m_locationInput->widget()->setMode(KFile::Directory);

    ui->folderInfoLayout->addWidget(m_locationInput->label());
    ui->folderInfoLayout->addWidget(m_locationInput->hintLabel());
    ui->folderInfoLayout->addWidget(m_locationInput->errorLabel());
    ui->folderInfoLayout->addWidget(m_locationInput->widget());
    ui->folderInfoLayout->addStretch();

#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
    auto button = new QToolButton(this);
    button->setDefaultAction(addStoreAction);
    button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
    ui->folderInfoLayout->addWidget(button);
#endif

    ui->folderInfoLayout->setContentsMargins(style()->pixelMetric(QStyle::PM_LayoutLeftMargin),
                                             style()->pixelMetric(QStyle::PM_LayoutTopMargin),
                                             style()->pixelMetric(QStyle::PM_LayoutRightMargin),
                                             style()->pixelMetric(QStyle::PM_LayoutBottomMargin));

    connect(m_locationInput->widget(), &KUrlRequester::textChanged, this, &RootFoldersConfigurationPage::slotLocationChanged);
    connect(m_nameInput->widget(), &QLineEdit::textEdited, this, &RootFoldersConfigurationPage::slotNameChanged);
    connect(delegate, &RootFolderItemDelegate::removeFolder, m_rootFoldersModel, &RootFoldersModel::slotRemoveFolder);
    connect(delegate, &RootFolderItemDelegate::removeFolder, this, &RootFoldersConfigurationPage::changed);
}

void RootFoldersConfigurationPage::save()
{
    m_rootFoldersManager->save();
}

void RootFoldersConfigurationPage::load()
{
    m_rootFoldersModel->load();

    if (m_rootFoldersModel->rowCount({}) > 0) {
        const auto idx = m_rootFoldersModel->index(0, 0);
        ui->rootFoldersView->selectionModel()->setCurrentIndex(idx, QItemSelectionModel::ClearAndSelect);
    }
}

void RootFoldersConfigurationPage::defaults()
{
    m_rootFoldersManager->load();
}

void RootFoldersConfigurationPage::selectTreeItem(const QModelIndex &)
{
    const auto rootFolder = fromCurrentIndex();
    if (!rootFolder) {
        return;
    }

    m_nameInput->widget()->setText(rootFolder->name());

    m_locationInput->widget()->setUrl(QUrl::fromLocalFile(rootFolder->path()));

    for (int i = 0, count = ui->folderInfoLayout->count(); i < count; i++) {
        const auto item = ui->folderInfoLayout->itemAt(i);
        const auto spacer = item->spacerItem();
        if (spacer) {
            spacer->changeSize(0, style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing), QSizePolicy::Minimum, QSizePolicy::Fixed);
            break;
        }
    }
    ui->folderInfoLayout->invalidate();
}

RootFolderConfig *RootFoldersConfigurationPage::fromCurrentIndex()
{
    const auto index = ui->rootFoldersView->currentIndex();
    if (!index.isValid()) {
        return {};
    }

    return index.data(Qt::UserRole).value<RootFolderConfig *>();
}

void RootFoldersConfigurationPage::slotNameChanged(const QString &text)
{
    auto rootFolder = fromCurrentIndex();
    rootFolder->setName(text);
    rootFolder->save();

    Q_EMIT changed();
}

void RootFoldersConfigurationPage::slotLocationChanged(const QString &text)
{
    auto rootFolder = fromCurrentIndex();
    const auto oldPath = rootFolder->path();
    if (oldPath == text) {
        return;
    }
    rootFolder->setPath(m_locationInput->widget()->url().toLocalFile());
    rootFolder->save();

    Q_EMIT changed();
}

#include "rootfoldersconfigurationpage.moc"
