// SPDX-FileCopyrightText: 2010 Manjeet Dahiya <manjeetdahiya@gmail.com>
// SPDX-FileCopyrightText: 2021 Linus Jahn <lnj@kaidan.im>
//
// SPDX-License-Identifier: LGPL-2.1-or-later

#ifndef QXMPPDISCOVERYMANAGER_H
#define QXMPPDISCOVERYMANAGER_H

#include "QXmppClientExtension.h"
#include "QXmppDiscoveryIq.h"

#include <memory>
#include <variant>

#include <QDateTime>
#include <QProperty>

template<typename T>
class QXmppTask;
class QXmppDataForm;
class QXmppDiscoveryIq;
class QXmppDiscoveryManagerPrivate;
struct QXmppError;

/*!
    \inmodule QXmpp

    \brief Lightweight handle to a service discovery watch.

    Returned by QXmppDiscoveryManager::discoverServices(). Provides reactive access
    to discovered server services matching a filter. Cheap to copy — all copies
    share the same underlying state. When the last copy goes out of scope, the
    watch is automatically unregistered from the discovery manager.

    \since QXmpp 1.16
*/
class QXMPP_EXPORT QXmppDiscoServicesWatch
{
public:
    QBindable<bool> loaded() const;
    QBindable<QList<QXmppDiscoService>> services() const;

private:
    friend class QXmppDiscoveryManager;
    friend class QXmppDiscoveryManagerPrivate;
    QXmppDiscoServicesWatch() = default;

    struct Data {
        QProperty<bool> loaded { false };
        QProperty<QList<QXmppDiscoService>> services;
    };
    std::shared_ptr<Data> d;
};

/*!
    \inmodule QXmpp

    \brief The QXmppDiscoveryManager class makes it possible to discover information
    about XMPP entities as defined by \xep{0030}{Service Discovery}.

    Since QXmpp 1.12 info and items queries are cached per session by default.

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

public:
    /*!
        Policies for how cached service discovery information is used.

        \value Strict Always ensure the data is up-to-date. Cached data may be used only if it is guaranteed to be current (e.g. via entity capabilities).
        \value Relaxed Cached data may be used even if it is not guaranteed to be current, within the configured limits.
    */
    enum class CachePolicy {
        Strict,
        Relaxed,
    };

    QXmppDiscoveryManager();
    ~QXmppDiscoveryManager() override;

    QXmppTask<QXmpp::Result<QXmppDiscoInfo>> info(const QString &jid, const QString &node = {}, CachePolicy fetchPolicy = CachePolicy::Relaxed);
    QXmppTask<QXmpp::Result<QList<QXmppDiscoItem>>> items(const QString &jid, const QString &node = {}, CachePolicy fetchPolicy = CachePolicy::Relaxed);

    QXmppDiscoServicesWatch discoverServices(QXmpp::Disco::Category category, std::optional<QXmpp::Disco::Type> type = {}, QStringList requiredFeatures = {});
    QXmppDiscoServicesWatch discoverServices(QString category, std::optional<QString> type = {}, QStringList requiredFeatures = {});

    const QList<QXmppDiscoIdentity> &identities() const;
    void setIdentities(const QList<QXmppDiscoIdentity> &identities);

    const QList<QXmppDataForm> &infoForms() const;
    void setInfoForms(const QList<QXmppDataForm> &dataForms);

    QString clientCapabilitiesNode() const;
    void setClientCapabilitiesNode(const QString &);

    QXmppDiscoInfo buildClientInfo() const;

    QStringList discoveryFeatures() const override;
    bool handleStanza(const QDomElement &element) override;

#if QXMPP_DEPRECATED_SINCE(1, 13)
    /*!
        This signal is emitted when an information response is received.
        \deprecated
    */
    [[deprecated("Use info()")]]
    Q_SIGNAL void infoReceived(const QXmppDiscoveryIq &);

    /*!
        This signal is emitted when an items response is received.
        \deprecated
    */
    [[deprecated("Use items()")]]
    Q_SIGNAL void itemsReceived(const QXmppDiscoveryIq &);
#endif

#if QXMPP_DEPRECATED_SINCE(1, 12)
    QT_WARNING_PUSH
    QT_WARNING_DISABLE_DEPRECATED
    using InfoResult = std::variant<QXmppDiscoveryIq, QXmppError>;
    using ItemsResult = std::variant<QList<QXmppDiscoveryIq::Item>, QXmppError>;
    [[deprecated("Use info()")]]
    QXmppTask<InfoResult> requestDiscoInfo(const QString &jid, const QString &node = {});
    [[deprecated("Use items()")]]
    QXmppTask<ItemsResult> requestDiscoItems(const QString &jid, const QString &node = {});
    QT_WARNING_POP

    [[deprecated("Use buildClientInfo()")]]
    QXmppDiscoveryIq capabilities();

    [[deprecated("Use identities()")]]
    QString clientCategory() const;
    [[deprecated("Use setIdentities()")]]
    void setClientCategory(const QString &);

    [[deprecated("Use identities()")]]
    void setClientName(const QString &);
    [[deprecated("Use setIdentities()")]]
    QString clientApplicationName() const;

    [[deprecated("Use identities()")]]
    QString clientType() const;
    [[deprecated("Use setIdentities()")]]
    void setClientType(const QString &);

    [[deprecated("Use infoForms()")]]
    QXmppDataForm clientInfoForm() const;
    [[deprecated("Use setInfoForms()()")]]
    void setClientInfoForm(const QXmppDataForm &form);

    [[deprecated("Use info()")]]
    QString requestInfo(const QString &jid, const QString &node = QString());
    [[deprecated("Use items()")]]
    QString requestItems(const QString &jid, const QString &node = QString());
#endif

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

private:
    friend class QXmppDiscoveryManagerPrivate;
    const std::unique_ptr<QXmppDiscoveryManagerPrivate> d;
};

#endif  // QXMPPDISCOVERYMANAGER_H
