#include "pch.h"
#include "tunnel.h"
#include "usrerr.h"
#include <strsafe.h>
#include <tchar.h>
#include <shlwapi.h>

#include "globalcfg.h"
#include "misc.h"
#include "network.h"

#pragma comment(lib, "Shlwapi.lib")
#pragma comment(lib, "Winmm.lib")

static NET_SHARE_MODE g_CurShareMode = ICS_SHARE_MODE;

NET_SHARE_MODE GetCurrentNetShareMode() {
    return g_CurShareMode;
}

void SetCurrentNetShareMode(NET_SHARE_MODE shareMode) {
    g_CurShareMode = shareMode;
}

int GetWireGuardWorkMode(bool *pIsWorkServer) {
    if (pIsWorkServer == nullptr) {
        SPDLOG_ERROR(TEXT("Input pIsWorkServer params error"));
        return -ERR_INPUT_PARAMS;
    }

    *pIsWorkServer = GetGlobalCfgInfo()->isWorkServer;

    return ERR_SUCCESS;
}

int WireGuardInstallDefaultServerService(bool bInstall) {
    TCHAR cfgVal[MAX_PATH];

    GetPrivateProfileString(CFG_WIREGUARD_SECTION,
                            CFG_WGCFG_PATH,
                            TEXT(""),
                            cfgVal,
                            MAX_PATH,
                            GetGlobalCfgInfo()->cfgPath);

    if (lstrlen(cfgVal) > 0) {
        if (PathFileExists(cfgVal)) {
            int   ret;
            TCHAR svrName[MAX_PATH];

            StringCbCopy(svrName, MAX_PATH, cfgVal);
            PathStripPath(svrName);
            PathRemoveExtension(svrName);

            if (bInstall) {
                ret = WireGuardInstallServerService(cfgVal);    //CreateWireGuardService(svrName, cfgVal);
            } else {
                ret = RemoveGuardService(svrName, true);
            }

            if (bInstall && ret == ERR_SUCCESS) {
                int retry = 10;
                do {
                    ret = WaitNetAdapterConnected(svrName, 1000);
                } while (ret != ERR_SUCCESS && retry--);
            }

            return ret;
        } else {
            SPDLOG_ERROR(TEXT("WireGuard configure file [{0}] not found"), cfgVal);
            return -ERR_FILE_NOT_EXISTS;
        }
    } else {
        SPDLOG_ERROR(TEXT("Configure [{0}] = {1} not found"), CFG_WGCFG_PATH, cfgVal);
        return -ERR_ITEM_UNEXISTS;
    }
}

int WireGuardInstallServerService(const TCHAR *pTunnelCfgPath) {
    // 卸载服务
    TCHAR svrName[MAX_PATH];
    int   ret;

    StringCbCopy(svrName, MAX_PATH, pTunnelCfgPath);
    PathStripPath(svrName);
    PathRemoveExtension(svrName);

    if (pTunnelCfgPath == nullptr || lstrlen(pTunnelCfgPath) == 0) {
        SPDLOG_ERROR(TEXT("Input pTunnelCfgPath params error"));
        return -ERR_INPUT_PARAMS;
    }
    if (!PathFileExists(pTunnelCfgPath)) {
        SPDLOG_ERROR(TEXT("WireGuard configure file {0} unexists."), pTunnelCfgPath);
        return -ERR_ITEM_UNEXISTS;
    }

    if ((ret = CreateWireGuardService(svrName, pTunnelCfgPath)) != ERR_SUCCESS) {
        SPDLOG_ERROR(TEXT("Create WireGuard Service Error({0}): {1}, {2} "), ret, svrName, pTunnelCfgPath);
        return ret;
    }

    return ERR_SUCCESS;
}

int WireGuardUnInstallServerService(const TCHAR *pTunnelName) {
    // 卸载服务
    int ret;

    if (pTunnelName == nullptr || lstrlen(pTunnelName) == 0) {
        SPDLOG_ERROR(TEXT("Input pTunnelName params error"));
        return -ERR_INPUT_PARAMS;
    }

    if ((ret = RemoveGuardService(pTunnelName, true)) != ERR_SUCCESS) {
        SPDLOG_ERROR(TEXT("Stop WireGuard Service Error: {0}"), ret);
        return ret;
    }

    return ERR_SUCCESS;
}

