#include "httpserver.h"
#include "logger.h"
#include <fstream>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QDir>
#include <QCryptographicHash>
#include <QMessageAuthenticationCode>
#include "appcontext.h"

#define FILE_SERVER_VERSION     "1.0.0"
#define FILE_SERVER_PASSWD      "HGwP42vt65AgQyJa"
#define FILE_SERVER_SECRIT_KEY  "7R1mo2T7XJTrSgSJHBzRZYSydWybDD46"

HttpServer::HttpServer(QObject *parent)
    : QObject{parent}
{
    moveToThread(&m_thread);
    connect(&m_thread, &QThread::started, this, &HttpServer::run_hhttp_sever);
    run("0.0.0.0", 18000);
}

HttpServer::~HttpServer()
{
    stop();
}

std::string HttpServer::addr() const
{
    return m_addr;
}

uint16_t HttpServer::port() const
{
    return m_port;
}

bool HttpServer::isRunning() const
{
    return m_thread.isRunning();
}

void HttpServer::run(const char *addr, unsigned short port)
{
    m_addr = std::string(addr);
    m_port = port;
    LOG_DEBUG("file server start...");
    m_thread.start();
}

void HttpServer::stop()
{
    m_server.stop();
    m_thread.quit();
    m_thread.wait();
}

