#include "networkaccessmanager.h"
#include "appevent.h"

#include <QBuffer>
#include <QCoreApplication>
#include <QEventLoop>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QMetaObject>
#include <QPromise>
#include <QUrlQuery>
#include <QtConcurrent>

// 基础URL配置
static QString base_url("http://106.55.186.74:8200");

// static QString base_url("http://127.0.0.1:8200");

static const QString messageVal = "msg";

static NetworkAccessManager* namInstance = nullptr;

void cleanupNetworkAccessManager()
{
    delete namInstance;
    namInstance = nullptr;
}

NetworkAccessManager* NetworkAccessManager::instance()
{
    if (!namInstance) {
        namInstance = new NetworkAccessManager;
        qAddPostRoutine(cleanupNetworkAccessManager);
    }
    return namInstance;
}

NetworkAccessManager::NetworkAccessManager(QObject* parent)
    : QNetworkAccessManager(parent)
{
    // #ifdef QT_DEBUG
    //     connect(this, &QNetworkAccessManager::finished, this, [](QNetworkReply* reply) {
    //         const auto data = reply->readAll();
    //         reply->seek(0);
    //         const auto startTimeValue = reply->property("startTime");
    //         const QDateTime startTime = startTimeValue.value<QDateTime>();
    //         const QDateTime endTime = QDateTime::currentDateTime();
    //         qDebug() << QString("[%1] time:[%2]ms --> %3")
    //                         .arg(endTime.toString("yyyy-MM-dd hh:mm:ss.zzz"))
    //                         .arg((endTime.msecsTo(startTime)))
    //                         .arg(QString::fromUtf8(data));
    //     });
    // #endif
}

QNetworkReply* NetworkAccessManager::createRequest(Operation op,
                                                   const QNetworkRequest& request,
                                                   QIODevice* outgoingData)
{
    QNetworkRequest req(request);
    // 记录请求开始时间
    QDateTime startTime = QDateTime::currentDateTime();

    // 获取操作类型字符串
    QString opStr;
    switch (op) {
    case QNetworkAccessManager::GetOperation:
        opStr = "GET";
        break;
    case QNetworkAccessManager::PostOperation:
        opStr = "POST";
        break;
    case QNetworkAccessManager::PutOperation:
        opStr = "PUT";
        break;
    case QNetworkAccessManager::DeleteOperation:
        opStr = "DELETE";
        break;
    case QNetworkAccessManager::HeadOperation:
        opStr = "HEAD";
        break;
    default:
        opStr = "UNKNOWN";
        break;
    }
    qDebug() << QString("[%1] --> %2 %3")
                    .arg(startTime.toString("yyyy-MM-dd hh:mm:ss.zzz"))
                    .arg(opStr)
                    .arg(request.url().toString());
    // 默认的请求处理
    QString agentStr = QString::fromLatin1("%1/%2 (QNetworkAccessManager %3; %4; %5; %6 bit)")
                           .arg(QCoreApplication::applicationName(),
                                QCoreApplication::applicationVersion(),
                                QLatin1String(qVersion()),
                                QSysInfo::prettyProductName(),
                                QLocale::system().name())
                           .arg(QSysInfo::WordSize);
    req.setRawHeader("User-Agent", agentStr.toLatin1());

    const QString token = AppEvent::instance()->jwtToken();
    if (!token.isEmpty()) {
        req.setRawHeader("Authorization", "Bearer " + token.toUtf8());
    }

    req.setRawHeader("Machine-Code", AppEvent::instance()->machineCode().toUtf8());
    req.setRawHeader("Accept-Language", AppEvent::instance()->locale().toUtf8());

    const QUrl& url = request.url();
    const QString urlPath = url.path();

    if (!urlPath.contains("/api/auth/refresh-token") && !urlPath.contains("/api/auth/login")) {
        if (AppEvent::instance()->isRefreshToken()) {
            emit AppEvent::instance()->refreshTokenNeeded();
        }
    }

    QNetworkReply* reply
        = QNetworkAccessManager::createRequest(op,
                                               req,
                                               outgoingData); // 存储开始时间，用于计算请求耗时
    reply->setProperty("startTime", startTime);
    return reply;
}

