/*
  Copyright (C) 2026 Erich Eickmeyer <eeickmeyer@ubuntu.com>

  This program 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 2 of
  the License or (at your option) version 3 or any later version
  accepted by the membership of KDE e.V. (or its successor approved
  by the membership of KDE e.V.), which shall act as a proxy
  defined in Section 14 of version 3 of the license.

  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "l10ninstallgui.h"

#include <QDebug>
#include <QDialog>
#include <QDialogButtonBox>
#include <QLabel>
#include <QListWidget>
#include <QProgressBar>
#include <QPushButton>
#include <QVBoxLayout>

#include <KLocalizedString>
#include <KNotification>

#include <PackageKit/Daemon>
#include <PackageKit/Transaction>

L10nInstallGui::L10nInstallGui(QObject *parent, const QStringList &packageNames)
    : QObject(parent)
    , m_dialog(nullptr)
    , m_currentTransaction(nullptr)
{
    m_dialog = new QDialog();
    m_dialog->setWindowIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-locale")));
    m_dialog->setWindowTitle(i18n("Install Language Support"));
    m_dialog->setMinimumWidth(420);

    auto *layout = new QVBoxLayout(m_dialog);

    m_headerLabel = new QLabel(m_dialog);
    m_headerLabel->setWordWrap(true);
    m_headerLabel->setText(i18n("The following packages are needed to complete language support for your system. "
                                "Select the packages you wish to install:"));
    layout->addWidget(m_headerLabel);

    m_listWidget = new QListWidget(m_dialog);
    connect(m_listWidget, &QListWidget::itemChanged, this, &L10nInstallGui::packageToggled);
    layout->addWidget(m_listWidget);

    for (const QString &pkg : packageNames) {
        auto *item = new QListWidgetItem(pkg);
        item->setData(Qt::UserRole, pkg);
        item->setCheckState(Qt::Checked);
        m_listWidget->addItem(item);
        m_selectedPackages << pkg;
    }

    m_progressBar = new QProgressBar(m_dialog);
    m_progressBar->setRange(0, 100);
    m_progressBar->setValue(0);
    m_progressBar->setVisible(false);
    layout->addWidget(m_progressBar);

    m_statusLabel = new QLabel(m_dialog);
    m_statusLabel->setVisible(false);
    layout->addWidget(m_statusLabel);

    m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel, m_dialog);
    m_installButton = new QPushButton(QIcon::fromTheme(QStringLiteral("download")),
                                      i18n("Install"), m_dialog);
    m_buttonBox->addButton(m_installButton, QDialogButtonBox::AcceptRole);
    connect(m_installButton, &QPushButton::clicked, this, &L10nInstallGui::startInstall);
    connect(m_buttonBox, &QDialogButtonBox::rejected, this, &L10nInstallGui::cleanUpDialog);
    layout->addWidget(m_buttonBox);

    m_dialog->show();
}

L10nInstallGui::~L10nInstallGui()
{
    delete m_dialog;
}

void L10nInstallGui::packageToggled(QListWidgetItem *item)
{
    const QString packageName = item->data(Qt::UserRole).toString();
    if (item->checkState() == Qt::Checked) {
        if (!m_selectedPackages.contains(packageName)) {
            m_selectedPackages << packageName;
        }
    } else {
        m_selectedPackages.removeAll(packageName);
    }
    m_installButton->setEnabled(!m_selectedPackages.isEmpty());
}

void L10nInstallGui::startInstall()
{
    if (m_selectedPackages.isEmpty()) {
        return;
    }

    setUiInstalling(true);
    m_statusLabel->setText(i18n("Resolving packages..."));
    m_resolvedPackageIDs.clear();

    m_currentTransaction = PackageKit::Daemon::resolve(m_selectedPackages,
                                                       PackageKit::Transaction::FilterNotInstalled |
                                                       PackageKit::Transaction::FilterArch);

    connect(m_currentTransaction, &PackageKit::Transaction::package,
            this, &L10nInstallGui::onPackageResolved);
    connect(m_currentTransaction, &PackageKit::Transaction::finished,
            this, &L10nInstallGui::onResolveFinished);
    connect(m_currentTransaction, &PackageKit::Transaction::errorCode,
            this, &L10nInstallGui::onInstallErrorCode);
}

void L10nInstallGui::onPackageResolved(int info, const QString &packageID, const QString &summary)
{
    Q_UNUSED(info);
    Q_UNUSED(summary);
    qDebug() << "Resolved package:" << packageID;
    m_resolvedPackageIDs << packageID;
}

void L10nInstallGui::onResolveFinished(int status, uint runtime)
{
    Q_UNUSED(runtime);

    // The resolve transaction is done — clean it up before anything else.
    if (m_currentTransaction) {
        m_currentTransaction->disconnect(this);
        m_currentTransaction->deleteLater();
        m_currentTransaction = nullptr;
    }

    if (status != PackageKit::Transaction::ExitSuccess) {
        m_statusLabel->setText(i18n("Failed to resolve package names."));
        setUiInstalling(false);
        return;
    }

    if (m_resolvedPackageIDs.isEmpty()) {
        m_statusLabel->setText(i18n("No packages found to install."));
        setUiInstalling(false);
        return;
    }

    m_statusLabel->setText(i18n("Installing packages..."));
    m_progressBar->setValue(0);

    m_currentTransaction = PackageKit::Daemon::installPackages(m_resolvedPackageIDs);
    m_currentTransaction->setHints(QStringLiteral("interactive=true"));

    connect(m_currentTransaction, &PackageKit::Transaction::percentageChanged,
            this, [this]() {
                if (m_currentTransaction)
                    onInstallPercentage(m_currentTransaction->percentage());
            });
    connect(m_currentTransaction, &PackageKit::Transaction::statusChanged,
            this, [this]() {
                if (m_currentTransaction)
                    onInstallStatusChanged(m_currentTransaction->status());
            });
    connect(m_currentTransaction, &PackageKit::Transaction::finished,
            this, &L10nInstallGui::onInstallFinished);
    connect(m_currentTransaction, &PackageKit::Transaction::errorCode,
            this, &L10nInstallGui::onInstallErrorCode);
}

void L10nInstallGui::onInstallPercentage(int percentage)
{
    if (percentage >= 0 && percentage <= 100) {
        m_progressBar->setValue(percentage);
    }
}

void L10nInstallGui::onInstallStatusChanged(int status)
{
    m_statusLabel->setText(transactionStatusText(status));
}

void L10nInstallGui::onInstallFinished(int status, uint runtime)
{
    Q_UNUSED(runtime);

    if (m_currentTransaction) {
        m_currentTransaction->disconnect(this);
        m_currentTransaction->deleteLater();
        m_currentTransaction = nullptr;
    }

    if (status == PackageKit::Transaction::ExitSuccess) {
        m_progressBar->setValue(100);
        m_statusLabel->setText(i18n("Installation complete."));

        KNotification *notify = new KNotification(QStringLiteral("L10n"));
        notify->setComponentName(QStringLiteral("notificationhelper"));
        notify->setIconName(QStringLiteral("preferences-desktop-locale"));
        notify->setText(i18n("Language support packages have been installed successfully. "
                             "You may need to log out and back in for all changes to take effect."));
        notify->sendEvent();
    } else {
        m_statusLabel->setText(i18n("Installation failed."));
    }

    m_installButton->setVisible(false);
    m_buttonBox->setStandardButtons(QDialogButtonBox::Close);
    connect(m_buttonBox, &QDialogButtonBox::rejected, this, &L10nInstallGui::cleanUpDialog);
}

void L10nInstallGui::onInstallErrorCode(int code, const QString &details)
{
    Q_UNUSED(code);
    qWarning() << "PackageKit error:" << details;
    m_statusLabel->setText(i18n("Error: %1", details));
}

void L10nInstallGui::setUiInstalling(bool installing)
{
    m_listWidget->setEnabled(!installing);
    m_installButton->setEnabled(!installing);
    m_progressBar->setVisible(installing);
    m_statusLabel->setVisible(true);
}

QString L10nInstallGui::transactionStatusText(int status) const
{
    switch (status) {
    case PackageKit::Transaction::StatusWait:
    case PackageKit::Transaction::StatusWaitingForLock:
        return i18n("Waiting...");
    case PackageKit::Transaction::StatusSetup:
        return i18n("Setting up...");
    case PackageKit::Transaction::StatusRunning:
        return i18n("Installing...");
    case PackageKit::Transaction::StatusQuery:
        return i18n("Querying...");
    case PackageKit::Transaction::StatusRefreshCache:
        return i18n("Refreshing cache...");
    case PackageKit::Transaction::StatusDownload:
        return i18n("Downloading packages...");
    case PackageKit::Transaction::StatusInstall:
        return i18n("Installing packages...");
    case PackageKit::Transaction::StatusDepResolve:
        return i18n("Resolving dependencies...");
    case PackageKit::Transaction::StatusCommit:
        return i18n("Committing changes...");
    case PackageKit::Transaction::StatusCleanup:
        return i18n("Cleaning up...");
    case PackageKit::Transaction::StatusLoadingCache:
        return i18n("Loading...");
    case PackageKit::Transaction::StatusFinished:
        return i18n("Finished.");
    default:
        return i18n("Working...");
    }
}

void L10nInstallGui::cleanUpDialog()
{
    if (m_dialog) {
        m_dialog->deleteLater();
        m_dialog = nullptr;
    }
}

#include "l10ninstallgui.moc"