void HttpServer::run_hhttp_sever()
{

    // 心跳
    m_server.Get("/heartbeat", [&](const httplib::Request &req, httplib::Response &res) {
        QJsonDocument doc;
        QJsonObject obj = {
            {"message", QString("Connected")}
        };
        doc.setObject(obj);
        
        res.status = httplib::StatusCode::OK_200;
        res.set_content(doc.toJson(QJsonDocument::Compact).data(), "application/json");
    });

    // 插件库信息
    m_server.Get("/plugininfo", [&](const httplib::Request &req, httplib::Response &res) {
        PluginManager &pluginMgr = AppContext::instance().pluginManager();
        QJsonObject info = pluginMgr.pluginInfo();
        QJsonDocument doc;
        doc.setObject(info);
        res.status = httplib::StatusCode::OK_200;
        res.set_content(doc.toJson(QJsonDocument::Compact).data(), "application/json");
    });

    m_server.Post("/plugininfo/delete", [&](const httplib::Request &req, httplib::Response &res) {
        if(req.body.empty()) {
            res.status = httplib::StatusCode::BadRequest_400;
            res.set_content(R"({"message": "Request error"})", "application/json");
            return;
        }
        PluginManager &pluginMgr = AppContext::instance().pluginManager();
        QString jsonStr = QString::fromStdString(req.body);
        QJsonParseError error;
        QString pluginName;
        QJsonDocument doc = QJsonDocument::fromJson(jsonStr.toUtf8(), &error);
        if(error.error == QJsonParseError::NoError && doc.isObject()) {
            QJsonObject obj = doc.object();
            if(obj.contains("name")) {
                pluginName = obj["name"].toString();

                if(pluginMgr.delPlugin(pluginName)) {
                    QJsonObject reply = {
                        {"message", QString("Plugin delete successfully")}
                    };
                    doc.setObject(reply);
                    res.status = httplib::StatusCode::OK_200;
                    res.set_content(doc.toJson(QJsonDocument::Compact).data(), "application/json");
                    return;
                } else {
                    QJsonObject reply = {
                        {"message", QString("Plugin delete failed")}
                    };
                    doc.setObject(reply);
                    res.status = httplib::StatusCode::InternalServerError_500;
                    res.set_content(doc.toJson(QJsonDocument::Compact).data(), "application/json");
                    return;
                }
            }
        } 

        res.status = httplib::StatusCode::BadRequest_400;
        res.set_content(R"({"message": "Invalid json"})", "application/json");
    });

    m_server.Post("/plugininfo/update", [&](const httplib::Request &req, httplib::Response &res) {
        if(req.body.empty()) {
            res.status = httplib::StatusCode::BadRequest_400;
            res.set_content(R"({"message": "Request error"})", "application/json");
            return;
        }
        PluginManager &pluginMgr = AppContext::instance().pluginManager();
        QString jsonStr = QString::fromStdString(req.body);
        QJsonParseError error;
        QString pluginName;
        QJsonDocument doc = QJsonDocument::fromJson(jsonStr.toUtf8(), &error);
        if(error.error == QJsonParseError::NoError && doc.isObject()) {
            QJsonObject obj = doc.object();
            if(obj.contains("name") && obj.contains("enabled")) {
                pluginName = obj["name"].toString();

                if(pluginMgr.updatePluginInfo(pluginName, obj["enabled"].toBool())) {
                    emit reloadCanvas();
                    QJsonObject reply = {
                        {"message", QString("Plugin update successfully")}
                    };
                    doc.setObject(reply);
                    res.status = httplib::StatusCode::OK_200;
                    res.set_content(doc.toJson(QJsonDocument::Compact).data(), "application/json");
                    return;
                } else {
                    QJsonObject reply = {
                        {"message", QString("Plugin update failed")}
                    };
                    doc.setObject(reply);
                    res.status = httplib::StatusCode::InternalServerError_500;
                    res.set_content(doc.toJson(QJsonDocument::Compact).data(), "application/json");
                    return;
                }
            }
        } 

        res.status = httplib::StatusCode::BadRequest_400;
        res.set_content(R"({"message": "Invalid json"})", "application/json");
    });

    // 画面配置文件更新接口
    m_server.Post("/", [&](const httplib::Request &req, httplib::Response &res, const httplib::ContentReader &content_reader) {

        // std::cout << "Headers:\n";
        // for (const auto &header : req.headers) {
        //     std::cout << header.first << ": " << header.second << "\n";
        // }

        QJsonDocument doc;

        // 检查请求头中是否存在 Access-Token
        auto it = req.headers.find("Access-Token");
        if (it != req.headers.end() && !it->second.empty()) {
            QString token = QString::fromStdString(it->second);
            auto x = token.split(".");
            QString h = x.first();
            QString s = x.last();

            auto header = QJsonDocument::fromJson(QByteArray::fromBase64(h.toUtf8())).array();
            auto timestamp = header[0].toString();
            auto type = header[2].toString();

            QJsonArray signature;
            signature.push_back(timestamp);
            signature.push_back(FILE_SERVER_VERSION);
            signature.push_back(type);
            signature.push_back(FILE_SERVER_PASSWD);
            doc.setArray(signature);

            QByteArray byteArray;
            byteArray.push_back(h.toUtf8());
            byteArray.push_back(".");
            byteArray.push_back(QMessageAuthenticationCode::hash(QString(doc.toJson(QJsonDocument::Compact)).toUtf8(), FILE_SERVER_SECRIT_KEY, QCryptographicHash::Sha256).toBase64());
            QString sign = QString(byteArray);
            LOG_DEBUG("Access-Token: {} {}", token.toStdString(), sign.toStdString());

            if(sign == token) {
                if (req.is_multipart_form_data()) {
                    // NOTE: `content_reader` is blocking until every form data field is read
                    // This approach allows streaming processing of large files
                    std::vector<httplib::FormData> items;
                    content_reader(
                        [&](const httplib::FormData &item) {
                        items.push_back(item);
                        return true;
                        },
                        [&](const char *data, size_t data_length) {
                        items.back().content.append(data, data_length);
                        return true;
                    });

                    // Process the received items
                    for (const auto& item : items) {
                        if (item.filename.empty()) {
                            // Text field
                            QJsonObject obj = {
                                {"message", QString("File error")}
                            };
                            doc.setObject(obj);
                            
                            res.status = httplib::StatusCode::BadRequest_400;
                            res.set_content(doc.toJson(QJsonDocument::Compact).data(), "application/json");
                        } else {
                            // File
                            QString currentPath = QDir::currentPath();
                            QString savePath;
                            QString filename;

                            if(type == "monitor") {
                                savePath = QString("%1/%2").arg(currentPath, "storage/upload/monitor");
                                filename = QString("%1/%2").arg(savePath, "monitor.xml");
                            } else if(type == "netparse") {
                                savePath = QString("%1/%2").arg(currentPath, "storage/upload/netparse");
                                // filename = QString("%1/%2").arg(savePath, QString::fromStdString(item.filename));
                                filename = QString("%1/%2").arg(savePath, "netparse.xml");
                            } else if(type == "formula") {
                                savePath = QString("%1/%2").arg(currentPath, "storage/upload/formula");
                                // filename = QString("%1/%2").arg(savePath, QString::fromStdString(item.filename));
                                filename = QString("%1/%2").arg(savePath, "formula.json");
                            } else if(type == "pluginadd" || type == "pluginupgrade") {
                                savePath = QString("%1/%2").arg(currentPath, "storage/upload/plugins");
                                filename = QString("%1/%2").arg(savePath, QString::fromStdString(item.filename));
                            } else {
                                res.status = httplib::StatusCode::BadRequest_400;
                                res.set_content(R"({"message": "Invalid file type"})", "application/json");
                                return;
                            }

                            QDir dir(savePath);
                            if(!dir.exists()) {
                                dir.mkpath(savePath);
                            }

                            std::ofstream ofs(filename.toStdString(), std::ios::binary);
                            if (!ofs) {
                                LOG_ERROR("open file failed: {}", filename.toStdString());
                                continue;
                            }

                            ofs.write(item.content.data(), item.content.size());
                            ofs.close();

                            LOG_INFO("upload filename: {}", filename.toStdString());
                            
                            if(type == "monitor") {
                                LOG_INFO("monitor configure file successfully update");
                                emit monitorConfigureUpdate(filename);
                            } else if(type == "formula") {
                                LOG_INFO("formula configure file successfully update");
                                emit formulaConfigureUpdate(filename);
                            } else if(type == "netparse") {
                                LOG_INFO("netparse configure file successfully update");
                                emit netparseConfigureUpdate(filename);
                            } else if(type == "pluginadd") {
                                LOG_INFO("add plugin: {}", item.filename);
                                PluginManager &pluginMgr = AppContext::instance().pluginManager();
                                bool ret = pluginMgr.addPlugin(filename);
                                LOG_INFO("add plugin: {}", ret);
                            } else if(type == "pluginupgrade") {
                                auto pluginName = header[3].toString();
                                LOG_INFO("upgrade plugin: {}, {}", pluginName.toStdString(), item.filename);
                                PluginManager &pluginMgr = AppContext::instance().pluginManager();
                                bool ret = pluginMgr.upgradePlugin(pluginName, filename);
                                LOG_INFO("upgrade plugin: {}", ret);
                            }

                            QJsonObject obj = {
                                {"message", QString("'File successfully upload")}
                            };
                            doc.setObject(obj);
                            
                            res.status = httplib::StatusCode::OK_200;
                            res.set_content(doc.toJson(QJsonDocument::Compact).data(), "application/json");
                        }
                    }
                } else {
                    // std::string body;
                    // content_reader([&](const char *data, size_t data_length) {
                    //     body.append(data, data_length);
                    //     return true;
                    // });

                    QJsonObject obj = {
                        {"message", QString("File error")}
                    };
                    doc.setObject(obj);
                    
                    res.status = httplib::StatusCode::BadRequest_400;
                    res.set_content(doc.toJson(QJsonDocument::Compact).data(), "application/json");
                }
            } else {
                res.status = httplib::StatusCode::BadRequest_400; // error token
                res.set_content(R"({"message": "Invalid access token"})", "application/json");
            }
        } else {
            LOG_DEBUG("No Access-Token found");
            res.status = httplib::StatusCode::Unauthorized_401; // 未授权
            res.set_content(R"({"message": "Access token missing"})", "application/json");
        }

        // return;


    });

    m_server.listen(m_addr, m_port);
}