int IsWireGuardServerInstalled(bool *pIsInstalled) {
    DWORD dwStatus;
    int   ret;

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

    *pIsInstalled = false;

    ret = GetWindowsServiceStatus(TEXT("WireGuard"), &dwStatus);

    if (ret == ERR_SUCCESS) {
        switch (dwStatus) {
            case SERVICE_CONTINUE_PENDING:
            case SERVICE_RUNNING:
            case SERVICE_START_PENDING:
                *pIsInstalled = true;
                break;
            default:
                *pIsInstalled = false;
                break;
        }
    }

    return ret;
}

int IsWireGuardServerRunning(const TCHAR *pIfName, bool *pIsRunning) {
    return GetWireGuardServiceStatus(pIfName, pIsRunning);
}

int WireGuardCreateClientConfig(const PWGCLIENT_CONFIG pWgConfig) {
    const size_t bufSize     = 4096 * sizeof(TCHAR);
    const TCHAR  cfgFormat[] = TEXT(
        "[Interface]\nPrivateKey = %s\nAddress = %s\n\n[Peer]\nPublicKey = %s\nAllowedIPs = %s\nEndpoint = "
         "%s\nPersistentKeepalive = 30\n");
    TCHAR  cfgPath[MAX_PATH];
    size_t length;
    HANDLE hFile;
    TCHAR *pBuf;

#pragma region
    if (pWgConfig == nullptr) {
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbLength(pWgConfig->Name, 64, &length)) || 0 == length) {
        SPDLOG_ERROR(TEXT("WireGuard Name error: {0}"), pWgConfig->Name);
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbLength(pWgConfig->Address, 32, &length)) || 0 == length) {
        SPDLOG_ERROR(TEXT("WireGuard Address error: {0}"), pWgConfig->Address);
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbLength(pWgConfig->PrivateKey, 64, &length)) || 0 == length) {
        SPDLOG_ERROR(TEXT("WireGuard Private key error: {0}"), pWgConfig->PrivateKey);
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbLength(pWgConfig->SvrPubKey, 64, &length)) || 0 == length) {
        SPDLOG_ERROR(TEXT("WireGuard Server Public key error: {0}"), pWgConfig->SvrPubKey);
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbLength(pWgConfig->AllowNet, 256, &length)) || 0 == length) {
        SPDLOG_ERROR(TEXT("WireGuard Allow Client Network error: {0}"), pWgConfig->AllowNet);
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbLength(pWgConfig->ServerURL, 256, &length)) || 0 == length) {
        SPDLOG_ERROR(TEXT("WireGuard Server Network error: {0}"), pWgConfig->ServerURL);
        return -ERR_INPUT_PARAMS;
    }