namespace TC {
RequestClient::RequestClient(const RequestClientOptions& options)
    : m_baseUrl(options.baseURL)
    , m_timeout(options.timeout)
    , m_networkManager(NetworkAccessManager::instance())
{
    if (options.enableDefaultInterceptors) {
        setupDefaultInterceptors();
    }
}

RequestClient::Ptr RequestClient::create(const QString& baseURL, const RequestClientOptions& options)
{
    RequestClientOptions opts = options;
    opts.baseURL = baseURL;
    return Ptr(new RequestClient(opts));
}

RequestClient::Ptr RequestClient::globalInstance()
{
    static RequestClient::Ptr instance = create(base_url, {"", true});
    return instance;
}

RequestClient::Ptr RequestClient::baseGlobalInstance()
{
    static RequestClient::Ptr instance = create(base_url, {"", false});
    return instance;
}

void RequestClient::setupDefaultInterceptors()
{
    QMutexLocker locker(&m_interceptorMutex);

    // 添加认证拦截器
    m_interceptors.push_back(std::make_shared<AuthRequestInterceptor>());

    // 添加错误处理拦截器
    m_interceptors.push_back(std::make_shared<ErrorResponseInterceptor>());
}

void RequestClient::addInterceptor(std::shared_ptr<Interceptor> interceptor)
{
    QMutexLocker locker(&m_interceptorMutex);
    if (!m_interceptors.contains(interceptor)) {
        m_interceptors.append(interceptor);
    }
}

void RequestClient::removeInterceptor(std::shared_ptr<Interceptor> interceptor)
{
    QMutexLocker locker(&m_interceptorMutex);
    m_interceptors.removeOne(interceptor);
}

void RequestClient::clearInterceptors()
{
    QMutexLocker locker(&m_interceptorMutex);
    m_interceptors.clear();
}

HttpResponse RequestClient::get(const QString& url, const QVariantMap& params)
{
    QScopedPointer<QNetworkReply> reply(sendGetRequest(url, params));

    QEventLoop loop;
    QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();

    return parseReplyData(reply.get());
}

HttpResponse RequestClient::post(const QString& url, const QJsonDocument& data)
{
    QScopedPointer<QNetworkReply> reply(sendPostRequest(url, data));

    QEventLoop loop;
    QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();

    return parseReplyData(reply.get());
}

HttpResponse RequestClient::put(const QString& url, const QJsonDocument& data)
{
    QScopedPointer<QNetworkReply> reply(sendPutRequest(url, data));

    QEventLoop loop;
    QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();

    return parseReplyData(reply.get());
}

HttpResponse RequestClient::deleteResource(const QString& url)
{
    QScopedPointer<QNetworkReply> reply(sendDeleteRequest(url));

    QEventLoop loop;
    QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();

    return parseReplyData(reply.get());
}

HttpResponse RequestClient::upload(const QString& url, QHttpMultiPart* multiPart)
{
    QScopedPointer<QNetworkReply> reply(sendUploadRequest(url, multiPart));

    QEventLoop loop;
    QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();

    return parseReplyData(reply.get());
}

QFuture<HttpResponse> RequestClient::getAsync(const QString& url, const QVariantMap& params)
{
    auto promise = std::make_shared<QPromise<HttpResponse>>();
    auto future = promise->future();
    
    QMetaObject::invokeMethod(QCoreApplication::instance(), [this, url, params, promise]() {
        try {
            auto result = get(url, params);
            promise->addResult(result);
            promise->finish();
        } catch (...) {
            promise->finish();
        }
    }, Qt::QueuedConnection);
    
    return future;
}

QFuture<HttpResponse> RequestClient::postAsync(const QString& url, const QJsonDocument& data)
{
    auto promise = std::make_shared<QPromise<HttpResponse>>();
    auto future = promise->future();
    
    QMetaObject::invokeMethod(QCoreApplication::instance(), [this, url, data, promise]() {
        try {
            auto result = post(url, data);
            promise->addResult(result);
            promise->finish();
        } catch (...) {
            promise->finish();
        }
    }, Qt::QueuedConnection);
    
    return future;
}

QFuture<HttpResponse> RequestClient::putAsync(const QString& url, const QJsonDocument& data)
{
    auto promise = std::make_shared<QPromise<HttpResponse>>();
    auto future = promise->future();
    
    QMetaObject::invokeMethod(QCoreApplication::instance(), [this, url, data, promise]() {
        try {
            auto result = put(url, data);
            promise->addResult(result);
            promise->finish();
        } catch (...) {
            promise->finish();
        }
    }, Qt::QueuedConnection);
    
    return future;
}

QFuture<HttpResponse> RequestClient::deleteAsync(const QString& url)
{
    auto promise = std::make_shared<QPromise<HttpResponse>>();
    auto future = promise->future();
    
    QMetaObject::invokeMethod(QCoreApplication::instance(), [this, url, promise]() {
        try {
            auto result = deleteResource(url);
            promise->addResult(result);
            promise->finish();
        } catch (...) {
            promise->finish();
        }
    }, Qt::QueuedConnection);
    
    return future;
}

QFuture<HttpResponse> RequestClient::uploadAsync(const QString& url, QHttpMultiPart* multiPart)
{
    auto promise = std::make_shared<QPromise<HttpResponse>>();
    auto future = promise->future();
    
    QMetaObject::invokeMethod(QCoreApplication::instance(), [this, url, multiPart, promise]() {
        try {
            auto result = upload(url, multiPart);
            promise->addResult(result);
            promise->finish();
        } catch (...) {
            promise->finish();
        }
    }, Qt::QueuedConnection);
    
    return future;
}

bool RequestClient::download(const QString& url, const QString& saveFilePath)
{
    QScopedPointer<QNetworkReply> reply(sendGetRequest(url, {}));

    QFile file(saveFilePath);
    if (!file.open(QIODevice::WriteOnly)) {
        return false;
    }

    QEventLoop loop;
    QObject::connect(reply.get(), &QNetworkReply::readyRead, [&]() {
        file.write(reply->readAll());
    });

    QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();

    file.close();
    return reply->error() == QNetworkReply::NoError;
}

QFuture<bool> RequestClient::downloadAsync(const QString& url, const QString& saveFilePath)
{
    auto promise = std::make_shared<QPromise<bool>>();
    auto future = promise->future();
    
    QMetaObject::invokeMethod(QCoreApplication::instance(), [this, url, saveFilePath, promise]() {
        try {
            auto result = download(url, saveFilePath);
            promise->addResult(result);
            promise->finish();
        } catch (...) {
            promise->finish();
        }
    }, Qt::QueuedConnection);
    
    return future;
}

QNetworkReply* RequestClient::sendGetRequest(const QString& url, const QVariantMap& params)
{
    m_networkManager->setTransferTimeout(m_timeout);

    QString base = m_baseUrl.isEmpty() ? base_url : m_baseUrl;
    QUrl fullUrl(base + url);

    if (!params.isEmpty()) {
        QUrlQuery query;
        for (auto it = params.constBegin(); it != params.constEnd(); ++it) {
            query.addQueryItem(it.key(), it.value().toString());
        }
        fullUrl.setQuery(query);
    }

    QNetworkRequest request(fullUrl);
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    processRequest(request);

    emit requestStarted(fullUrl);
    return m_networkManager->get(request);
}

QNetworkReply* RequestClient::sendPostRequest(const QString& url, const QJsonDocument& data)
{
    m_networkManager->setTransferTimeout(m_timeout);

    QString base = m_baseUrl.isEmpty() ? base_url : m_baseUrl;
    QUrl fullUrl(base + url);

    QBuffer* buffer = new QBuffer;
    buffer->setData(data.toJson());
    buffer->open(QIODevice::ReadOnly);

    QNetworkRequest request(fullUrl);
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    processRequest(request);

    emit requestStarted(fullUrl);
    QNetworkReply* reply = m_networkManager->post(request, buffer);
    buffer->setParent(reply);

    return reply;
}

QNetworkReply* RequestClient::sendPutRequest(const QString& url, const QJsonDocument& data)
{
    m_networkManager->setTransferTimeout(m_timeout);

    QString base = m_baseUrl.isEmpty() ? base_url : m_baseUrl;
    QUrl fullUrl(base + url);

    QBuffer* buffer = new QBuffer;
    buffer->setData(data.toJson());
    buffer->open(QIODevice::ReadOnly);

    QNetworkRequest request(fullUrl);
    processRequest(request);

    emit requestStarted(fullUrl);
    QNetworkReply* reply = m_networkManager->put(request, buffer);
    buffer->setParent(reply);

    return reply;
}

QNetworkReply* RequestClient::sendDeleteRequest(const QString& url)
{
    m_networkManager->setTransferTimeout(m_timeout);

    QString base = m_baseUrl.isEmpty() ? base_url : m_baseUrl;
    QUrl fullUrl(base + url);

    QNetworkRequest request(fullUrl);
    processRequest(request);

    emit requestStarted(fullUrl);
    return m_networkManager->deleteResource(request);
}

QNetworkReply* RequestClient::sendUploadRequest(const QString& url, QHttpMultiPart* multiPart)
{
    m_networkManager->setTransferTimeout(m_timeout);

    QString base = m_baseUrl.isEmpty() ? base_url : m_baseUrl;
    QUrl fullUrl(base + url);

    QNetworkRequest request(fullUrl);
    processRequest(request);

    emit requestStarted(fullUrl);
    return m_networkManager->post(request, multiPart);
}

HttpResponse RequestClient::parseReplyData(QNetworkReply* reply)
{
    HttpResponse response;

    if (reply->error() != QNetworkReply::NoError) {
        response.code = reply->error();
        response.message = reply->errorString();
        emit requestFailed(reply->url(), response.message);
        return response;
    }

    const QByteArray responseData = readReplyData(reply);
    response.rawData = responseData;

    QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData);
    if (jsonDoc.isObject()) {
        const QJsonObject jsonObj = jsonDoc.object();
        if (jsonObj.contains("code")) {
            response.code = jsonObj["code"].toInt();
        }
        if (jsonObj.contains(messageVal)) {
            response.message = jsonObj[messageVal].toString();
        }
        if (jsonObj.contains("data")) {
            response.data = jsonObj["data"];
        }
    }

