{{>licenseInfo}}
#include "{{prefixLowerCase}}baseapi.h"
#include "{{prefixLowerCase}}httprequest.h"

#include <QtCore/QDir>
#include <QtCore/qjsonarray.h>
#include <QtCore/qjsondocument.h>
#include <QtNetwork/qhttpmultipart.h>
#include <QtNetwork/qnetworkaccessmanager.h>
#include <QtNetwork/qnetworkreply.h>
#include <QtNetwork/qnetworkrequestfactory.h>
#include <QtNetwork/qrestaccessmanager.h>

QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
QT_END_NAMESPACE

{{#cppCommonNamespaceDeclarations}}
namespace {{this}} {
{{/cppCommonNamespaceDeclarations}}

{{prefix}}BaseApi::{{prefix}}BaseApi(QObject *parent)
    : QObject(parent),
      m_workingDirectory(QDir::currentPath()),
      m_restManager(nullptr),
      m_networkFactory(nullptr),
      m_isResponseCompressionEnabled(false),
      m_isRequestCompressionEnabled(false)
{
    auto manager = new QNetworkAccessManager(this);
    m_restManager = new QRestAccessManager(manager, this);

    if (!m_networkFactory) {
        m_networkFactory = std::make_shared<QNetworkRequestFactory>();
    }
}

{{prefix}}BaseApi::~{{prefix}}BaseApi()
{
}

QList<QString> {{prefix}}BaseApi::operations() const
{
    return m_serverConfigs.keys();
}

QList<{{prefix}}ServerConfiguration> {{prefix}}BaseApi::serverConfigurations(const QString &operation) const
{
    const auto it = m_serverConfigs.find(operation);
    if (it != m_serverConfigs.cend())
        return it.value();
    return {};
}

{{prefix}}BaseApi::ServerError
{{prefix}}BaseApi::setServerVariable(const QString &operation, qsizetype serverIndex,
                                     const QString &variable, const QString &value)
{
    auto it = m_serverConfigs.find(operation);

    if (it == m_serverConfigs.end())
        return ServerError::OperationNotFound;

    auto &configs = it.value();
    if (serverIndex < 0 || serverIndex >= configs.size())
        return ServerError::ServerIndexNotFound;

    return configs[serverIndex].setVariableValue(variable, value);
}

{{prefix}}BaseApi::ServerError
{{prefix}}BaseApi::setServer(const QString &operation, qsizetype serverIndex)
{
    const auto it = m_serverConfigs.constFind(operation);

    if (it == m_serverConfigs.cend())
        return ServerError::OperationNotFound;

    if (serverIndex < 0 || serverIndex >= it.value().size())
        return ServerError::ServerIndexNotFound;

    m_serverIndices[operation] = serverIndex;
    return ServerError::NoError;
}

std::optional<qsizetype> {{prefix}}BaseApi::activeServer(const QString &operation) const
{
    const auto it = m_serverIndices.find(operation);
    if (it == m_serverIndices.cend())
        return std::nullopt;
    return it.value();
}

void {{prefix}}BaseApi::setApiKey(const QString &apiKeyName, const QString &apiKey)
{
    m_apiKeys.insert(apiKeyName, apiKey);
}

void {{prefix}}BaseApi::setBearerToken(const QString &token)
{
    m_bearerToken = token;
}

void {{prefix}}BaseApi::setUsername(const QString &username)
{
    m_username = username;
}

void {{prefix}}BaseApi::setPassword(const QString &password)
{
    m_password = password;
}

void {{prefix}}BaseApi::setTimeOut(std::chrono::milliseconds timeOut)
{
    m_networkFactory->setTransferTimeout(timeOut);
}

#if QT_CONFIG(ssl)
void {{prefix}}BaseApi::setSslConfiguration(QSslConfiguration config)
{
    m_networkFactory->setSslConfiguration(config);
}
#endif // ssl

void {{prefix}}BaseApi::setWorkingDirectory(const QString &path)
{
    m_workingDirectory = path;
}

void {{prefix}}BaseApi::setRestAccessManager(QRestAccessManager *ram)
{
    if (ram == m_restManager)
        return;
    if (!ram)
        qCritical() << "QRestAccessManager cannot be nullptr.";

    // clean-up the objects if we own them
    auto manager = m_restManager->networkAccessManager();

    if (m_restManager->parent() == this)
        delete m_restManager;

    if (manager->parent() == this)
        delete manager;

    m_restManager = ram;
}

void {{prefix}}BaseApi::setNetworkRequestFactory(std::shared_ptr<QNetworkRequestFactory> factory)
{
    if (m_networkFactory == factory)
        return;
    if (!factory)
        qCritical() << "New network factory cannot be null.";
    m_networkFactory.reset();
    m_networkFactory = factory;
}

void {{prefix}}BaseApi::setHeader(QHttpHeaders::WellKnownHeader key, QAnyStringView value)
{
    if (m_networkFactory->commonHeaders().values(key).contains(value))
        return;
    QHttpHeaders headers = m_networkFactory->commonHeaders();
    headers.replaceOrAppend(key, value);
    m_networkFactory->setCommonHeaders(std::move(headers));
}

void {{prefix}}BaseApi::setHeader(QAnyStringView key, QAnyStringView value)
{
    if (m_networkFactory->commonHeaders().values(key).contains(value))
        return;
    QHttpHeaders headers = m_networkFactory->commonHeaders();
    headers.replaceOrAppend(key, value);
    m_networkFactory->setCommonHeaders(std::move(headers));
}

void {{prefix}}BaseApi::enableRequestCompression()
{
    m_isRequestCompressionEnabled = true;
}

void {{prefix}}BaseApi::enableResponseCompression()
{
    m_isResponseCompressionEnabled = true;
}

QString {{prefix}}BaseApi::errorString(ServerError error) const
{
    switch (error) {
    case ServerError::NoError:
        return "No Error"_L1;
    case ServerError::ServerVariableNotFound:
        return "Server Variable Not Found"_L1;
    case ServerError::EnumValueNotFound:
        return "Enum Value Not Found"_L1;
    case ServerError::OperationNotFound:
        return "Operation Not Found"_L1;
    case ServerError::ServerIndexNotFound:
        return "Server Index Not Found"_L1;
    }
    Q_UNREACHABLE_RETURN(QString());
}

QNetworkReply *{{prefix}}BaseApi::execute({{prefix}}HttpRequestInput &input, QNetworkRequest &request, QByteArray &requestContent)
{
    const bool isMultiPartMode = input.hasMultiPart();
    QNetworkReply *reply = nullptr;
    const QString httpMethod = input.httpMethod();
    if (httpMethod == "GET"_L1) {
        reply = m_restManager->get(request);
    } else if (httpMethod == "POST"_L1) {
        if (isMultiPartMode)
            reply = m_restManager->post(request, input.multiPart());
        else
            reply = m_restManager->post(request, requestContent);
    } else if (httpMethod == "PUT"_L1) {
        if (isMultiPartMode)
            reply = m_restManager->put(request, input.multiPart());
        else
            reply = m_restManager->put(request, requestContent);
    } else if (httpMethod == "HEAD"_L1) {
        reply = m_restManager->head(request);
    } else if (httpMethod == "DELETE"_L1) {
        reply = m_restManager->deleteResource(request);
    } else {
        if (isMultiPartMode)
            reply = m_restManager->sendCustomRequest(request, httpMethod.toLatin1(), input.multiPart());
        else
            reply = m_restManager->sendCustomRequest(request, httpMethod.toLatin1(), requestContent);
    }
    // Reparent QHttpMultiPart* to extend its lifetime to the lifetime of the reply
    if (reply && isMultiPartMode) {
        auto multiPart = input.takeMultiPart(); // releases the ownership
        if (multiPart)
            multiPart->setParent(reply);
    }
    return reply;
}

{{#cppCommonNamespaceDeclarations}}
} // namespace {{this}}
{{/cppCommonNamespaceDeclarations}}