#pragma endregion 参数检查

    pBuf = static_cast<TCHAR *>(malloc(bufSize));

    if (pBuf == nullptr) {
        SPDLOG_ERROR(TEXT("Malloc {1} bytes memory error: {0}"), GetLastError(), bufSize);
        return -ERR_MALLOC_MEMORY;
    }

    memset(pBuf, 0, bufSize);

    StringCbPrintf(cfgPath,
                   MAX_PATH,
                   "%s\\%s",
                   GetGlobalCfgInfo()->configDirectory,
                   GetGlobalCfgInfo()->userCfg.userName);

    // 如果当前用户配置目录不存在则自动创建
    if (!PathFileExists(cfgPath)) {
        if (!CreateDirectory(cfgPath, nullptr)) {
            SPDLOG_ERROR(TEXT("Create configure directory '{0}' error."), cfgPath);
            return -ERR_CREATE_FILE;
        }
    }

    StringCbPrintf(cfgPath,
                   MAX_PATH,
                   "%s\\%s\\%s.conf",
                   GetGlobalCfgInfo()->configDirectory,
                   GetGlobalCfgInfo()->userCfg.userName,
                   pWgConfig->Name);

    hFile = CreateFile(cfgPath,                         // name of the write
                       GENERIC_WRITE | GENERIC_READ,    // open for writing
                       FILE_SHARE_READ,                 // do not share
                       nullptr,                         // default security
                       CREATE_ALWAYS,                   // create new file only
                       FILE_ATTRIBUTE_NORMAL,           // normal file
                       nullptr);                        // no attr. template

    if (hFile == INVALID_HANDLE_VALUE) {
        SPDLOG_ERROR(TEXT("CreatFile [{0}] error: {1}"), cfgPath, GetLastError());
        free(pBuf);
        return -ERR_OPEN_FILE;
    }

    // 保存到配置文件中
    WritePrivateProfileString(CFG_WIREGUARD_SECTION, CFG_WGCFG_PATH, cfgPath, GetGlobalCfgInfo()->cfgPath);

    if (FAILED(StringCbPrintf(pBuf,
                              bufSize,
                              cfgFormat,
                              pWgConfig->PrivateKey,
                              pWgConfig->Address,
                              pWgConfig->SvrPubKey,
                              pWgConfig->AllowNet,
                              pWgConfig->ServerURL))) {
        SPDLOG_ERROR(TEXT("Format string error: {0}"), GetLastError());
        free(pBuf);
        ::CloseHandle(hFile);
        return -ERR_MEMORY_STR;
    }

    if (FAILED(StringCbLength(pBuf, bufSize, &length))) {
        SPDLOG_ERROR(TEXT("Get string \'{0}\' length error: {1}"), pBuf, GetLastError());
        free(pBuf);
        ::CloseHandle(hFile);
        return -ERR_MEMORY_STR;
    }

    SPDLOG_DEBUG(TEXT("WG Client Configure:\n{0}"), pBuf);

    if (!WriteFile(hFile,                         // open file handle
                   pBuf,                          // start of data to write
                   static_cast<DWORD>(length),    // number of bytes to write
                   nullptr,                       // number of bytes that were written
                   nullptr)) {
        SPDLOG_ERROR(TEXT("WriteFile [{0}] error: {1}"), cfgPath, GetLastError());
        free(pBuf);
        ::CloseHandle(hFile);
        return -ERR_OPEN_FILE;
    }

    StringCbCopy(GetGlobalCfgInfo()->wgClientCfg.wgName, 260, pWgConfig->Name);
    StringCbCopy(GetGlobalCfgInfo()->wgClientCfg.wgIpaddr, MAX_IP_LEN, pWgConfig->Address);
    StringCbCopy(GetGlobalCfgInfo()->wgClientCfg.wgCfgPath, MAX_PATH, cfgPath);

    ::CloseHandle(hFile);
    return ERR_SUCCESS;
}

int WireGuardCreateServerConfig(const PWGSERVER_CONFIG pWgConfig) {
    const size_t bufSize     = 4096 * sizeof(TCHAR);
    const TCHAR  cfgFormat[] = TEXT(
        "[Interface]\nAddress = %s\nListenPort = %d\nPrivateKey = %s\n\n[Peer]\nPublicKey = %s\nAllowedIPs = %s\n");
    TCHAR  cfgPath[MAX_PATH];
    size_t length;
    HANDLE hFile;
    TCHAR *pBuf;

#pragma region
    if (pWgConfig == nullptr) {
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbLength(pWgConfig->Name, 64, &length)) || 0 == length) {
        SPDLOG_ERROR(TEXT("WireGuard Name error: {0}"), pWgConfig->Name);
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbLength(pWgConfig->Address, 32, &length)) || 0 == length) {
        SPDLOG_ERROR(TEXT("WireGuard Address error: {0}"), pWgConfig->Address);
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbLength(pWgConfig->PrivateKey, 64, &length)) || 0 == length) {
        SPDLOG_ERROR(TEXT("WireGuard Private key error: {0}"), pWgConfig->PrivateKey);
        return -ERR_INPUT_PARAMS;
    }

    if (pWgConfig->ListenPort <= 1024 || pWgConfig->ListenPort >= 65535) {
        SPDLOG_ERROR(TEXT("WireGuard Listen port error: {0}, should be in arrange (1024, 65535)"),
                     pWgConfig->ListenPort);
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbLength(pWgConfig->CliPubKey, 64, &length)) || 0 == length) {
        SPDLOG_ERROR(TEXT("WireGuard Client Public key error: {0}"), pWgConfig->CliPubKey);
        return -ERR_INPUT_PARAMS;
    }

    if (FAILED(StringCbLength(pWgConfig->AllowNet, 256, &length)) || 0 == length) {
        SPDLOG_ERROR(TEXT("WireGuard Allow Client Network error: {0}"), pWgConfig->AllowNet);
        return -ERR_INPUT_PARAMS;
    }
