/*
 * SPDX-License-Identifier: GPL-3.0-or-later
 * SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
 */
#ifndef ACCOUNTS_ACTIONS_P_H
#define ACCOUNTS_ACTIONS_P_H

#include "account.h"

#include <QDateTime>
#include <QList>
#include <QObject>
#include <QSet>
#include <QSettings>
#include <QString>
#include <QUuid>

#include <functional>
#include <optional>

#include "../secrets/secrets.h"
#include "keys.h"

namespace accounts
{
class AccountJob : public QObject // clazy:exclude=ctor-missing-parent-argument
{
    Q_OBJECT
public:
    explicit AccountJob();
    virtual ~AccountJob();
public Q_SLOTS:
    virtual void run(void);
Q_SIGNALS:
    void finished(void);
};

class Null : public AccountJob // clazy:exclude=ctor-missing-parent-argument
{
    Q_OBJECT
public:
    explicit Null();
    void run(void) override;
};

class RequestAccountPassword : public AccountJob // clazy:exclude=ctor-missing-parent-argument
{
    Q_OBJECT
public:
    explicit RequestAccountPassword(const SettingsProvider &settings, AccountSecret *secret);
    void run(void) override;
private Q_SLOTS:
    void fail(void);
    void unlock(void);
    void finish(void);
Q_SIGNALS:
    void unlocked(void);
    void failed(void);

private:
    const SettingsProvider m_settings;
    AccountSecret *const m_secret;

private:
    bool m_failed;
    bool m_succeeded;
};

class LoadAccounts : public AccountJob // clazy:exclude=ctor-missing-parent-argument
{
    Q_OBJECT
public:
    explicit LoadAccounts(const SettingsProvider &settings,
                          const AccountSecret *secret,
                          const std::function<qint64(void)> &clock = &QDateTime::currentMSecsSinceEpoch);
    void run(void) override;
Q_SIGNALS:
    void foundHotp(const QUuid id,
                   const QString name,
                   const QString issuer,
                   const QByteArray secret,
                   const QByteArray nonce,
                   uint tokenLength,
                   quint64 counter,
                   bool fixedTruncation,
                   uint offset,
                   bool checksum);
    void foundTotp(const QUuid id,
                   const QString name,
                   const QString issuer,
                   const QByteArray secret,
                   const QByteArray nonce,
                   uint tokenLength,
                   uint timeStep,
                   const QDateTime epoch,
                   accounts::Account::Hash hash);
    void failedToLoadAllAccounts(void);

private:
    const SettingsProvider m_settings;
    const AccountSecret *const m_secret;
    const std::function<qint64(void)> m_clock;
};

class DeleteAccounts : public AccountJob // clazy:exclude=ctor-missing-parent-argument
{
    Q_OBJECT
public:
    explicit DeleteAccounts(const SettingsProvider &settings, const QSet<QUuid> &ids);
    void run(void) override;
Q_SIGNALS:
    void invalid(void);

private:
    const SettingsProvider m_settings;
    const QSet<QUuid> m_ids;
};

class SaveHotp : public AccountJob // clazy:exclude=ctor-missing-parent-argument
{
    Q_OBJECT
public:
    explicit SaveHotp(const SettingsProvider &settings,
                      const QUuid id,
                      const QString &accountName,
                      const QString &issuer,
                      const secrets::EncryptedSecret &secret,
                      uint tokenLength,
                      quint64 counter,
                      const std::optional<uint> offset,
                      bool checksum);
    void run(void) override;
Q_SIGNALS:
    void invalid(void);
    void saved(const QUuid id,
               const QString accountName,
               const QString issuer,
               const QByteArray secret,
               const QByteArray nonce,
               uint tokenLength,
               quint64 counter,
               bool fixedTruncation,
               uint offset,
               bool checksum);

private:
    const SettingsProvider m_settings;
    const QUuid m_id;
    const QString m_accountName;
    const QString m_issuer;
    const secrets::EncryptedSecret m_secret;
    const uint m_tokenLength;
    const quint64 m_counter;
    const std::optional<uint> m_offset;
    const bool m_checksum;
};

class SaveTotp : public AccountJob // clazy:exclude=ctor-missing-parent-argument
{
    Q_OBJECT
public:
    explicit SaveTotp(const SettingsProvider &settings,
                      const QUuid id,
                      const QString &accountName,
                      const QString &issuer,
                      const secrets::EncryptedSecret &secret,
                      uint tokenLength,
                      uint timeStep,
                      const QDateTime &epoch,
                      Account::Hash hash,
                      const std::function<qint64(void)> &clock = &QDateTime::currentMSecsSinceEpoch);
    void run(void) override;
Q_SIGNALS:
    void invalid(void);
    void saved(const QUuid id,
               const QString accountName,
               const QString issuer,
               const QByteArray secret,
               const QByteArray nonce,
               uint tokenLength,
               uint timeStep,
               const QDateTime epoch,
               accounts::Account::Hash hash);

private:
    const SettingsProvider m_settings;
    const QUuid m_id;
    const QString m_accountName;
    const QString m_issuer;
    const secrets::EncryptedSecret m_secret;
    const uint m_tokenLength;
    const uint m_timeStep;
    const QDateTime m_epoch;
    const Account::Hash m_hash;
    const std::function<qint64(void)> m_clock;
};

class ComputeTotp : public AccountJob // clazy:exclude=ctor-missing-parent-argument
{
    Q_OBJECT
public:
    explicit ComputeTotp(const AccountSecret *secret,
                         const secrets::EncryptedSecret &tokenSecret,
                         uint tokenLength,
                         const QDateTime &epoch,
                         uint timeStep,
                         const Account::Hash hash,
                         const std::function<qint64(void)> &clock = &QDateTime::currentMSecsSinceEpoch);
    void run(void) override;
Q_SIGNALS:
    void otp(const QString otp, const QString nextOtp, const QDateTime validFrom, const QDateTime validUntil);

private:
    const AccountSecret *const m_secret;
    const secrets::EncryptedSecret m_tokenSecret;
    const uint m_tokenLength;
    const QDateTime m_epoch;
    const uint m_timeStep;
    const Account::Hash m_hash;
    const std::function<qint64(void)> m_clock;
};

class ComputeHotp : public AccountJob // clazy:exclude=ctor-missing-parent-argument
{
    Q_OBJECT
public:
    explicit ComputeHotp(const AccountSecret *secret,
                         const secrets::EncryptedSecret &tokenSecret,
                         uint tokenLength,
                         quint64 counter,
                         const std::optional<uint> offset,
                         bool checksum);
    void run(void) override;
Q_SIGNALS:
    void otp(const QString otp, const QString nextOtp, const quint64 nextCounter);

private:
    const AccountSecret *const m_secret;
    const secrets::EncryptedSecret m_tokenSecret;
    const uint m_tokenLength;
    const quint64 m_counter;
    const std::optional<uint> m_offset;
    const bool m_checksum;
};

class Dispatcher : public QObject
{
    Q_OBJECT
public:
    explicit Dispatcher(QThread *thread, QObject *parent = nullptr);
    bool empty(void) const;
    void queueAndProceed(AccountJob *job, const std::function<void(void)> &setup_callbacks);
Q_SIGNALS:
    void dispatch(void);
private Q_SLOTS:
    void next(void);

private:
    void dispatchNext(void);

private:
    QThread *const m_thread;

private:
    AccountJob *m_current;
    QList<AccountJob *> m_pending;
};
}

#endif