    emit requestFinished(reply->url(), response);
    return response;
}

void RequestClient::processRequest(QNetworkRequest& request)
{
    QMutexLocker locker(&m_interceptorMutex);
    for (auto& interceptor : m_interceptors) {
        interceptor->interceptRequest(request);
    }
}

void RequestClient::processResponse(QNetworkReply* reply)
{
    QMutexLocker locker(&m_interceptorMutex);
    for (auto& interceptor : m_interceptors) {
        interceptor->interceptResponse(reply);
    }
}

QByteArray RequestClient::readReplyData(QNetworkReply* reply)
{
    if (reply->property("cachedData").isValid()) {
        return reply->property("cachedData").toByteArray();
    }

    QByteArray data = reply->readAll();
    cacheReplyData(reply, data);
    return data;
}

void RequestClient::cacheReplyData(QNetworkReply* reply, const QByteArray& data)
{
    reply->setProperty("cachedData", data);
}

// 拦截器实现
void AuthRequestInterceptor::interceptRequest(QNetworkRequest& request)
{
    const QString token = AppEvent::instance()->jwtToken();
    if (!token.isEmpty()) {
        request.setRawHeader("Authorization", "Bearer " + token.toUtf8());
    }

    request.setRawHeader("Machine-Code", AppEvent::instance()->machineCode().toUtf8());
    request.setRawHeader("Accept-Language", AppEvent::instance()->locale().toUtf8());
}

