#include "httpRequestManager.h"
#include <QJsonObject>
#include <QJsonArray>
#include <QDebug>

HttpRequestManager::HttpRequestManager(QObject* parent)
    : QObject(parent)
{
}
HttpRequestManager::~HttpRequestManager()
{
    for(auto& config : m_endpoints)
    {
        if(config.activeReply)
        {
            config.activeReply->abort();
            config.activeReply->deleteLater();
            config.activeReply = nullptr;
        }
        if(config.timeoutTimer)
        {
            config.timeoutTimer->stop();
            config.timeoutTimer->deleteLater();
            config.timeoutTimer = nullptr;
        }
    }
}

void HttpRequestManager::sendRequest(EndpointConfig& config)
{
    QNetworkRequest request(config.url);

    for(auto it = config.headers.constBegin(); it != config.headers.constEnd(); ++it)
        request.setRawHeader(it.key(), it.value());

    QNetworkReply* reply = nullptr;
    if(config.method == "GET")
        reply = m_networkManager.get(request);
    else if(config.method == "POST")
        reply = m_networkManager.post(request, config.body);
    else if(config.method == "PUT")
        reply = m_networkManager.put(request, config.body);
    else if(config.method == "DELETE")
        reply = m_networkManager.deleteResource(request);
    else
    {
        qWarning() << "Unsupported HTTP method:" << config.method;
        emit requestFailed(config.dataKey, "Unsupported HTTP method");
        return;
    }

    connect(reply, &QNetworkReply::finished, this, &HttpRequestManager::onReplyFinished);
    connect(reply, &QNetworkReply::errorOccurred, this, &HttpRequestManager::onReplyError);

    config.timeoutTimer = new QTimer(this);
    config.timeoutTimer->setSingleShot(true);
    config.timeoutTimer->setInterval(m_defaultTimeout);
    connect(config.timeoutTimer, &QTimer::timeout, this, [this, &config](){
        handleRequestTimeout(config.dataKey);
    });
}

void HttpRequestManager::handleResponse(const QString& dataKey, QNetworkReply* reply)
{
    if(!m_endpoints.contains(dataKey))
    {
        qWarning() << "Recived response for unregistered dataKey:" << dataKey;
        reply->deleteLater();
        return;
    }

    EndpointConfig& config = m_endpoints[dataKey];
    //关闭超时重连定时器
    if(config.timeoutTimer)
    {
        config.timeoutTimer->stop();
        config.timeoutTimer->deleteLater();
        config.timeoutTimer = nullptr;
    }

    if(reply->error() != QNetworkReply::NoError)
        return; //具体错误信息在onReplyError中进行处理

    QByteArray responseData = reply->readAll();
    QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString();
    if(contentType.contains("application/json"))
        parseJsonResponse(dataKey, responseData);
    else if(contentType.contains("text/"))
        parseTextResponse(dataKey, responseData);
    else
        parseBinaryResponse(dataKey, responseData);

    //数据重置
    reply->deleteLater();
    config.retryCount = 0;
    config.activeReply = nullptr;
}

void HttpRequestManager::handleRequestTimeout(const QString& dataKey)
{
    if(!m_endpoints.contains(dataKey))
        return;

    EndpointConfig& config = m_endpoints[dataKey];
    qWarning() << "Request timeout for dataKey: " << dataKey;
    if(config.activeReply)
        config.activeReply->abort();
    //重试
    if(config.retryCount < config.maxRetries)
        scheduleRetry(config.dataKey);
    else
    {
        emit requestFailed(config.dataKey, "Request timed out");
        config.retryCount = 0;
    }
}

void HttpRequestManager::scheduleRetry(const QString& dataKey)
{
    if(!m_endpoints.contains(dataKey))
        return;

    EndpointConfig& config = m_endpoints[dataKey];
    config.retryCount++;

    QTimer::singleShot(config.retryInterval, this, [this, dataKey](){
        requestData(dataKey);
    });
}

void HttpRequestManager::parseJsonResponse(const QString& dataKey, const QByteArray& data)
{
    QJsonParseError parseError;
    QJsonDocument document = QJsonDocument::fromJson(data, &parseError);
    if(document.isNull())
    {
        emit requestFailed(dataKey, "get JSonDocument failed");
        return;
    }
    if(parseError.error != QJsonParseError::NoError)
    {
        emit requestFailed(dataKey, "JSON parse error: " + parseError.errorString());
        return;
    }

    if(document.isObject())
        emit dataReceived(dataKey, document.object().toVariantMap());
    else if(document.isArray())
        emit dataReceived(dataKey, document.array().toVariantList());
    else
        emit dataReceived(dataKey, QVariant());
}

