#include "pch.h"
#include "tunnel.h"
#include <spdlog/spdlog.h>
#include <objbase.h>

#include "globalcfg.h"
#include "httplib.h"
#include "misc.h"
#include "network.h"
#include "protocol.h"
#include "usrerr.h"
#include "user.h"

#include <strsafe.h>

static HANDLE             g_ControlSvrThread = nullptr;
static httplib::Server    g_httpServer;
static USER_SERVER_CONFIG g_UserSvrCfg;

/**
 * @brief 连接到服务端控制服务
 * @param pUserSvrUrl 服务端控制服务 URL 地址 
 */
void ConnectServerControlService(const TCHAR *pUserSvrUrl) {
    InitControlServer(pUserSvrUrl);
}

static void HttpResponseError(httplib::Response &pRes, int errCode, const TCHAR *pErrMessage) {
    ProtocolResponse<ResponseStatus> rsp;
    std::string                      json;

    if (errCode != ERR_SUCCESS) {
        rsp.msgContent.errCode = errCode;
    }

    if (pErrMessage && lstrlen(pErrMessage) > 0) {
        rsp.msgContent.errMessage = pErrMessage;
    } else {
        if (errCode == ERR_SUCCESS) {
            rsp.msgContent.errMessage = TEXT("OK");
        }
    }

    if (aigc::JsonHelper::ObjectToJson(rsp, json)) {
        pRes.set_content(json, TEXT("application/json"));
    } else {
        SPDLOG_ERROR(TEXT("ProtocolResponse<ResponseStatus> to json error"));
    }
}

