#include "HttpClient.h"
#include "../usr/config.h"

#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QEventLoop>
#include <QTimer>
#include <QHttpPart>
#include <QHttpMultiPart>
#include <QBuffer>
#include <QJsonDocument>
#include <QJsonObject>
#include <QUrlQuery>
#include <QDebug>
#include <QDateTime>

QString HttpClient::sToken = QString();
const int HttpClient::HTTP_REQUEST_TIMEOUT = 15000;

void HttpClient::clearToken()
{
    sToken.clear();
}

HttpClient::HttpClient(QObject *parent)
    : QThread(parent)
{
    m_pHttpMgr = new QNetworkAccessManager(this);
    m_threadstatus = true;
    m_workStat = httpWorkStat::httpWorkSleep;
}

HttpClient::~HttpClient()
{
    slotSetThreadStop();
    wait();
}

QString HttpClient::formatErrorResponse(int statusCode, const QString& errorInfo)
{
    QString escapedError = errorInfo;
    escapedError.replace("\"", "\\\"").replace("\\", "\\\\");
    return QString("{\"code\":%1,\"result\":\"%2\"}").arg(statusCode).arg(escapedError);
}

QUrl HttpClient::buildNormalizedUrl(const QString& host, const QString& path, const QString& query)
{
    QString fullUrl = QString("http://%1%2").arg(host).arg(path);
    QUrl url(fullUrl);
    url.setPath(url.path());
    if (!query.isEmpty()) {
        url.setQuery(query);
    }
    return url;
}

QString HttpClient::sendRequest(const QNetworkRequest& request, QNetworkAccessManager::Operation op, const QByteArray& data)
{
    QNetworkReply* reply = nullptr;
    switch (op) {
    case QNetworkAccessManager::GetOperation:
        reply = m_pHttpMgr->get(request);
        break;
    case QNetworkAccessManager::PostOperation:
        reply = m_pHttpMgr->post(request, data);
        break;
    case QNetworkAccessManager::PutOperation:
        reply = m_pHttpMgr->put(request, data);
        break;
    default:
        return formatErrorResponse(405, "Unsupported request method");
    }

    QEventLoop eventloop;
    bool isConn = connect(reply, &QNetworkReply::finished, &eventloop, &QEventLoop::quit);
    if (!isConn) {
        reply->deleteLater();
        return formatErrorResponse(500, "Signal slot connect failed");
    }

    QTimer::singleShot(HTTP_REQUEST_TIMEOUT, &eventloop, &QEventLoop::quit);
    eventloop.exec();

    int statusCode = 0;
    QString result;
    if (reply->isFinished()) {
        statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
        if (reply->error() == QNetworkReply::NoError) {
            QByteArray resData = reply->readAll();
            result = QString::fromUtf8(resData);
            qDebug().noquote() << "[HttpClient] 返回状态:" << statusCode << " 长度:" << resData.size() << "字节";
            qDebug().noquote() << "[HttpClient] 返回值:" << result;
        } else {
            result = formatErrorResponse(statusCode, reply->errorString());
            qDebug().noquote() << "[HttpClient] 返回状态:" << statusCode << " 错误:" << reply->errorString();
            qDebug().noquote() << "[HttpClient] 返回值:" << result;
        }
    } else {
        reply->abort();
        statusCode = 408;
        result = formatErrorResponse(408, "Request timeout");
        qDebug().noquote() << "[HttpClient] 返回状态:408(超时) 返回值:" << result;
    }

    reply->deleteLater();
    return result;
}

QString HttpClient::postJsonRequest()
{
    QUrl url = buildNormalizedUrl(Config()->httpHost, m_postUrl);
    QNetworkRequest request(url);
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    request.setRawHeader("Module", "Android_Material");
    request.setRawHeader("Authorization", m_token.toUtf8());
    request.setRawHeader("tenant-id", Config()->tenant_id.toUtf8());

    qDebug().noquote() << "[HTTP POST(Json)] url=" << url.toString()
                       << " tenant_id=" << Config()->tenant_id
                       << " Authorization=" << (m_token.isEmpty() ? "(empty)" : "(set)") << m_token.toUtf8()
                       << " body=" << QString::fromUtf8(m_httpData);

    return sendRequest(request, QNetworkAccessManager::PostOperation, m_httpData);
}