void HttpRequestManager::parseTextResponse(const QString& dataKey, const QByteArray& data)
{
    emit dataReceived(dataKey, QString::fromUtf8(data));
}

void HttpRequestManager::parseBinaryResponse(const QString& dataKey, const QByteArray& data)
{
    emit dataReceived(dataKey, data);
}

void HttpRequestManager::registerEndpoint(const QString& dataKey, const QUrl& url, const QByteArray& method,
                      const QByteArray& body, const QMap<QByteArray, QByteArray>& headers)
{
    EndpointConfig config;
    config.dataKey = dataKey;
    config.url = url;
    config.method = method.toUpper();
    config.body = body;
    config.headers = headers;
    config.maxRetries = m_maxRetries;
    config.retryInterval = m_retryIntrval;

    m_endpoints[dataKey] = config;
}

void HttpRequestManager::requestData(const QString& dataKey)
{
    if(!m_endpoints.contains(dataKey))
    {
        qWarning() << "This dataKey is not present in endpoints: " << dataKey;
        emit requestFailed(dataKey, "dataKey is not present in endpoints");
        return;
    }

    EndpointConfig& config = m_endpoints[dataKey];
    if(config.activeReply)
    {
        config.activeReply->abort();
        config.activeReply->deleteLater();
        config.activeReply = nullptr;
    }
    if(config.timeoutTimer)
    {
        config.timeoutTimer->stop();
        config.timeoutTimer->deleteLater();
        config.timeoutTimer = nullptr;
    }

    sendRequest(config);
}

void HttpRequestManager::cancleRequest(const QString& dataKey)
{
    if(!m_endpoints.contains(dataKey))
        return;

    EndpointConfig& config = m_endpoints[dataKey];
    if(config.activeReply)
    {
        config.activeReply->abort();
        config.activeReply->deleteLater();
        config.activeReply = nullptr;
    }
    if(config.timeoutTimer)
    {
        config.timeoutTimer->stop();
        config.timeoutTimer->deleteLater();
        config.timeoutTimer = nullptr;
    }
    config.retryCount = 0;
}

void HttpRequestManager::setDefaultTimeout(int ms)
{
    m_defaultTimeout = qMax(1000, ms);
}

void HttpRequestManager::setRetryPolicy(int maxRetries, int retryInterval)
{
    m_maxRetries = qMax(0, maxRetries);
    m_retryIntrval = qMax(500, retryInterval);
    //更新所以已注册端点的充实策略
    for(auto& config : m_endpoints)
    {
        config.maxRetries = m_maxRetries;
        config.retryInterval = m_retryIntrval;
    }
}

bool HttpRequestManager::hasVaildEndpoint(const QString& dataKey)
{
    return m_endpoints.contains(dataKey) && m_endpoints[dataKey].url.isEmpty();
}

void HttpRequestManager::onReplyFinished()
{
    QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
    if(!reply)
        return;

    QString dataKey;
    for(auto& config : m_endpoints)
    {
        if(config.activeReply == reply)
        {
            dataKey = config.dataKey;
            break;
        }
    }

    if(dataKey.isEmpty())
    {
        reply->deleteLater();
        return;
    }

    handleResponse(dataKey, reply);
}

void HttpRequestManager::onReplyError(QNetworkReply::NetworkError code)
{
    Q_UNUSED(code);

    QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
    if(!reply)
        return;

    QString dataKey;
    for(auto& config : m_endpoints)
    {
        if(config.activeReply == reply)
        {
            dataKey = config.dataKey;
            break;
        }
    }

    if(dataKey.isEmpty())
        return;

    EndpointConfig& config = m_endpoints[dataKey];
    //关闭超时重连定时器
    if(config.timeoutTimer)
    {
        config.timeoutTimer->stop();
        config.timeoutTimer->deleteLater();
        config.timeoutTimer = nullptr;
    }

    //打印错误
    QString error = reply->errorString();
    qWarning() << "HTTP request failed for " << dataKey << ":" << error;

    //重试
    if(config.retryCount < config.maxRetries)
        scheduleRetry(dataKey);
    else
    {
        emit requestFailed(dataKey, error);
        config.retryCount = 0;
    }

    reply->deleteLater();
    config.activeReply = nullptr;
}