#pragma endregion 参数检查

    pBuf = static_cast<TCHAR *>(malloc(bufSize));

    if (pBuf == nullptr) {
        SPDLOG_ERROR(TEXT("Malloc {1} bytes memory error: {0}"), GetLastError(), bufSize);
        return -ERR_MALLOC_MEMORY;
    }

    memset(pBuf, 0, bufSize);

    StringCbPrintf(cfgPath,
                   MAX_PATH,
                   "%s\\%s",
                   GetGlobalCfgInfo()->configDirectory,
                   GetGlobalCfgInfo()->userCfg.userName);

    // 如果当前用户配置目录不存在则自动创建
    if (!PathFileExists(cfgPath)) {
        if (!CreateDirectory(cfgPath, nullptr)) {
            SPDLOG_ERROR(TEXT("Create configure directory '{0}' error."), cfgPath);
            return -ERR_CREATE_FILE;
        }
    }

    StringCbPrintf(cfgPath,
                   MAX_PATH,
                   "%s\\%s\\%s.conf",
                   GetGlobalCfgInfo()->configDirectory,
                   GetGlobalCfgInfo()->userCfg.userName,
                   pWgConfig->Name);

    hFile = CreateFile(cfgPath,                         // name of the write
                       GENERIC_WRITE | GENERIC_READ,    // open for writing
                       FILE_SHARE_READ,                 // do not share
                       nullptr,                         // default security
                       CREATE_ALWAYS,                   // create new file only
                       FILE_ATTRIBUTE_NORMAL,           // normal file
                       nullptr);                        // no attr. template

    if (hFile == INVALID_HANDLE_VALUE) {
        SPDLOG_ERROR(TEXT("CreatFile [{0}] error: {1}"), cfgPath, GetLastError());
        free(pBuf);
        return -ERR_OPEN_FILE;
    }

    // 清空文件
    SetFilePointer(hFile, 0, nullptr, FILE_BEGIN);
    SetEndOfFile(hFile);

    WritePrivateProfileString(CFG_WIREGUARD_SECTION, CFG_WGCFG_PATH, cfgPath, GetGlobalCfgInfo()->cfgPath);

    if (FAILED(StringCbPrintf(pBuf,
                              bufSize,
                              cfgFormat,
                              pWgConfig->Address,
                              pWgConfig->ListenPort,
                              pWgConfig->PrivateKey,
                              pWgConfig->CliPubKey,
                              pWgConfig->AllowNet))) {
        SPDLOG_ERROR(TEXT("Format string error: {0}"), GetLastError());
        free(pBuf);
        ::CloseHandle(hFile);
        return -ERR_MEMORY_STR;
    }

    if (FAILED(StringCbLength(pBuf, bufSize, &length))) {
        SPDLOG_ERROR(TEXT("Get string \'{0}\' length error: {1}"), pBuf, GetLastError());
        free(pBuf);
        ::CloseHandle(hFile);
        return -ERR_MEMORY_STR;
    }

    SPDLOG_DEBUG(TEXT("WG Server Configure:\n{0}"), pBuf);

    if (FALSE ==
        WriteFile(hFile,                         // open file handle
                  pBuf,                          // start of data to write
                  static_cast<DWORD>(length),    // number of bytes to write
                  nullptr,                       // number of bytes that were written
                  nullptr))                      // no overlapped structure)
    {
        SPDLOG_ERROR(TEXT("WriteFile [{0}] error: {1}"), cfgPath, GetLastError());
        free(pBuf);
        ::CloseHandle(hFile);
        return -ERR_OPEN_FILE;
    }

    ::CloseHandle(hFile);

    StringCbCopy(GetGlobalCfgInfo()->wgServerCfg.wgName, 260, pWgConfig->Name);
    StringCbCopy(GetGlobalCfgInfo()->wgServerCfg.wgIpaddr, MAX_IP_LEN, pWgConfig->Address);
    StringCbCopy(GetGlobalCfgInfo()->wgServerCfg.wgCfgPath, MAX_PATH, cfgPath);
    return ERR_SUCCESS;
}