QString HttpClient::postFormdataRequest()
{
    if (m_httpFile.isNull() || m_httpFile.isEmpty()) {
        return formatErrorResponse(400, "Upload file is empty");
    }
    QUrl url = buildNormalizedUrl(Config()->httpHost, m_postUrl);
    QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);

    if (!m_httpData.isNull() && !m_httpData.isEmpty()) {
        QHttpPart jsonPart;
        jsonPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"userName\"");
        jsonPart.setBody(m_httpData);
        multiPart->append(jsonPart);
    }

    QHttpPart filePart;
    filePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/bmp");
    filePart.setHeader(QNetworkRequest::ContentDispositionHeader,
                       QString("form-data; name=\"file\"; filename=\"finger.bmp\""));

    QBuffer* buffer = new QBuffer(&m_httpFile);
    if (!buffer->open(QIODevice::ReadOnly)) {
        QString error = formatErrorResponse(400, "Cannot open file buffer");
        delete multiPart;
        delete buffer;
        return error;
    }
    filePart.setBodyDevice(buffer);
    buffer->setParent(multiPart);
    multiPart->append(filePart);

    QNetworkRequest request(url);
    request.setHeader(QNetworkRequest::ContentTypeHeader,
                      "multipart/form-data; boundary=" + multiPart->boundary());
    request.setRawHeader("Module", "Android_Material");
    request.setRawHeader("Authorization", m_token.toUtf8());
    request.setRawHeader("tenant-id", Config()->tenant_id.toUtf8());

    qDebug().noquote() << "[HTTP POST(FormData)] url=" << url.toString()
                       << " tenant_id=" << Config()->tenant_id
                       << " Authorization=" << (m_token.isEmpty() ? "(empty)" : "(set)")
                       << " fileSize=" << m_httpFile.size() << "bytes";

    QNetworkReply* reply = m_pHttpMgr->post(request, multiPart);
    multiPart->setParent(reply);

    QEventLoop eventloop;
    connect(reply, &QNetworkReply::finished, &eventloop, &QEventLoop::quit);
    QTimer::singleShot(HTTP_REQUEST_TIMEOUT, &eventloop, &QEventLoop::quit);
    eventloop.exec();

    QString result;
    if (reply->isFinished()) {
        if (reply->error() == QNetworkReply::NoError) {
            result = QString::fromUtf8(reply->readAll());
        } else {
            int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
            result = formatErrorResponse(statusCode, reply->errorString());
        }
    } else {
        reply->abort();
        result = formatErrorResponse(408, "Request timeout");
    }

    reply->deleteLater();
    return result;
}

QString HttpClient::getRequest()
{
    QString queryStr;
    if (m_httpData != "{}" && !m_httpData.isEmpty()) {
        QJsonDocument jsonDoc = QJsonDocument::fromJson(m_httpData);
        if (jsonDoc.isObject()) {
            QUrlQuery query;
            QJsonObject jsonObj = jsonDoc.object();
            for (const QString& key : jsonObj.keys()) {
                QJsonValue value = jsonObj.value(key);
                if (value.isString()) query.addQueryItem(key, value.toString());
                else if (value.isDouble()) query.addQueryItem(key, QString::number(value.toDouble()));
                else if (value.isBool()) query.addQueryItem(key, value.toBool() ? "true" : "false");
            }
            queryStr = query.toString(QUrl::FullyEncoded);
        }
    }

    QUrl url = buildNormalizedUrl(Config()->httpHost, m_postUrl, queryStr);
    QNetworkRequest request(url);
    request.setRawHeader("Authorization", m_token.toUtf8());
    request.setRawHeader("tenant-id", Config()->tenant_id.toUtf8());

    qDebug().noquote() << "[HTTP GET] url=" << url.toString()
                       << " tenant_id=" << Config()->tenant_id
                       << " Authorization=" << (m_token.isEmpty() ? "(empty)" : "(set)");

    return sendRequest(request, QNetworkAccessManager::GetOperation);
}

