// SPDX-FileCopyrightText: 2019 Linus Jahn <lnj@kaidan.im>
//
// SPDX-License-Identifier: LGPL-2.1-or-later

#ifndef QXMPPUPLOADREQUESTMANAGER_H
#define QXMPPUPLOADREQUESTMANAGER_H

#include "QXmppClientExtension.h"
#include "QXmppError.h"

#include <variant>

#include <QSharedDataPointer>

class QFileInfo;
template<typename T>
class QXmppTask;
class QMimeType;
class QXmppHttpUploadRequestIq;
class QXmppHttpUploadSlotIq;
class QXmppUploadServicePrivate;
class QXmppUploadRequestManagerPrivate;

/*!
    \inmodule QXmpp

    \brief QXmppUploadService represents an HTTP File Upload service.

    It is used to store the JID and maximum file size for uploads.
*/
class QXMPP_EXPORT QXmppUploadService
{
public:
    QXmppUploadService();
    QXmppUploadService(const QXmppUploadService &);
    ~QXmppUploadService();

    QXmppUploadService &operator=(const QXmppUploadService &);

    QString jid() const;
    void setJid(const QString &jid);

    qint64 sizeLimit() const;
    void setSizeLimit(qint64 sizeLimit);

private:
    QSharedDataPointer<QXmppUploadServicePrivate> d;
};

/*!
    \inmodule QXmpp

    \brief The QXmppUploadRequestManager implements the core of \xep{0363}{HTTP File Upload}.

    It handles the discovery of QXmppUploadServices and can send upload
    requests and outputs the upload slots. It doesn't do the actual upload via.
    HTTP.

    To make use of this manager, you need to instantiate it and load it into
    the QXmppClient instance as follows:

    \code
    auto *manager = new QXmppUploadRequestManager;
    client->addExtension(manager);
    \endcode

    Apart from that, you also need to discover HTTP File Upload service(s) by
    requesting the Service Discovery info for each Service Discovery item of
    the server. The QXmppUploadManager will then automatically recognize upload
    services and add them to the list of discovered services
    \c uploadServices().

    Keep in mind that theoretically any XMPP entity could promote to be an
    upload service and so is recognized by this manager. A potential attacker
    could exploit this vulnerability, so the client could be uploading files to
    the attacker (e.g. a normal user JID) instead of the own server.

    As soon as at least one upload service has been discovered, you can start
    to request upload slots by using \c requestUploadSlot(). Alternatively you
    can provide the JID of the upload service which should be used for
    uploading.

    \since QXmpp 1.1

    \ingroup Managers
*/
class QXMPP_EXPORT QXmppUploadRequestManager : public QXmppClientExtension
{
    Q_OBJECT

public:
    QXmppUploadRequestManager();
    ~QXmppUploadRequestManager();

    QString requestUploadSlot(const QFileInfo &file,
                              const QString &uploadService = QString());
    QString requestUploadSlot(const QFileInfo &file,
                              const QString &customFileName,
                              const QString &uploadService = QString());
    QString requestUploadSlot(const QString &fileName,
                              qint64 fileSize,
                              const QMimeType &mimeType,
                              const QString &uploadService = QString());

    using SlotResult = std::variant<QXmppHttpUploadSlotIq, QXmppError>;
    auto requestSlot(const QFileInfo &file, const QString &uploadService = {}) -> QXmppTask<SlotResult>;
    auto requestSlot(const QFileInfo &file, const QString &customFileName, const QString &uploadService = {}) -> QXmppTask<SlotResult>;
    auto requestSlot(const QString &fileName, qint64 fileSize, const QMimeType &mimeType, const QString &uploadService = {}) -> QXmppTask<SlotResult>;

    bool serviceFound() const;

    QVector<QXmppUploadService> uploadServices() const;

    bool handleStanza(const QDomElement &stanza) override;

    /*! Emitted when an upload \a slot was received. */
    Q_SIGNAL void slotReceived(const QXmppHttpUploadSlotIq &slot);

    /*!
        Emitted when the slot request failed.

        \a request is the sent IQ with a QXmppStanza::Error from the server.
    */
    Q_SIGNAL void requestFailed(const QXmppHttpUploadRequestIq &request);

    /*! Emitted when the first upload service has been found. */
    Q_SIGNAL void serviceFoundChanged();

protected:
    void onRegistered(QXmppClient *client) override;
    void onUnregistered(QXmppClient *client) override;

private:
    void handleDiscoInfo(const QXmppDiscoveryIq &iq);

    const std::unique_ptr<QXmppUploadRequestManagerPrivate> d;
};

#endif  // QXMPPUPLOADREQUESTMANAGER_H