int CreateControlService(PUSER_SERVER_CONFIG pSvr) {
    static TCHAR           g_CliNetwork[MAX_IP_LEN] = {};
    static WGSERVER_CONFIG g_curCliConfig           = {};
    static std::mutex      g_InterfaceMutex;
    DWORD                  dwStatus = 0;

    // HTTP 服务已经运行
    if (g_httpServer.is_running()) {
        return ERR_SUCCESS;
    }

    // 线程已经运行
    if (g_ControlSvrThread && GetExitCodeThread(g_ControlSvrThread, &dwStatus) && dwStatus == STILL_ACTIVE) {
        return -ERR_ITEM_EXISTS;
    }

    if (pSvr == nullptr) {
        SPDLOG_ERROR(TEXT("Input pSvr params error"));
        return -ERR_INPUT_PARAMS;
    }

    if (pSvr->svrListenPort <= 0 || pSvr->svrListenPort >= 65535) {
        SPDLOG_ERROR(TEXT("Input svrListenPort params error {0}"), pSvr->svrListenPort);
        return -ERR_INPUT_PARAMS;
    }

    if (lstrlen(pSvr->svrPrivateKey) != lstrlen(TEXT("4PPcnW3wYewNpoXjNoY3hQuCnzTNq/E9hhfU9/U6QmY="))) {
        SPDLOG_ERROR(TEXT("Input svrPrivateKey params length error {0}"), pSvr->svrPrivateKey);
        return -ERR_INPUT_PARAMS;
    }

    if (lstrlen(pSvr->svrAddress) == 0) {
        SPDLOG_ERROR(TEXT("Input svrAddress params error {0}"), pSvr->svrAddress);
        return -ERR_INPUT_PARAMS;
    }

    // 保存参数
    memcpy(&g_UserSvrCfg, pSvr, sizeof(USER_SERVER_CONFIG));

    g_httpServer.set_exception_handler([](const auto &req, auto &res, std::exception_ptr ep) {
        const auto fmt = TEXT("<h1>Error 500</h1><p>%s</p>");
        char       buf[BUFSIZ];
        try {
            std::rethrow_exception(ep);
        }
        catch (std::exception &e) {
            StringCbPrintf(buf, BUFSIZ, fmt, e.what());
        }
        catch (...) {    // See the following NOTE
            StringCbPrintf(buf, BUFSIZ, fmt, TEXT("Unknown Exception"));
        }
        res.set_content(buf, TEXT("text/html"));
        res.status = 500;
    });

    g_httpServer.set_error_handler([](const auto &req, auto &res) {
        const auto fmt = TEXT("<p>Error Status: <span style='color:red;'>%d</span></p>");
        char       buf[BUFSIZ];
        StringCbPrintf(buf, BUFSIZ, fmt, res.status);
        res.set_content(buf, TEXT("text/html"));
    });

    g_httpServer.Post(SET_CLIENTHEART_PATH, [](const httplib::Request &req, httplib::Response &res) {
        ProtocolResponse<RspHeartParams> rsp;
        std::string                      json;

        rsp.msgContent.errCode    = ERR_SUCCESS;
        rsp.msgContent.errMessage = TEXT("OK");

        if (aigc::JsonHelper::ObjectToJson(rsp, json)) {
            res.set_content(json, TEXT("application/json"));
        } else {
            SPDLOG_ERROR(TEXT("ProtocolResponse<ResponseStatus> to json error"));
        }
    });

    g_httpServer.Post(SET_CLIENTSTART_TUNNEL, [](const httplib::Request &req, httplib::Response &res) {
        ProtocolRequest<ReqStartTunnelParams> reqData;

        if (aigc::JsonHelper::JsonToObject(reqData, req.body)) {
            int  ret;
            bool isSvrStart = false;

            g_InterfaceMutex.lock();
            // Because of COM return CO_E_FIRST
            CoInitialize(nullptr);

            // 判断先前是否启动过服务
            if ((ret = IsWireGuardServerRunning(GetGlobalCfgInfo()->userCfg.userName, &isSvrStart)) != ERR_SUCCESS) {
                // 返回获取系统服务错误，是否未安装
                HttpResponseError(res, ret, TEXT("Not found WireGuard application in system"));
                SPDLOG_ERROR(TEXT("IsWireGuardServerInstalled error: {0}"), ret);
                g_InterfaceMutex.unlock();
                return;
            }

            // 当前服务状态和需要执行的操作不同
            if (isSvrStart != reqData.msgContent.isStart) {
                if (reqData.msgContent.isStart) {
                    IP_INFO cliInfo;
                    IP_INFO tunnelInfo;
                    int     retry = 3;

                    // 启动服务
                    ret = WireGuardInstallDefaultServerService(true);
                    if (ret != ERR_SUCCESS) {
                        // 返回启动服务失败
                        SPDLOG_ERROR(TEXT("WireGuardInstallDefaultServerService error: {0}"), ret);
                        HttpResponseError(res, ret, TEXT("Start WireGuard Tunnel Service error."));
                        g_InterfaceMutex.unlock();
                        return;
                    }
                    // 添加路由
                    if ((ret = GetIpV4InfoFromCIDR(g_CliNetwork, &cliInfo)) != ERR_SUCCESS) {
                        // 返回启动服务失败
                        SPDLOG_ERROR(TEXT("GetIpV4InfoFromCIDR ({1}) error: {0}"), ret, g_CliNetwork);
                        HttpResponseError(res, ret, TEXT("Parse IpAddress error."));
                        g_InterfaceMutex.unlock();
                        return;
                    }

                    if ((ret = GetIpV4InfoFromCIDR(g_UserSvrCfg.svrAddress, &tunnelInfo)) != ERR_SUCCESS) {
                        // 返回启动服务失败
                        SPDLOG_ERROR(TEXT("GetIpV4InfoFromCIDR ({1}) error: {0}"), ret, g_UserSvrCfg.svrAddress);
                        HttpResponseError(res, ret, TEXT("Parse tunnel ip address error."));
                        g_InterfaceMutex.unlock();
                        return;
                    }

                    do {
                        ret = AddRouteTable(cliInfo.ip, cliInfo.netmask, tunnelInfo.ip);
                        Sleep(1000);
                    } while (ret != ERR_SUCCESS && retry--);

                    if (ret != ERR_SUCCESS) {
                        // 返回启动服务失败
                        SPDLOG_ERROR(TEXT("Add Route {1}/{2} gateway {3} error: {0}"),
                                     ret,
                                     cliInfo.ip,
                                     cliInfo.netmask,
                                     tunnelInfo.ip);
                        HttpResponseError(res, ret, TEXT("Parse tunnel ip address error."));
                        g_InterfaceMutex.unlock();
                        return;
                    }
                } else {
                    if ((ret = WireGuardUnInstallServerService(GetGlobalCfgInfo()->userCfg.userName)) != ERR_SUCCESS) {
                        // 返回停止服务失败
                        HttpResponseError(res, ret, TEXT("Stop pre running WireGuard service error"));
                        SPDLOG_ERROR(TEXT("WireGuardUnInstallServerService error: {0}"), ret);
                        g_InterfaceMutex.unlock();
                        return;
                    }
                }
            }

            if (reqData.msgContent.isStart) {
                SPDLOG_INFO(TEXT("Tunnel Service Start Now ......: {0}"), GetGlobalCfgInfo()->userCfg.userName);
            } else {
                SPDLOG_INFO(TEXT("Tunnel Service Stoped: {0}"), GetGlobalCfgInfo()->userCfg.userName);
            }

            HttpResponseError(res, ERR_SUCCESS, nullptr);
            g_InterfaceMutex.unlock();
        }
    });

    g_httpServer.Post(SET_CLIENTCFG_PATH, [](const httplib::Request &req, httplib::Response &res) {
        ProtocolRequest<ReqUserSetCliCfgParams> reqData;

        if (aigc::JsonHelper::JsonToObject(reqData, req.body)) {
            int                                      ret;
            bool                                     isSvrStart = false;
            ProtocolResponse<RspUserSetCliCfgParams> rsp;
            std::string                              json;

            g_InterfaceMutex.lock();
            // Because of COM return CO_E_FIRST
            CoInitialize(nullptr);

            // 判断先前是否启动过服务
            if ((ret = IsWireGuardServerRunning(GetGlobalCfgInfo()->userCfg.userName, &isSvrStart)) != ERR_SUCCESS) {
                // 返回获取系统服务错误，是否未安装
                HttpResponseError(res, ret, TEXT("Not found WireGuard application in system"));
                SPDLOG_ERROR(TEXT("IsWireGuardServerInstalled error: {0}"), ret);
                g_InterfaceMutex.unlock();
                return;
            }

            if (isSvrStart) {
                SPDLOG_DEBUG(TEXT("WireGuardUnInstallServerService: {0}"), GetGlobalCfgInfo()->userCfg.userName);
                if ((ret = WireGuardUnInstallServerService(GetGlobalCfgInfo()->userCfg.userName)) != ERR_SUCCESS) {
                    // 返回停止服务失败
                    HttpResponseError(res, ret, TEXT("Stop pre running WireGuard service error"));
                    SPDLOG_ERROR(TEXT("WireGuardUnInstallServerService error: {0}"), ret);
                    g_InterfaceMutex.unlock();
                    return;
                }
            }

            memset(&g_curCliConfig, 0, sizeof(WGSERVER_CONFIG));
            g_curCliConfig.ListenPort = g_UserSvrCfg.svrListenPort - 1;
            StringCbCopy(g_curCliConfig.Name, 64, GetGlobalCfgInfo()->userCfg.userName);
            StringCbCopy(g_curCliConfig.Address, 32, g_UserSvrCfg.svrAddress);
            StringCbCopy(g_curCliConfig.PrivateKey, 64, g_UserSvrCfg.svrPrivateKey);
            StringCbCopy(g_curCliConfig.CliPubKey, 64, reqData.msgContent.cliPublicKey.c_str());
            StringCbCopy(g_CliNetwork, MAX_IP_LEN, reqData.msgContent.cliNetwork.c_str());
            StringCbPrintf(g_curCliConfig.AllowNet,
                           256,
                           TEXT("%s,%s"),
                           reqData.msgContent.cliNetwork.c_str(),
                           reqData.msgContent.cliTunnelAddr.c_str());

            // 创建 WireGuard 配置文件
            ret = WireGuardCreateServerConfig(&g_curCliConfig);
            if (ret != ERR_SUCCESS) {
                // 返回写入 WireGuard 配置文件错误
                HttpResponseError(res, ret, TEXT("Create WireGuard service configure file error"));
                SPDLOG_ERROR(TEXT("WireGuardCreateServerConfig error: {0}"), ret);
                g_InterfaceMutex.unlock();
                return;
            }

            // 返回当前隧道信息
            rsp.msgContent.errCode    = ERR_SUCCESS;
            rsp.msgContent.errMessage = TEXT("OK");
            rsp.msgContent.svrNetwork = g_UserSvrCfg.svrAddress;

            if (aigc::JsonHelper::ObjectToJson(rsp, json)) {
                res.set_content(json, TEXT("application/json"));
            } else {
                SPDLOG_ERROR(TEXT("ProtocolResponse<RspUserSetCliCfgParams> to json error"));
                HttpResponseError(res, ERR_JSON_CREATE, TEXT("ProtocolResponse<RspUserSetCliCfgParams> to json error"));
            }
            g_InterfaceMutex.unlock();
        }
    });

    SPDLOG_DEBUG(TEXT("Start HTTP Service at {0}"), pSvr->svrListenPort);
    if (!g_httpServer.bind_to_port(TEXT("0.0.0.0"), pSvr->svrListenPort)) {
        SPDLOG_ERROR(TEXT("Start HTTP Service at {0} error"), pSvr->svrListenPort);
        return -ERR_SOCKET_BIND_PORT;
    }

    g_ControlSvrThread = CreateThread(
        nullptr,    // Thread attributes
        0,          // Stack size (0 = use default)
        [](LPVOID lpParameter) {
            if (!g_httpServer.listen_after_bind()) {
                SPDLOG_ERROR(TEXT("Start HTTP Service at {0} error"));
            }

            SPDLOG_DEBUG(TEXT("Http service exit....."));

            return static_cast<DWORD>(0);
        },           // Thread start address
        nullptr,     // Parameter to pass to the thread
        0,           // Creation flags
        nullptr);    // Thread id

    if (g_ControlSvrThread == nullptr) {
        // Thread creation failed.
        // More details can be retrieved by calling GetLastError()
        return -ERR_CREATE_THREAD;
    }

    g_httpServer.wait_until_ready();

    return ERR_SUCCESS;
}

int StopControlService() {
    if (g_httpServer.is_running()) {
        g_httpServer.stop();
    }

    if (g_ControlSvrThread) {
        // Wait for thread to finish execution
        if (WaitForSingleObject(g_ControlSvrThread, 10 * 1000) == WAIT_TIMEOUT) {
            SPDLOG_ERROR(TEXT("Waitting HTTP Service clost timeout"));
            return -ERROR_TIMEOUT;
        }
        CloseHandle(g_ControlSvrThread);
        g_ControlSvrThread = nullptr;
    }

    return ERR_SUCCESS;
}