QString HttpClient::putJsonRequest()
{
    QUrl url = buildNormalizedUrl(Config()->httpHost, m_postUrl);
    QNetworkRequest request(url);
    request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    request.setRawHeader("Module", "Android_Material");
    request.setRawHeader("Authorization", m_token.toUtf8());
    request.setRawHeader("tenant-id", Config()->tenant_id.toUtf8());

    qDebug().noquote() << "[HTTP PUT(Json)] url=" << url.toString()
                       << " tenant_id=" << Config()->tenant_id
                       << " Authorization=" << (m_token.isEmpty() ? "(empty)" : "(set)")
                       << " body=" << QString::fromUtf8(m_httpData);

    return sendRequest(request, QNetworkAccessManager::PutOperation, m_httpData);
}

void HttpClient::run()
{
    // 在工作线程内创建 QNAM，保证 post() 创建的 QNetworkReply 与 QNAM 同线程
    m_pHttpMgrWorker = new QNetworkAccessManager();

    // 工作线程必须有事件循环，否则 QNetworkReply::finished 无法派发，API 回调永远不会执行
    QEventLoop loop;
    QTimer pollTimer;
    pollTimer.setInterval(10);
    QObject::connect(&pollTimer, &QTimer::timeout, this, [this, &loop]() {
        if (!m_threadstatus) {
            loop.quit();
            return;
        }
        if (m_workStat != httpWorkStat::httpWorkSleep) {
            QByteArray responseData;
            QString responseStr;

            m_mutex.lock();
            httpWorkStat curWorkStat = m_workStat;
            QString curPostUrl = m_postUrl;
            m_mutex.unlock();

            switch (curWorkStat) {
            case httpWorkStat::httpWorkJson:
                responseStr = postJsonRequest();
                break;
            case httpWorkStat::httpWorkFormdata:
                responseStr = postFormdataRequest();
                break;
            case httpWorkStat::httpWorkGet:
                responseStr = getRequest();
                break;
            default:
                responseStr = formatErrorResponse(400, "Invalid work status");
                break;
            }

            responseData = responseStr.toUtf8();
            // 其他 formdata 的响应由各业务在 slotPostRequestData 里根据 curPostUrl 分发，此处不重复 emit

            m_mutex.lock();
            m_workStat = httpWorkStat::httpWorkSleep;
            m_mutex.unlock();
        }
    }, Qt::DirectConnection);
    pollTimer.start();
    loop.exec();
    pollTimer.stop();

    if (m_pHttpMgrWorker) {
        delete m_pHttpMgrWorker;
        m_pHttpMgrWorker = nullptr;
    }
    qDebug() << "HttpClient thread exit success!";
}

void HttpClient::simulateUpdateBackLock(const QString& postUrl, const QString& jsonBody, const QString& token)
{
    slotPostRequestData(0, postUrl, jsonBody.toUtf8(), QByteArray(), token);
}

