// SPDX-FileCopyrightText: 2002 Marc Mutz <mutz@kde.org>
// SPDX-FileCopyrightText: 2014-2024 Laurent Montel <montel@kde.org>
// SPDX-FileCopyrightText: 2024 g10 Code GmbH
// SPDX-FileContributor: Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: GPL-2.0-or-later

#include "openpgpcertificatecreationjob.h"

#if KLEO_HAS_CERTIFICATE_CREATION_DIALOG
#include <QProgressDialog>

#include <KLocalizedString>

#include <Libkleo/Formatting>
#include <Libkleo/KeyParameters>
#include <Libkleo/OpenPGPCertificateCreationDialog>

#include <QGpgME/KeyGenerationJob>
#include <QGpgME/Protocol>

#include <gpgme++/context.h>
#include <gpgme++/keygenerationresult.h>

char *EmptyPassphraseProvider::getPassphrase(const char *useridHint, const char *description, bool previousWasBad, bool &canceled)
{
    Q_UNUSED(useridHint);
    Q_UNUSED(description);
    Q_UNUSED(previousWasBad);
    Q_UNUSED(canceled);
    return gpgrt_strdup("");
}

OpenPGPCertificateCreationJob::OpenPGPCertificateCreationJob(const QString &name, const QString &email, QWidget *parent)
    : KJob(parent)
    , mName(name)
    , mEmail(email)
{
}

OpenPGPCertificateCreationJob::~OpenPGPCertificateCreationJob() = default;

bool OpenPGPCertificateCreationJob::doKill()
{
    if (mJob) {
        mJob->slotCancel();
    }
    return true;
}

void OpenPGPCertificateCreationJob::start()
{
    auto dialog = new Kleo::OpenPGPCertificateCreationDialog(qobject_cast<QWidget *>(parent()));
    dialog->setName(mName);
    dialog->setEmail(mEmail);
    dialog->setAttribute(Qt::WA_DeleteOnClose);

    connect(dialog, &QDialog::accepted, this, [this, dialog]() {
        const auto keyParameters = dialog->keyParameters();
        const auto protectKeyWithPassword = dialog->protectKeyWithPassword();

        QMetaObject::invokeMethod(
            this,
            [this, keyParameters, protectKeyWithPassword] {
                createCertificate(keyParameters, protectKeyWithPassword);
            },
            Qt::QueuedConnection);
    });
    connect(dialog, &QDialog::rejected, this, [this]() {
        emitResult();
    });

    dialog->show();
}

void OpenPGPCertificateCreationJob::createCertificate(const Kleo::KeyParameters &keyParameters, bool protectKeyWithPassword)
{
    Q_ASSERT(keyParameters.protocol() == Kleo::KeyParameters::OpenPGP);

    auto keyGenJob = QGpgME::openpgp()->keyGenerationJob();
    if (!keyGenJob) {
        setError(GpgError);
        setErrorText(i18nc("@info:status", "Could not start OpenPGP certificate generation."));
        emitResult();
        return;
    }
    if (!protectKeyWithPassword) {
        auto ctx = QGpgME::Job::context(keyGenJob);
        ctx->setPassphraseProvider(&emptyPassphraseProvider);
        ctx->setPinentryMode(GpgME::Context::PinentryLoopback);
    }

    connect(keyGenJob, &QGpgME::KeyGenerationJob::result, this, &OpenPGPCertificateCreationJob::keyGenerated);
    if (const GpgME::Error err = keyGenJob->start(keyParameters.toString())) {
        setError(GpgError);
        setErrorText(i18n("Could not start OpenPGP certificate generation: %1", Kleo::Formatting::errorAsString(err)));
        emitResult();
        return;
    } else {
        mJob = keyGenJob;
    }
    auto progressDialog = new QProgressDialog;
    progressDialog->setAttribute(Qt::WA_DeleteOnClose);
    progressDialog->setModal(true);
    progressDialog->setWindowTitle(i18nc("@title", "Generating an OpenPGP Certificate…"));
    progressDialog->setLabelText(
        i18n("The process of generating an OpenPGP certificate requires large amounts of random numbers. This may require several minutes…"));
    progressDialog->setRange(0, 0);
    connect(progressDialog, &QProgressDialog::canceled, this, [this]() {
        kill();
    });
    connect(mJob, &QGpgME::Job::done, this, [progressDialog]() {
        if (progressDialog) {
            progressDialog->accept();
        }
    });
    progressDialog->show();
}

void OpenPGPCertificateCreationJob::keyGenerated(const GpgME::KeyGenerationResult &result)
{
    mJob = nullptr;
    if (result.error()) {
        setError(GpgError);
        setErrorText(i18n("Could not generate an OpenPGP certificate: %1", Kleo::Formatting::errorAsString(result.error())));
        emitResult();
        return;
    } else if (result.error().isCanceled()) {
        setError(GpgError);
        setErrorText(i18nc("@info:status", "Key generation was cancelled."));
        emitResult();
        return;
    }

    mFingerprint = QLatin1StringView(result.fingerprint());
    emitResult();
}

QString OpenPGPCertificateCreationJob::fingerprint() const
{
    return mFingerprint;
}
#endif