#if 0

/**
 * @brief 创建 WireGuard 密钥对
 * @param[out] pPubKey 公钥缓冲区
 * @param[in] pubkeySize 公钥缓冲区大小(字节数)
 * @param[out] pPrivKey 私钥缓冲区
 * @param[in] privKeySize 私钥缓冲区大小(字节数)
 * @return 函数执行结果 0: 成功， 小于0 失败 @see USER_ERRNO
 * - -ERR_ITEM_UNEXISTS WireGuard 未配置或未安装
 * - -ERR_CALL_SHELL 调用操作系统命令行工具失败
 * - ERR_SUCCESS 成功
 */
int GenerateWireguardKeyPairs(TCHAR *pPubKey, int pubkeySize, TCHAR *pPrivKey, int privKeySize) {

    int         ret;
    DWORD       retCode;
    TCHAR       cmdBuffer[MAX_PATH];
    TCHAR       cmdResult[MAX_PATH];
    PSDK_CONFIG pCfg = GetGlobalCfgInfo();

    // WireGuard 不存在或者未配置目录
    if (!pCfg->wireguardCfg.wgExists || !pCfg->wireguardCfg.wireguardExists) {
        return -ERR_ITEM_UNEXISTS;
    }

    memset(cmdBuffer, 0, MAX_PATH);
    memset(cmdResult, 0, MAX_PATH);

    StringCbPrintf(cmdBuffer, MAX_PATH, TEXT("cmd.exe /C \"%s\" genkey"), pCfg->wireguardCfg.wgPath);

    if ((ret = RunCommand(cmdBuffer, cmdResult, MAX_PATH, &retCode)) != ERR_SUCCESS) {
        SPDLOG_ERROR(TEXT("Run command [{0}] error: {1}"), cmdBuffer, ret);
        return -ERR_CALL_SHELL;
    }

    SPDLOG_DEBUG(TEXT("Run command [{0}] resutl \'{1}\'"), cmdBuffer, cmdResult);

    StringCbCopy(pPrivKey, privKeySize, cmdResult);
    memset(cmdBuffer, 0, MAX_PATH);
    StringCbPrintf(cmdBuffer,
                   MAX_PATH,
                   TEXT("cmd.exe /C echo %s | \"%s\" pubkey"),
                   cmdResult,
                   pCfg->wireguardCfg.wgPath);

    memset(cmdResult, 0, MAX_PATH);
    if ((ret = RunCommand(cmdBuffer, cmdResult, MAX_PATH, &retCode)) != ERR_SUCCESS) {
        SPDLOG_ERROR(TEXT("Run command [{0}] error: {1}"), cmdBuffer, ret);
        return -ERR_CALL_SHELL;
    }

    StringCbCopy(pPubKey, pubkeySize, cmdResult);
    SPDLOG_DEBUG(TEXT("Run command [{0}] resutl \'{1}\'"), cmdBuffer, cmdResult);

    return ERR_SUCCESS;
}

/**
 * @brief 设置 wireguard.exe 程序路径
 * @param[in] pPath wireguard.exe 程序路径
 * @return 函数执行结果 0: 成功， 小于0 失败 @see USER_ERRNO
 * - -ERR_INPUT_PARAMS 输入参数错误
 * - -ERR_ITEM_UNEXISTS 文件不存在
 * - ERR_SUCCESS 成功
 */