void HttpClient::slotPostRequestData(quint64 id, QString postUrl, QByteArray data, QByteArray file, QString token)
{
    m_mutex.lock();
    m_id = id;
    m_postUrl = postUrl;
    m_httpData = data;
    m_httpFile = file;
    m_token = token;
    sToken = token;

    if (!m_httpData.isNull() && !m_httpFile.isNull()) {
        m_workStat = httpWorkStat::httpWorkFormdata;
        m_mutex.unlock();
    } else if (!m_httpFile.isNull() && !m_httpFile.isEmpty()) {
        m_workStat = httpWorkStat::httpWorkFormdata;
        m_mutex.unlock();
    } else if (!m_httpData.isNull()) {
        QString localPostUrl = m_postUrl;
        m_mutex.unlock();

        QString res = postJsonRequest();
        QByteArray bb = res.toUtf8();

        if (localPostUrl == Config()->usernameLogin_url) {
            emit signalResponsePasswordLoginData(bb);
        } else if (localPostUrl == Config()->cardLogin_url) {
            emit signalResponseCardLoginData(bb);
        } else if (localPostUrl == Config()->updateNodeApprovalUrl) {
            emit signalResponseUpdateNodeApprovalUrl(bb);
        } else if (localPostUrl == Config()->uploadJobTicketUrl) {
            emit signalResponseUploadJobTicket(bb);
        } else if (localPostUrl == Config()->uploadPositionInfoUrl) {
            emit signalResponseUploadPositionInfo(bb);
        } else if (localPostUrl == Config()->updateUncolockUrl) {
            emit signalResponseUpdateUncolock(bb);
        } else if (localPostUrl == Config()->updateColockUrl) {
            emit signalResponseUpdateColock(bb);
        } else if (localPostUrl == Config()->updatePointUnlock) {
            emit signalResponseUpdatePointUnlock(bb);
        } else if (localPostUrl == Config()->updateBackLock) {
            emit signalResponseUpdateBackLock(bb);
        } else if (localPostUrl == Config()->updateKeyBack) {
            emit signalResponseUpdateKeyBack(bb);
        } else if (localPostUrl == Config()->updateWorkTicketUrl) {
            emit signalResponseUpdateWorkTicket(bb);
        }
    } else {
        m_mutex.unlock();
    }
}

void HttpClient::slotGetRequestData(quint64 id, QString postUrl, QByteArray data, QString token)
{
    m_mutex.lock();
    m_id = id;
    m_postUrl = postUrl;
    m_httpData = data;
    m_token = token;
    sToken = token;

    QString localPostUrl = m_postUrl;
    m_mutex.unlock();

    QString res = getRequest();
    QByteArray bb = res.toUtf8();

    if (localPostUrl == Config()->jobTicketsUrl) {
        emit signalResponseGetJobTickets(bb);
    } else if (localPostUrl == Config()->workNodeDetail) {
        emit signalResponseGetWorkNodeDetail(bb);
    } else if (localPostUrl == Config()->workNodeDetailForm) {
        emit signalResponseGetFormById(bb);
    } else if (localPostUrl == Config()->userInfoUrl) {
        emit signalResponseGetUserInfoUrl(bb);
    } else if (localPostUrl == Config()->keyMACByNFC) {
        emit signalResponseGetKeyMAC(bb);
    } else if (localPostUrl == Config()->isolationPointById) {
        emit signalResponseGetIsolationPointInfo(bb);
    } else if (localPostUrl == Config()->workTicketByNodeId) {
        emit signalResponseGetWorkTicketByNodeId(bb);
    }
}

void HttpClient::slotPutRequestData(quint64 id, QString postUrl, QByteArray data, QByteArray file, QString token)
{
    m_mutex.lock();
    m_id = id;
    m_postUrl = postUrl;
    m_httpData = data;
    m_httpFile = file;
    m_token = token;
    sToken = token;

    if (!m_httpData.isNull() && !m_httpFile.isNull()) {
        m_workStat = httpWorkStat::httpWorkFormdata;
        m_mutex.unlock();
    } else if (!m_httpData.isNull()) {
        QString localPostUrl = m_postUrl;
        m_mutex.unlock();

        QString res = putJsonRequest();
        QByteArray bb = res.toUtf8();

        if (localPostUrl == Config()->updateUserInfoUrl) {
            emit signalReponseUpdateUserInfoUrl(bb);
        } else if (localPostUrl == Config()->updatePasswordUrl) {
            emit signalReponseUpdateUserPasswordUrl(bb);
        }
    } else {
        m_mutex.unlock();
    }
}

void HttpClient::slotSetThreadStop()
{
    m_mutex.lock();
    m_threadstatus = false;
    m_workStat = httpWorkStat::httpWorkSleep;
    m_mutex.unlock();
}
