/*
    SPDX-FileCopyrightText: 2014-2015 Romain Bignon <romain@symlink.me>
    SPDX-FileCopyrightText: 2014-2015 Florent Fourcot <weboob@flo.fourcot.fr>
    SPDX-FileCopyrightText 2017 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>

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

#include "mapaccountwizard.h"

#include <memory>

// ----------------------------------------------------------------------------
// QT Includes

#include <QFutureWatcher>
#include <QProgressDialog>
#include <QtConcurrentRun>

// ----------------------------------------------------------------------------
// KDE Includes

#include <KLocalizedString>
#include <KMessageBox>

// ----------------------------------------------------------------------------
// Project Includes

#include "ui_mapaccountwizard.h"

#include "../interface/woobinterface.h"
#include "../woobexc.h"

enum {
    BACKENDS_PAGE = 0,
    ACCOUNTS_PAGE,
};

class MapAccountWizardPrivate
{
    Q_DISABLE_COPY_MOVE(MapAccountWizardPrivate)

public:
    MapAccountWizardPrivate(WoobInterface* woob)
        : ui(new Ui::MapAccountWizard)
        , m_woob(*woob)
    {
    }

    ~MapAccountWizardPrivate()
    {
        delete ui;
    }

    Ui::MapAccountWizard* ui;
    WoobInterface& m_woob;
    QFutureWatcher<QList<WoobInterface::Account>> accountsWatcher;
    QFutureWatcher<QList<WoobInterface::Backend>> backendsWatcher;
    std::unique_ptr<QProgressDialog> progress;
};

MapAccountWizard::MapAccountWizard(QWidget* parent, WoobInterface* woob)
    : QWizard(parent)
    , d_ptr(new MapAccountWizardPrivate(woob))
{
    Q_D(MapAccountWizard);
    d->ui->setupUi(this);
    d->ui->addBackendButton->setVisible(false); // the button isn't connected to anything

    slotCheckNextButton();
    connect(this, &QWizard::currentIdChanged, this, &MapAccountWizard::slotCheckNextButton);
    connect(this, &QWizard::currentIdChanged, this, &MapAccountWizard::slotNewPage);
    connect(d->ui->backendsList, &QTreeWidget::itemSelectionChanged, this, &MapAccountWizard::slotCheckNextButton);
    connect(d->ui->accountsList, &QTreeWidget::itemSelectionChanged, this, &MapAccountWizard::slotCheckNextButton);
    connect(&d->accountsWatcher, &QFutureWatcherBase::finished, this, &MapAccountWizard::slotGotAccounts);
    connect(&d->backendsWatcher, &QFutureWatcherBase::finished, this, &MapAccountWizard::slotGotBackends);
}

/**
 * @internal Destructor stub needed to delete unique_ptrs with type Private
 */
MapAccountWizard::~MapAccountWizard()
{
    Q_D(MapAccountWizard);
    delete d;
}

QString MapAccountWizard::currentBackend() const
{
    Q_D(const MapAccountWizard);
    return d->ui->backendsList->currentItem()->text(0);
}

QString MapAccountWizard::currentAccount() const
{
    Q_D(const MapAccountWizard);
    return d->ui->accountsList->currentItem()->text(0);
}

void MapAccountWizard::slotCheckNextButton(void)
{
    Q_D(MapAccountWizard);
    auto enableButton = false;
    switch (currentId()) {
    case BACKENDS_PAGE:
        enableButton = (d->ui->backendsList->currentItem() != nullptr) && d->ui->backendsList->currentItem()->isSelected();
        button(QWizard::NextButton)->setEnabled(enableButton);
        break;
    case ACCOUNTS_PAGE:
        enableButton = (d->ui->accountsList->currentItem() != nullptr) && d->ui->accountsList->currentItem()->isSelected();
        button(QWizard::FinishButton)->setEnabled(enableButton);
        break;
    }
}

void MapAccountWizard::slotNewPage(int id)
{
    Q_D(MapAccountWizard);
    d->progress = std::make_unique<QProgressDialog>(this);
    d->progress->setModal(true);
    d->progress->setCancelButton(nullptr);
    d->progress->setMinimum(0);
    d->progress->setMaximum(0);
    d->progress->setMinimumDuration(0);

    switch (id) {
    case BACKENDS_PAGE: {
        d->ui->backendsList->clear();
        d->progress->setWindowTitle(i18n("Loading Woob backend..."));
        d->progress->setLabelText(i18n("Getting list of backends."));

        QCoreApplication::processEvents();
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        d->backendsWatcher.setFuture(QtConcurrent::run(&WoobInterface::getBackends, &d->m_woob));
#else
        d->backendsWatcher.setFuture(QtConcurrent::run(&d->m_woob, &WoobInterface::getBackends));
#endif
        break;
    }
    case ACCOUNTS_PAGE: {
        d->ui->accountsList->clear();
        d->progress->setWindowTitle(i18n("Connecting to bank..."));
        d->progress->setLabelText(i18n("Getting list of accounts from your bank."));

        QCoreApplication::processEvents();

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        d->accountsWatcher.setFuture(QtConcurrent::run(&WoobInterface::getAccounts, &d->m_woob, d->ui->backendsList->currentItem()->text(0)));
#else
        d->accountsWatcher.setFuture(QtConcurrent::run(&d->m_woob, &WoobInterface::getAccounts, d->ui->backendsList->currentItem()->text(0)));
#endif
        button(QWizard::BackButton)->setEnabled(false);
        d->ui->accountsList->setEnabled(false);

        break;
    }

    default:
        // I do not know if this can actually happen. But to be safe:
        d->progress.reset();
    }
}

void MapAccountWizard::slotGotBackends()
{
    Q_D(MapAccountWizard);
    const auto backends = d->backendsWatcher.result();
    for (const auto& backend : backends)
        d->ui->backendsList->addTopLevelItem(new QTreeWidgetItem(QStringList{backend.name, backend.module}));
    d->progress.reset();

    if (backends.isEmpty())
        KMessageBox::information(this, i18n("No backends available.\nAdd one using woob config-qt."));
}

void MapAccountWizard::slotGotAccounts()
{
    Q_D(MapAccountWizard);
    try {
        const auto accounts = d->accountsWatcher.result();
        for (const auto& account : accounts)
            d->ui->accountsList->addTopLevelItem(new QTreeWidgetItem(QStringList{account.id, account.name, account.balance.formatMoney(QString(), 2)}));
        d->progress.reset();

        if (accounts.isEmpty())
            KMessageBox::information(this, i18n("No accounts available.\nCheck your backend configuration in woob config-qt."));
        else
            button(QWizard::FinishButton)->setEnabled(true);

    } catch (const WoobException& e) {
        d->progress.reset();
        QString msg;
        switch (e.msg()) {
        case ExceptionCode::BrowserIncorrectPassword:
            msg = i18n("Incorrect password.");
            break;
        default:
            break;
        }
        if (!msg.isEmpty())
            KMessageBox::error(this, msg);
    }

    button(QWizard::BackButton)->setEnabled(true);
    d->ui->accountsList->setEnabled(true);
}