int SetWireguardPath(const TCHAR *pPath) {
    if (pPath == nullptr) {
        return -ERR_INPUT_PARAMS;
    }

    if (PathFileExists(pPath)) {
        TCHAR wgPath[MAX_PATH];

        SPDLOG_DEBUG(TEXT("Used configure file:{0}"), GetGlobalCfgInfo()->cfgPath);

        WritePrivateProfileString(CFG_WIREGUARD_SECTION, CFG_WIREGUARD_PATH, pPath, GetGlobalCfgInfo()->cfgPath);
        SPDLOG_DEBUG(TEXT("Save configure: {1} --> {0}"), pPath, CFG_WIREGUARD_PATH);

        StringCbCopy(wgPath, MAX_PATH, pPath);

        if (TCHAR *pIndex = _tcsrchr(wgPath, '\\')) {
            *pIndex = 0;
            StringCbCat(wgPath, MAX_PATH, "\\wg.exe");
            WritePrivateProfileString(CFG_WIREGUARD_SECTION, CFG_WG_PATH, wgPath, GetGlobalCfgInfo()->cfgPath);
            SPDLOG_DEBUG(TEXT("Save configure: {1} --> {0}"), wgPath, CFG_WG_PATH);
        }

        return ERR_SUCCESS;
    } else {
        SPDLOG_ERROR(TEXT("WireGuard not found: {0}"), pPath);
        return -ERR_ITEM_UNEXISTS;
    }
}

/**
 * @brief 查找 WireGuard 运行环境
 * @param[out] pFullPath wireguard.exe 程序路径
 * @param[in] maxSize pFullPath 缓冲区最大字节数
 * @return 函数执行结果 0: 成功， 小于0 失败 @see USER_ERRNO
 * - -ERR_INPUT_PARAMS 输入参数错误
 * - -ERR_MALLOC_MEMORY 分配内存失败
 * - -ERR_FILE_NOT_EXISTS 文件不存在
 * - ERR_SUCCESS 成功
 */
