/**
 * Qt6 Common library
 *
 * Copyright (C) 2026 The Qt Company Ltd.
 * SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
 *
 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 * https://openapi-generator.tech
 * Do not edit the class manually.
 */

#include "qoaibaseapi.h"
#include "qoaihttprequest.h"

#include <QtCore/qdir.h>
#include <QtCore/qjsonarray.h>
#include <QtCore/qjsondocument.h>
#include <QtCore/qlist.h>
#include <QtCore/qmap.h>
#include <QtCore/qstring.h>
#include <QtCore/qurl.h>

#include <QtNetwork/qhttpmultipart.h>
#include <QtNetwork/qnetworkaccessmanager.h>
#include <QtNetwork/qnetworkreply.h>
#include <QtNetwork/qrestaccessmanager.h>

QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
QT_END_NAMESPACE

QT_BEGIN_NAMESPACE

namespace QtOpenApiCommon {

class QOAIBaseApiPrivate
{
public:
    QMap<QString, qsizetype> m_serverIndices;
    QMap<QString, QList<QOAIServerConfiguration>> m_serverConfigs;
    QMap<QString, QString> m_apiKeys;
    QMap<QNetworkReply *, QOAIBaseApi::QOAICallerInfo> m_callerData;
    QString m_bearerToken;
    QString m_username;
    QString m_password;
    QString m_workingDirectory;
    // Networking API
    QRestAccessManager *m_restManager = nullptr;
    QNetworkRequestFactory m_networkFactory;
    bool m_isResponseCompressionEnabled = false;
    bool m_isRequestCompressionEnabled = false;
    QOAIFileConflictPolicy m_fileConflictPolicy = QOAIFileConflictPolicy::Rename;
};

QOAIBaseApi::QOAIBaseApi(QObject *parent)
    : QObject(parent),
      d(new QOAIBaseApiPrivate)
{
    d->m_workingDirectory = QDir::currentPath();

    auto manager = new QNetworkAccessManager(this);
    d->m_restManager = new QRestAccessManager(manager, this);
}

QOAIBaseApi::~QOAIBaseApi()
{
}

QList<QString> QOAIBaseApi::operations() const
{
    return d->m_serverConfigs.keys();
}

QList<QOAIServerConfiguration> QOAIBaseApi::serverConfigurations(const QString &operation) const
{
    const auto it = d->m_serverConfigs.find(operation);
    if (it != d->m_serverConfigs.cend())
        return it.value();
    return {};
}

QOAIBaseApi::ServerError
QOAIBaseApi::setServerVariable(const QString &operation, qsizetype serverIndex,
                                     const QString &variable, const QString &value)
{
    auto it = d->m_serverConfigs.find(operation);

    if (it == d->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);
}

QOAIBaseApi::ServerError
QOAIBaseApi::setServer(const QString &operation, qsizetype serverIndex)
{
    const auto it = d->m_serverConfigs.constFind(operation);

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

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

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

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

void QOAIBaseApi::setApiKey(const QString &apiKeyName, const QString &apiKey)
{
    d->m_apiKeys.insert(apiKeyName, apiKey);
}

std::optional<QString> QOAIBaseApi::apiKey(const QString &name) const
{
    const auto it = d->m_apiKeys.find(name);
    if (it != d->m_apiKeys.cend())
        return it.value();
    return std::nullopt;
}

void QOAIBaseApi::setBearerToken(const QString &token)
{
    d->m_bearerToken = token;
}

QString QOAIBaseApi::bearerToken() const
{
    return d->m_bearerToken;
}

void QOAIBaseApi::setUsername(const QString &username)
{
    d->m_username = username;
}

QString QOAIBaseApi::username() const
{
    return d->m_username;
}

void QOAIBaseApi::setPassword(const QString &password)
{
    d->m_password = password;
}

QString QOAIBaseApi::password() const
{
    return d->m_password;
}

void QOAIBaseApi::setTimeOut(std::chrono::milliseconds timeOut)
{
    d->m_networkFactory.setTransferTimeout(timeOut);
}

#if QT_CONFIG(ssl)
void QOAIBaseApi::setSslConfiguration(QSslConfiguration config)
{
    d->m_networkFactory.setSslConfiguration(config);
}

QSslConfiguration QOAIBaseApi::sslConfiguration() const
{
    return d->m_networkFactory.sslConfiguration();
}
#endif // ssl

void QOAIBaseApi::setWorkingDirectory(const QString &path)
{
    d->m_workingDirectory = path;
}

QString QOAIBaseApi::workingDirectory() const
{
    return d->m_workingDirectory;
}

void QOAIBaseApi::setRestAccessManager(QRestAccessManager *ram)
{
    if (ram == d->m_restManager)
        return;
    if (!ram)
        qCritical() << "QRestAccessManager cannot be nullptr.";

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

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

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

    d->m_restManager = ram;
}

QRestAccessManager *QOAIBaseApi::restAccessManager() const
{
    return d->m_restManager;
}

void QOAIBaseApi::setNetworkRequestFactory(QNetworkRequestFactory factory)
{
    d->m_networkFactory = factory;
}

QNetworkRequestFactory QOAIBaseApi::networkRequestFactory() const
{
    return d->m_networkFactory;
}

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

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

void QOAIBaseApi::setRequestCompressionEnabled(bool enabled)
{
    d->m_isRequestCompressionEnabled = enabled;
}

bool QOAIBaseApi::requestCompressionEnabled() const
{
    return d->m_isRequestCompressionEnabled;
}

void QOAIBaseApi::setResponseCompressionEnabled(bool enabled)
{
    d->m_isResponseCompressionEnabled = enabled;
}

bool QOAIBaseApi::responseCompressionEnabled() const
{
    return d->m_isResponseCompressionEnabled;
}

void QOAIBaseApi::setFileConflictPolicy(FileConflictPolicy policy)
{
    d->m_fileConflictPolicy = policy;
}

QOAIBaseApi::FileConflictPolicy QOAIBaseApi::fileConflictPolicy() const
{
    return d->m_fileConflictPolicy;
}

QString QOAIBaseApi::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 *QOAIBaseApi::execute(QOAIHttpRequestInput &input, QNetworkRequest &request, QByteArray &requestContent)
{
    const bool isMultiPartMode = input.hasMultiPart();
    QNetworkReply *reply = nullptr;
    const QString httpMethod = input.httpMethod();
    if (httpMethod == "GET"_L1) {
        reply = d->m_restManager->get(request);
    } else if (httpMethod == "POST"_L1) {
        if (isMultiPartMode)
            reply = d->m_restManager->post(request, input.multiPart());
        else
            reply = d->m_restManager->post(request, requestContent);
    } else if (httpMethod == "PUT"_L1) {
        if (isMultiPartMode)
            reply = d->m_restManager->put(request, input.multiPart());
        else
            reply = d->m_restManager->put(request, requestContent);
    } else if (httpMethod == "HEAD"_L1) {
        reply = d->m_restManager->head(request);
    } else if (httpMethod == "DELETE"_L1) {
        reply = d->m_restManager->deleteResource(request);
    } else {
        if (isMultiPartMode)
            reply = d->m_restManager->sendCustomRequest(request, httpMethod.toLatin1(), input.multiPart());
        else
            reply = d->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;
}

void QOAIBaseApi::setServersForOperation(const QString &operation, const QList<QOAIServerConfiguration> &configs)
{
    Q_ASSERT(!configs.empty()); // This probably means a corrupted yaml file!
    d->m_serverConfigs[operation] = configs;
    d->m_serverIndices[operation] = 0;
}

void QOAIBaseApi::setBaseServerUrl(const QString &operation)
{
    const QUrl serverUrl = d->m_serverConfigs[operation][d->m_serverIndices.value(operation)].url();
    d->m_networkFactory.setBaseUrl(serverUrl);
}

void QOAIBaseApi::setCallerInfo(QNetworkReply *reply, const QOAIBaseApi::QOAICallerInfo &info)
{
    d->m_callerData.insert(reply, info);
}

QOAIBaseApi::QOAICallerInfo QOAIBaseApi::takeCallerInfo(QNetworkReply *reply)
{
    return d->m_callerData.take(reply);
}

} // namespace QtOpenApiCommon

QT_END_NAMESPACE