void AuthRequestInterceptor::interceptResponse(QNetworkReply* reply)
{
    if (reply->error() == QNetworkReply::AuthenticationRequiredError) {
        // 触发token刷新
        emit AppEvent::instance()->refreshTokenNeeded();

        if (AppEvent::instance()->isEnableRefreshToken()) {
            // 刷新token逻辑
            auto client = RequestClient::baseGlobalInstance();
            QJsonObject emptyData;
            QJsonDocument jsonDoc(emptyData);

            HttpResponse response = client->post("/api/auth/refresh-token", jsonDoc);

            if (response.code == 0) {
                QString newToken = response.data.toString();
                AppEvent::instance()->setJwtToken(newToken);
            } else {
                // 刷新失败，需要重新登录
                AppEvent::instance()->setJwtToken("");
                emit AppEvent::instance()->loginExpired(true);
            }
        } else {
            // 直接重新认证
            AppEvent::instance()->setJwtToken("");
            emit AppEvent::instance()->loginExpired(true);
        }
    }
}

void ErrorResponseInterceptor::interceptResponse(QNetworkReply* reply)
{
    if (reply->error() != QNetworkReply::NoError) {
        QByteArray responseData = reply->property("cachedData").toByteArray();
        QString errorMessage;

        if (!responseData.isEmpty()) {
            QJsonDocument jsonResponse = QJsonDocument::fromJson(responseData);
            if (jsonResponse.isObject()) {
                QJsonObject jsonObject = jsonResponse.object();
                if (jsonObject.contains("error")) {
                    errorMessage = jsonObject["error"].toString();
                } else if (jsonObject.contains(messageVal)) {
                    errorMessage = jsonObject[messageVal].toString();
                }
            }
        }

        if (errorMessage.isEmpty()) {
            errorMessage = reply->errorString();
        }

        emit AppEvent::instance()->errorMessage(errorMessage);
    }
}
} // namespace TC