int FindWireguardExe(TCHAR *pFullPath, int maxSize) {
    TCHAR  path[MAX_PATH];
    TCHAR  wireguardPath[MAX_PATH];
    DWORD  dwRet;
    LPSTR  pEnvBuf;
    TCHAR *token, *p = nullptr;

    GetPrivateProfileString(CFG_WIREGUARD_SECTION,
                            CFG_WIREGUARD_PATH,
                            TEXT(""),
                            wireguardPath,
                            MAX_PATH,
                            GetGlobalCfgInfo()->cfgPath);

    if (PathFileExists(wireguardPath)) {
        if (pFullPath && maxSize > 0) {
            StringCbCopy(pFullPath, maxSize, wireguardPath);
        }

        StringCbCopy(GetGlobalCfgInfo()->wireguardCfg.wireguardPath, MAX_PATH, wireguardPath);
        GetGlobalCfgInfo()->wireguardCfg.wireguardExists = TRUE;

        SPDLOG_DEBUG(TEXT("Ini found WireGuard at: {0}"), wireguardPath);

        GetPrivateProfileString(CFG_WIREGUARD_SECTION,
                                CFG_WG_PATH,
                                TEXT(""),
                                wireguardPath,
                                MAX_PATH,
                                GetGlobalCfgInfo()->cfgPath);

        if (PathFileExists(wireguardPath)) {
            StringCbCopy(GetGlobalCfgInfo()->wireguardCfg.wgPath, MAX_PATH, wireguardPath);
            GetGlobalCfgInfo()->wireguardCfg.wgExists = TRUE;
            SPDLOG_DEBUG(TEXT("Ini found WireGuard Tools at: {0}"), wireguardPath);
        }

        return ERR_SUCCESS;
    }

    StringCbCopy(wireguardPath, MAX_PATH, GetGlobalCfgInfo()->systemDirectory);
    PathStripToRoot(wireguardPath);
    StringCbCat(wireguardPath, MAX_PATH, TEXT("Program Files\\WireGuard\\wireguard.exe"));

    if (PathFileExists(wireguardPath)) {
        // 保存路径到配置文件
        SetWireguardPath(wireguardPath);
        GetGlobalCfgInfo()->wireguardCfg.wireguardExists = TRUE;

        PathRemoveFileSpec(wireguardPath);
        StringCbCat(wireguardPath, MAX_PATH, TEXT("\\wg.exe"));

        if (PathFileExists(wireguardPath)) {
            GetGlobalCfgInfo()->wireguardCfg.wgExists = TRUE;
        }

        return ERR_SUCCESS;
    }

    // 从环境变量中查找
    pEnvBuf = static_cast<LPSTR>(malloc(WINENVBUF_SIZE));
    if (nullptr == pEnvBuf) {
        SPDLOG_ERROR(TEXT("Malloc {0} bytes memory error"), WINENVBUF_SIZE);
        return -ERR_MALLOC_MEMORY;
    }

    dwRet = GetEnvironmentVariable(TEXT("path"), pEnvBuf, WINENVBUF_SIZE);

    if (0 == dwRet) {
        DWORD dwErr;
        dwErr = GetLastError();
        if (ERROR_ENVVAR_NOT_FOUND == dwErr) {
            SPDLOG_DEBUG(TEXT("Environment variable path does not exist."));
            free(pEnvBuf);
            return -ERR_FILE_NOT_EXISTS;
        }
    } else if (WINENVBUF_SIZE < dwRet) {
        const auto pBuf = static_cast<LPSTR>(realloc(pEnvBuf, dwRet * sizeof(CHAR)));
        if (nullptr == pBuf) {
            SPDLOG_ERROR(TEXT("Malloc {0} bytes memory error"), dwRet * sizeof(CHAR));
            free(pEnvBuf);
            return -ERR_MALLOC_MEMORY;
        }

        pEnvBuf = pBuf;
        dwRet   = GetEnvironmentVariable("path", pEnvBuf, dwRet);
        if (!dwRet) {
            SPDLOG_ERROR(TEXT("GetEnvironmentVariable failed (%d)"), GetLastError());
            free(pEnvBuf);
            return -ERR_FILE_NOT_EXISTS;
        }
    }

    token = strtok_s(pEnvBuf, TEXT(";"), &p);

    while (token != nullptr) {
        memset(path, 0, MAX_PATH);
        StringCbPrintfA(path, MAX_PATH, TEXT("%s\\wireguard.exe"), token);

        if (PathFileExists(path)) {
            if (pFullPath && maxSize > 0) {
                StringCbCopy(pFullPath, maxSize, path);
            }

            // 保存路径到配置文件
            SetWireguardPath(path);
            SPDLOG_DEBUG(TEXT("Path Environment found WireGuard at: {0}"), path);

            StringCbCopy(GetGlobalCfgInfo()->wireguardCfg.wireguardPath, MAX_PATH, wireguardPath);
            GetGlobalCfgInfo()->wireguardCfg.wireguardExists = TRUE;

            memset(path, 0, MAX_PATH);
            StringCbPrintf(path, MAX_PATH, TEXT("%s\\wg.exe"), token);

            SPDLOG_DEBUG(TEXT("Find WireGuard tools at: {0}"), path);

            if (PathFileExists(path)) {
                StringCbCopy(GetGlobalCfgInfo()->wireguardCfg.wgPath, MAX_PATH, path);
                GetGlobalCfgInfo()->wireguardCfg.wgExists = TRUE;

                SPDLOG_DEBUG(TEXT("Path Environment found WireGuard tools at: {0}"), path);
            }

            //TODO: throw exception by C# call, why??????
            //CloseHandle(hFind);
            free(pEnvBuf);
            return ERR_SUCCESS;
        }
        token = strtok_s(nullptr, TEXT(";"), &p);
    }

    free(pEnvBuf);
    return -ERR_FILE_NOT_EXISTS;
}

/**
 * @brief 安装 Windows NAT 自定义 PowerShell 命令
 * @return 0: 成功， 小于0 失败 @see USER_ERRNO
 * - -ERR_INPUT_PARAMS 输入参数错误
 * - -ERR_OPEN_FILE 打开文件失败
 * - -ERR_MEMORY_STR 字符串处理
 * - -ERR_ITEM_UNEXISTS 资源不存在
 * - -ERR_CALL_SHELL 调用系统命令行失败
 * - ERR_SUCCESS 成功
 */
int InstallWindowsNATCommand() {
    TCHAR psCmdPath[MAX_PATH];

    // 如果已经安装则退出
    if (IsCustomNatPSCmdInstalled()) {
        return ERR_SUCCESS;
    }

    StringCbPrintf(psCmdPath,
                   MAX_PATH,
                   TEXT("%s\\system32\\WindowsPowerShell\\v1.0\\Modules\\wireguard\\wireguard.psm1"),
                   GetGlobalCfgInfo()->systemDirectory);

    const HANDLE hFile = CreateFile(psCmdPath,
                                    GENERIC_WRITE | GENERIC_READ,
                                    FILE_SHARE_READ,
                                    nullptr,
                                    CREATE_ALWAYS,
                                    FILE_ATTRIBUTE_NORMAL,
                                    nullptr);

    if (hFile == INVALID_HANDLE_VALUE) {
        SPDLOG_ERROR("CreatFile [{0}] error: {1}", psCmdPath, GetLastError());
        return -ERR_OPEN_FILE;
    }

    const HMODULE hMod = GetModuleHandle(TEXT("NetTunnelSDK.dll"));

    if (nullptr == hMod) {
        SPDLOG_ERROR(TEXT("Load NetTunnelSDK.dll module error"));
        ::CloseHandle(hFile);
        return -ERR_ITEM_UNEXISTS;
    }

    const HRSRC hRsrc = FindResource(hMod, MAKEINTRESOURCE(PSCMD_RES_ID), TEXT("TXT"));
    if (nullptr == hRsrc) {
        SPDLOG_ERROR(TEXT("Donot found resource {0} of type {1}"), PSCMD_RES_ID, TEXT("TXT"));
        ::CloseHandle(hFile);
        return -ERR_ITEM_UNEXISTS;
    }

    const DWORD resSize = SizeofResource(hMod, hRsrc);

    if (resSize == 0) {
        SPDLOG_ERROR(TEXT("Resource {0} of type {1} is empty"), PSCMD_RES_ID, TEXT("TXT"));
        ::CloseHandle(hFile);
        return -ERR_ITEM_UNEXISTS;
    }

    const HGLOBAL hGlobal = LoadResource(hMod, hRsrc);

    if (hGlobal == hRsrc) {
        SPDLOG_ERROR(TEXT("Load resource {0} of type {1} error"), PSCMD_RES_ID, TEXT("TXT"));
        ::CloseHandle(hFile);
        return -ERR_ITEM_UNEXISTS;
    }

    if (const LPVOID pBuffer = LockResource(hGlobal)) {
        if (!WriteFile(hFile,                          // open file handle
                       pBuffer,                        // start of data to write
                       static_cast<DWORD>(resSize),    // number of bytes to write
                       nullptr,                        // number of bytes that were written
                       nullptr)) {
            SPDLOG_ERROR("WriteFile [{0}] error: {1}", psCmdPath, GetLastError());
            GlobalUnlock(hGlobal);
            ::CloseHandle(hFile);
            return -ERR_OPEN_FILE;
        }
    }

    GlobalUnlock(hGlobal);
    ::CloseHandle(hFile);

    return ERR_SUCCESS;
}

int WireGuardNetConnectionSharingEnable() {
    int   ret;
    DWORD retCode;
    TCHAR cmdResult[MAX_PATH] = {};
    TCHAR cmdBuf[] = TEXT("PowerShell -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass Invoke-Command -ArgumentList 'wg_cli' -ScriptBlock {param($IFNAME);$netShare = New-Object -ComObject HNetCfg.HNetShare;"
                          "$privateConnection = $netShare.EnumEveryConnection |? { $netShare.NetConnectionProps.Invoke($_).Name -eq 'wg_cli' };"
                          "$privateConfig = $netShare.INetSharingConfigurationForINetConnection.Invoke($privateConnection);"
                          "Write-Output $privateConfig}");

    if ((ret = RunCommand(cmdBuf, cmdResult, MAX_PATH, &retCode)) != ERR_SUCCESS) {
        SPDLOG_ERROR("Run command [{0}] error: {1}", cmdBuf, ret);
        return -ERR_CALL_SHELL;
    }

    SPDLOG_DEBUG("Run command [{0}] resutl \'{1}\' return {2}", cmdBuf, cmdResult, retCode);

    return ERR_SUCCESS;
}
#endif