//
// Created by xajhu on 2021/7/30 0030.
//
#include <libconfig.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>
#include <sds/sds.h>

#include "uthash/uthash.h"
#include "config.h"
#include "misc.h"
#include "user_errno.h"
#include "crypto.h"
#include "hardware.h"
#include "zlog_module.h"
#include "common.h"

typedef struct {
    CONFIG_ITEM_ID    cfgId;
    const char       *cfgPath;
    CONFIG_VALUE_TYPE valType;
    const char       *defValue;
    const char       *readme;
    const char       *pStrId;
} CFG_ITEM, *PCFG_ITEM;

#define CFG_INT_VALUE(p)    (p->value.longValue)
#define CFG_BOOL_VALUE(p)   (p->value.longValue == FALSE ? FALSE : TRUE)
#define CFG_FLOAT_VALUE(p)  (p->value.floatValue)
#define CFG_STRING_VALUE(p) (p->value.strValue)
#define ENC_HEAD            ("AES@")

typedef union {
    long long   longValue;
    char       *strValue;
    long double floatValue;
    c_vector    array;
} CFG_VALUE, *PCFG_VALUE;

typedef struct {
    CONFIG_ITEM_ID    cfgId;
    const char       *pcfgKey;
    CONFIG_VALUE_TYPE valType;
    const char       *defaultValue;
    int               isChanged;
    const char       *pMessage;
    const char       *pStrId;
    CFG_VALUE         value;

    UT_hash_handle hh;
} CONFIG_ITEM, *PCONFIG_ITEM;

static config_t    g_cfgContent;
static const char *g_cfgFilePath;
static const char *g_pCfgKey     = NULL;
PCONFIG_ITEM       g_pConfigItem = NULL;

#define DEF_CFG_ITEM(id, key, type, defVal, desc) \
    { .cfgId = (id), .cfgPath = (key), .valType = (type), .defValue = (defVal), .readme = (desc), .pStrId = (#id) }

// clang-format off
static CFG_ITEM g_cfgItem[] = {
    DEF_CFG_ITEM(CFG_DIRECTORY, "system.config_file_path", VAL_STR, ".", "Configuration files location path"),
    DEF_CFG_ITEM(CFG_CURL_CA_PATH, "system.ssl_ca_file_path", VAL_STR, "~/.ssh/ca.crt", "Libcurl access HTTPS CA File"),
    DEF_CFG_ITEM(CFG_BANNER_SHOW, "system.show_banner", VAL_BOOL, "1", "Enable/Disable show banner"),
    DEF_CFG_ITEM(CFG_HARDWARE_WATCH, "system.system_info_watch", VAL_BOOL, "1", "Monitor cpu, memory, disk, fan, temperature ..."),
    DEF_CFG_ITEM(CFG_HARDWARE_REFRESH, "system.system_info_refresh", VAL_INT, "10", "Monitor hardware information upgrade frequency"),
    // Agent 配置
    DEF_CFG_ITEM(CFG_AGENT_IPTV_POST_URL, "agent.iptv_report_url", VAL_STR, "", "IPTV report URL"),
    DEF_CFG_ITEM(CFG_AGENT_MONITER_URL, "agent.moniter_rep_url", VAL_STR, "", "vCPE moniter report URL"),
    /* 系统监控设备相配置 */
    DEF_CFG_ITEM(CFG_WATCH_CPU, "watch_params.cpu", VAL_BOOL, "1", "Monitor cpu information"),
    DEF_CFG_ITEM(CFG_WATCH_MEMORY, "watch_params.memory", VAL_BOOL, "1", "Monitor memory information"),
    DEF_CFG_ITEM(CFG_WATCH_DISK, "watch_params.disk", VAL_BOOL, "1", "Monitor disk partition information"),
    DEF_CFG_ITEM(CFG_WATCH_SENSOR, "watch_params.sensor", VAL_BOOL, "1", "Sensor information refresh frequency"),
/* 数据库相配置 */
/* Redis配置 */
#ifdef USED_REDIS
    DEF_CFG_ITEM(CFG_DB_REDIS_SERVER, "database.redis_server", VAL_STR, "127.0.0.1", "Redis database server ip address"),
    DEF_CFG_ITEM(CFG_DB_REDIS_PORT, "database.redis_port", VAL_INT, "6379", "Redis database server port"),
    DEF_CFG_ITEM(CFG_DB_REDIS_PASSWD, "database.redis_passwd", VAL_STR, "", "Redis database server password"),
#endif
/* MySQL配置 */
#ifdef USED_MYSQL
    DEF_CFG_ITEM(CFG_DB_MYSQL_SERVER, "database.mysql_server", VAL_STR, "127.0.0.1", "MySQL database server ip address"),
    DEF_CFG_ITEM(CFG_DB_MYSQL_PORT, "database.mysql_port", VAL_INT, "3306", "MySQL database server port"),
    DEF_CFG_ITEM(CFG_DB_MYSQL_USER, "database.mysql_user", VAL_STR, "", "MySQL database user name"),
    DEF_CFG_ITEM(CFG_DB_MYSQL_PASSWD, "database.mysql_passwd", VAL_STR, "", "MySQL database server password"),
    DEF_CFG_ITEM(CFG_DB_MYSQL_DB_NAME, "database.mysql_database", VAL_STR, ".main", "MySQL database used"),
#endif
/* SQLite3配置 */
#ifdef SQLITE_ON
    DEF_CFG_ITEM(CFG_DB_SQLITE_DB_NAME, "database.sqlite_dbname", VAL_STR, "", "SQLite3 database file name"),
#ifdef SQLITE_CRYPTO_ON
    DEF_CFG_ITEM(CFG_DB_SQLITE_PASSWD, "database.sqlite_passwd", VAL_STR, "", "SQLite3 database password"),
#endif
#endif
#ifdef ZEROMQ_ON
    /* 消息队列相配置 */
    /* ZeroMq配置 */
    DEF_CFG_ITEM(CFG_MQ_SVR_PORT, "zero_mq.svr_port", VAL_INT, "6278", "ZeroMQ server port"),
    DEF_CFG_ITEM(CFG_MQ_DATA_PATH, "zero_mq.agent_addr", VAL_STR, "ipc:///tmp/msg_fifo0", "ZeroMQ Agent data path"),
#endif
    /* vxLan 隧道配置 */
    DEF_CFG_ITEM(CFG_VXLAN_NIC_NAME, "vxlan_wan.nic", VAL_STR, "", "Network card name to send data"),
    DEF_CFG_ITEM(CFG_VXLAN_SUPPORT, "vxlan_wan.enable", VAL_BOOL, "1", "Is support vxLan tune"),
    DEF_CFG_ITEM(CFG_VXLAN_PEER_IP, "vxlan_wan.peer_ip", VAL_STR, "", "vxLan peer ip address"),
    DEF_CFG_ITEM(CFG_VXLAN_PEER_MAC, "vxlan_wan.peer_mac", VAL_STR, "", "vxLan peer mac address"),
    DEF_CFG_ITEM(CFG_VXLAN_PKG_FILTER, "vxlan_wan.pkg_filter", VAL_STR, "", "vxLan package filter"),
    /*HTTP Server 配置*/
#ifdef HTTPSERVER_ON
    DEF_CFG_ITEM(CFG_HTTP_SVR_URI, "http_svr.uri", VAL_STR, "http://0.0.0.0:6789", "Network address to listen on"),
#endif
    DEF_CFG_ITEM(CFG_PROTO_CRYPTO, "protocol.crypto_type", VAL_INT, "0", "Protocol crypto algorithm"),
    DEF_CFG_ITEM(CFG_PROTO_CRYPTO_KEY, "protocol.crypto_key", VAL_STR, "", "Protocol crypto keys"),
#ifdef OPENDHCPD_ON
    // 配置DHCP服务器
    DEF_CFG_ITEM(CFG_DHCP_LEASE_TIME, "dhcp_server.lease_time", VAL_INT, "36000", "DHCP server lease time"),
    DEF_CFG_ITEM(CFG_DHCP_LISTEN_ON, "dhcp_server.listen_on", VAL_ARRAY_STR, "", "DHCP listen interface"),
    DEF_CFG_ITEM(CFG_DHCP_REPLICATION_SVR, "dhcp_server.replication", VAL_ARRAY_STR, "", "DHCP replication server configure"),
    DEF_CFG_ITEM(CFG_DHCP_RANGE_SET, "dhcp_server.range_set", VAL_ARRAY_OBJ, "", "DHCP IP pool"),
    DEF_CFG_ITEM(CFG_DHCP_NET_FILTER, "dhcp_server.net_filter", VAL_STR, "", "DHCP packet filter"),
    DEF_CFG_ITEM(CFG_DHCP_MAC_FILTER, "dhcp_server.mac_filter", VAL_ARRAY_STR, "", "DHCP client MAC address black list"),
    DEF_CFG_ITEM(CFG_DHCP_NIC_NAME, "dhcp_server.nic", VAL_STR, "ens192", "DHCP server network interface name"),
#endif
};    // clang-format on

static int cfg_is_upgrade(PCONFIG_ITEM pItem) {

    if (pItem->isChanged) {
        pItem->isChanged = FALSE;
        return TRUE;
    }

    return FALSE;
}

static const char *load_string_value(const char *pKeyName) {
    const char *pCfgVal;
    if (config_lookup_string(&g_cfgContent, pKeyName, &pCfgVal) != CONFIG_TRUE) {
        LOG_MOD(error, ZLOG_MOD_CONFIG, "No {%s} setting in configuration file.\n", pKeyName);
        return NULL;
    }

    if (strncmp(ENC_HEAD, pCfgVal, strlen(ENC_HEAD)) == 0) {
        if (g_pCfgKey == NULL || strlen(g_pCfgKey) == 0) {
            return NULL;
        } else {
            const char    *pKey = get_config_key(g_pCfgKey);
            int            bufSize, outSize;
            unsigned char *buf;
            unsigned char *pBuf = base64_decode(&pCfgVal[strlen(ENC_HEAD)], (unsigned int *)&bufSize);

            if (pBuf == NULL || bufSize <= 0) {
                LOG_MOD(error,
                        ZLOG_MOD_CONFIG,
                        "{%s} setting [%s] maybe a encryption message, base64 decode error.\n",
                        pKeyName,
                        pCfgVal);
                return NULL;
            }

            if (symmetric_decrypto(AES128_ECB_PKCS7PADDING_SHA1PRNG, pBuf, bufSize, &buf, &outSize, pKey) !=
                ERR_SUCCESS) {
                free((void *)pKey);
                free(pBuf);
                return NULL;
            }

            free(pBuf);
            free((void *)pKey);
            buf[outSize] = 0;

            return (const char *)buf;
        }
    } else {
        return strdup(pCfgVal);
    }
}

static int load_boolean_value(const char *pKeyName) {
    int val;
    if (config_lookup_bool(&g_cfgContent, pKeyName, &val) != CONFIG_TRUE) {
        LOG_MOD(error, ZLOG_MOD_CONFIG, "No {%s} setting in configuration file.\n", pKeyName);
        return DEFAULT_INTEGRAL_ERR_VALUE;
    }

    return val;
}

static long long int load_integral_value(const char *pKeyName) {
    long long val;
    if (config_lookup_int64(&g_cfgContent, pKeyName, &val) != CONFIG_TRUE) {
        LOG_MOD(error, ZLOG_MOD_CONFIG, "No {%s} setting in configuration file.\n", pKeyName);
        return DEFAULT_INTEGRAL_ERR_VALUE;
    }

    return val;
}

static double load_float_value(const char *pKeyName) {
    double val;
    if (config_lookup_float(&g_cfgContent, pKeyName, &val) != CONFIG_TRUE) {
        LOG_MOD(error, ZLOG_MOD_CONFIG, "No {%s} setting in configuration file.\n", pKeyName);
        return DEFAULT_INTEGRAL_ERR_VALUE;
    }

    return val;
}

static int cmp_str(const void *a, const void *b) {
    return strcmp((const char *)a, (const char *)b);
}

static int load_array_str(const char *pKeyName, PCONFIG_ITEM pValue) {
    int               i;
    config_setting_t *pItem = config_lookup(&g_cfgContent, pKeyName);

    if (pItem) {
        for (i = 0; i < config_setting_length(pItem); i++) {
            const char *pVal = config_setting_get_string_elem(pItem, i);
            if (pVal) {
                zvect_index idx = 0;
                if (!vect_bsearch(pValue->value.array, pVal, cmp_str, &idx)) {
                    vect_push(pValue->value.array, pVal);
                    pValue->isChanged = TRUE;
                }
            }
        }

        vect_shrink(pValue->value.array);
        return ERR_SUCCESS;
    }

    return -ERR_ITEM_UNEXISTS;
}

static int cmp_dhcp_obj(const void *a, const void *b) {
    POBJ_DHCP_RNG pV1 = (POBJ_DHCP_RNG)a;
    POBJ_DHCP_RNG pV2 = (POBJ_DHCP_RNG)b;

    if (strcmp(pV1->rangAddr, pV2->rangAddr) == 0 && strcmp(pV1->subnet, pV2->subnet) == 0 &&
        strcmp(pV1->dnsSvr, pV2->dnsSvr) == 0 && strcmp(pV1->gateway, pV2->gateway) == 0 && pV1->lease == pV2->lease) {
        return 0;
    }

    return -1;
}

static int load_array_obj(const char *pKeyName, PCONFIG_ITEM pValue) {
    int               i;
    config_setting_t *pItem = config_lookup(&g_cfgContent, pKeyName);

    if (!pItem) {
        return -ERR_ITEM_UNEXISTS;
    }
    for (i = 0; i < config_setting_length(pItem); i++) {
        OBJ_DHCP_RNG      v, k;
        config_setting_t *pObj = config_setting_get_elem(pItem, i);

        switch (pValue->cfgId) {
#if OPENDHCPD_ON
            case CFG_DHCP_RANGE_SET: {
                zvect_index idx = -1;
                const char *rangAddr, *subnet, *dnsSvr, *gateway;
                memset(&v, 0, sizeof(OBJ_DHCP_RNG));

                if (config_setting_lookup_string(pObj, "dhcp_range", &rangAddr)) {
                    strcpy(v.rangAddr, rangAddr);
                }

                if (config_setting_lookup_string(pObj, "subnet_mask", &subnet)) {
                    strcpy(v.subnet, subnet);
                }

                if (config_setting_lookup_string(pObj, "domain_server", &dnsSvr)) {
                    strcpy(v.dnsSvr, dnsSvr);
                }

                if (config_setting_lookup_string(pObj, "gateway", &gateway)) {
                    strcpy(v.gateway, gateway);
                }

                if (!config_setting_lookup_int(pObj, "lease_time", (int *)&v.lease)) {
                    v.lease = 0xFFFFFFFF;
                }

                if (!config_setting_lookup_int(pObj, "vni", (int *)&v.vni)) {
                    v.vni = 0;
                }

                if (!vect_bsearch(pValue->value.array, &v, cmp_dhcp_obj, &idx)) {
                    memcpy(&k, &v, sizeof(OBJ_DHCP_RNG));
                    vect_push(pValue->value.array, &k);
                    pValue->isChanged = TRUE;
                }
            } break;
#endif
            default:
                break;
        }
    }

    return ERR_SUCCESS;
}

static int setConfigItemValue(PCONFIG_ITEM pItem, const char *pValue) {
    errno = 0;

    if (pItem->valType == VAL_STR) {
        if (pItem->value.strValue != NULL) {
            free(pItem->value.strValue);
        }

        pItem->value.strValue = strdup(pValue);
    } else if (pItem->valType == VAL_INT || pItem->valType == VAL_BOOL) {
        char     *pOver;
        long long val = strtoll(pValue, &pOver, 10);

        if ((pOver != NULL && strlen(pOver)) || errno != 0) {
            return -ERR_STRING_TO_NUMBER;
        }

        pItem->value.longValue = val;
    } else if (pItem->valType == VAL_FLOAT) {
        char       *pOver;
        long double val = strtold(pValue, &pOver);

        if ((pOver != NULL && strlen(pOver)) || errno != 0) {
            return -ERR_STRING_TO_NUMBER;
        }

        pItem->value.floatValue = val;
    } else if (pItem->valType == VAL_ARRAY_STR) {
        pItem->value.array = vect_create(128, 512, ZV_SEC_WIPE);
    } else if (pItem->valType == VAL_ARRAY_OBJ) {
        pItem->value.array = vect_create(128, sizeof(OBJ_DHCP_RNG), ZV_SEC_WIPE);
    } else {
        return -ERR_UN_SUPPORT;
    }

    return ERR_SUCCESS;
}

static int add_new_cfg_item(CONFIG_ITEM_ID    id,
                            const char       *pKey,
                            CONFIG_VALUE_TYPE valType,
                            const char       *pDefVal,
                            const char       *pStrId,
                            const char       *pDesc) {
    PCONFIG_ITEM pItem;
    int          ret;

    HASH_FIND_INT(g_pConfigItem, &id, pItem);

    if (pItem != NULL) {
        return -ERR_ITEM_EXISTS;
    }

    pItem = (PCONFIG_ITEM)malloc(sizeof(CONFIG_ITEM));

    if (pItem == NULL) {
        return -ERR_MALLOC_MEMORY;
    }

    memset(pItem, 0, sizeof(CONFIG_ITEM));

    pItem->cfgId        = id;
    pItem->pcfgKey      = pKey;
    pItem->valType      = valType;
    pItem->defaultValue = pDefVal;
    pItem->pMessage     = pDesc;
    pItem->pStrId       = pStrId;

    ret = setConfigItemValue(pItem, pItem->defaultValue);

    if (ret != ERR_SUCCESS) {
        free(pItem);
        return ret;
    }

    HASH_ADD_INT(g_pConfigItem, cfgId, pItem);

    return ERR_SUCCESS;
}

static PCFG_VALUE cfg_get_value(CONFIG_ITEM_ID id) {
    PCONFIG_ITEM pItem;

    HASH_FIND_INT(g_pConfigItem, &id, pItem);

    if (pItem != NULL) {
        return &pItem->value;
    }

    return NULL;
}

static void refreshCfgFileCb() {
    PCONFIG_ITEM pItem, pTmp;
    int          cfgUpgrade = FALSE;

    if (!config_read_file(&g_cfgContent, g_cfgFilePath)) {
        LOG_MOD(error,
                ZLOG_MOD_CONFIG,
                "%s:%d - %s\n",
                config_error_file(&g_cfgContent),
                config_error_line(&g_cfgContent),
                config_error_text(&g_cfgContent));
        return;
    }

    HASH_ITER(hh, g_pConfigItem, pItem, pTmp) {
        char     *pStr;
        long long iVal;
        double    fVal;
        sds       realKey = sdsnew("application.");
        realKey           = sdscat(realKey, pItem->pcfgKey);

        switch (pItem->valType) {
            case VAL_STR:
                pStr = (char *)load_string_value(realKey);
                if (pStr) {
                    if (strcmp(pItem->value.strValue, pStr) != 0) {
                        if (pItem->value.strValue != NULL) {
                            free(pItem->value.strValue);
                        }

                        pItem->value.strValue = strdup(pStr);
                        pItem->isChanged      = TRUE;
                    }

                    free(pStr);
                }
                break;
            case VAL_BOOL:
                iVal = load_boolean_value(realKey);

                if (iVal != DEFAULT_INTEGRAL_ERR_VALUE) {
                    if (pItem->value.longValue != iVal) {
                        pItem->value.longValue = iVal;
                        pItem->isChanged       = TRUE;
                    }
                }

                break;
            case VAL_INT:
                iVal = load_integral_value(realKey);

                if (iVal != DEFAULT_INTEGRAL_ERR_VALUE) {
                    if (pItem->value.longValue != iVal) {
                        pItem->value.longValue = iVal;
                        pItem->isChanged       = TRUE;
                    }
                }

                break;
            case VAL_FLOAT:
                fVal = load_float_value(realKey);

                if (fVal != DEFAULT_INTEGRAL_ERR_VALUE) {
                    if (pItem->value.floatValue != fVal) {
                        pItem->value.floatValue = fVal;
                        pItem->isChanged        = TRUE;
                    }
                }

                break;
            case VAL_ARRAY_STR:
                load_array_str(realKey, pItem);
                break;

            case VAL_ARRAY_OBJ:
                load_array_obj(realKey, pItem);
                break;

            default:
                break;
        }

        if (pItem->isChanged) {
            cfgUpgrade = TRUE;
        }

        sdsfree(realKey);
    }

    if (cfgUpgrade) {
        config_item_dump("Configuration upgrade");
    }
}

const char *config_item_dump_fmt(const char *titleMessage) {
    int          i;
    const char  *pResp;
    PCONFIG_ITEM pItem, pTmp;
    char         tmp2[256];
    sds          tmp;
    sds          s = sdsempty();

    if (titleMessage && strlen(titleMessage) > 0) {
        s = sdscatprintf(s, "%s:\n", titleMessage);
    }

    s = sdscat(s,
               "--------------------------------------------------------------------------------"
               "-----------------------------------------------------------------------\n");

    s = sdscat(s,
               "|  id  |         Key Name          |             Configuration file key            |"
               "                               value                              |\n");

    s = sdscat(s,
               "--------------------------------------------------------------------------------"
               "-----------------------------------------------------------------------\n");

    HASH_ITER(hh, g_pConfigItem, pItem, pTmp) {
        memset(tmp2, 0, 256);
        sprintf(tmp2, "%s%s", cfg_is_upgrade(pItem) ? "*" : " ", pItem->pStrId);
        switch (pItem->valType) {
            case VAL_BOOL:
                s = sdscatprintf(s,
                                 "|%4d  | %-25s | %-45s | %-64s |\n",
                                 pItem->cfgId,
                                 tmp2,
                                 pItem->pcfgKey,
                                 CFG_BOOL_VALUE(pItem) ? "True" : "False");
                break;
            case VAL_INT:
                s = sdscatprintf(s,
                                 "|%4d  | %-25s | %-45s | %-64lld |\n",
                                 pItem->cfgId,
                                 tmp2,
                                 pItem->pcfgKey,
                                 CFG_INT_VALUE(pItem));
                break;
            case VAL_FLOAT:
                s = sdscatprintf(s,
                                 "|%4d  | %-25s | %-45s | %-64Lf |\n",
                                 pItem->cfgId,
                                 tmp2,
                                 pItem->pcfgKey,
                                 CFG_FLOAT_VALUE(pItem));
                break;
            case VAL_STR:
                tmp = sdsempty();
                tmp = sdscatprintf(tmp, "\"%s\"", CFG_STRING_VALUE(pItem));
                if (sdslen(tmp) > 63) {
                    sdsrange(tmp, 0, 59);
                    tmp = sdscat(tmp, "...\"");
                }
                s = sdscatprintf(s, "|%4d  | %-25s | %-45s | %-64s |\n", pItem->cfgId, tmp2, pItem->pcfgKey, tmp);
                sdsfree(tmp);
                break;
            case VAL_ARRAY_STR:
                tmp = sdsempty();
                tmp = sdscat(tmp, "[");
                for (i = 0; i < vect_size(pItem->value.array); i++) {
                    const char *p = (const char *)vect_get_at(pItem->value.array, i);
                    if (p) {
                        tmp = sdscatfmt(tmp, "\"%s\",", p);
                    }
                }
                sdsrange(tmp, 0, -2);
                tmp = sdscat(tmp, "]");
                //tmp = sdscatprintf(tmp, "\"%s\"", CFG_STRING_VALUE(pItem));
                if (sdslen(tmp) > 63) {
                    sdsrange(tmp, 0, 58);
                    tmp = sdscat(tmp, "...\"]");
                }
                s = sdscatprintf(s, "|%4d  | %-25s | %-45s | %-64s |\n", pItem->cfgId, tmp2, pItem->pcfgKey, tmp);
                sdsfree(tmp);
                break;
            case VAL_ARRAY_OBJ:
                for (i = 0; i < vect_size(pItem->value.array); i++) {
                    POBJ_DHCP_RNG p = (POBJ_DHCP_RNG)vect_get_at(pItem->value.array, i);
                    if (!p) {
                        continue;
                    }

                    sds title      = sdsempty();
                    sds keyRang    = sdsempty();
                    sds keySubnet  = sdsempty();
                    sds keyDns     = sdsempty();
                    sds keyGateway = sdsempty();

                    title      = sdscatprintf(title, "%s[%d]", pItem->pcfgKey, i);
                    keyRang    = sdscatprintf(keyRang, "\"%s\"", p->rangAddr);
                    keySubnet  = sdscatprintf(keySubnet, "\"%s\"", p->subnet);
                    keyDns     = sdscatprintf(keyDns, "\"%s\"", p->dnsSvr);
                    keyGateway = sdscatprintf(keyGateway, "\"%s\"", p->gateway);

                    s = sdscatprintf(s, "|%4d  | %-25s | %-45s | %-64s |\n", pItem->cfgId, tmp2, title, "");
                    s = sdscatprintf(s, "|      | %-25s | %-45s | %-64u |\n", "", "  .vni", p->vni);
                    s = sdscatprintf(s, "|      | %-25s | %-45s | %-64s |\n", "", "  .dhcp_range", keyRang);
                    s = sdscatprintf(s, "|      | %-25s | %-45s | %-64s |\n", "", "  .subnet_mask", keySubnet);
                    s = sdscatprintf(s, "|      | %-25s | %-45s | %-64s |\n", "", "  .domain_server", keyDns);
                    s = sdscatprintf(s, "|      | %-25s | %-45s | %-64s |\n", "", "  .gateway", keyGateway);
                    s = sdscatprintf(s, "|      | %-25s | %-45s | %-64u |\n", "", "  .lease_time", p->lease);

                    sdsfree(title);
                    sdsfree(keyRang);
                    sdsfree(keySubnet);
                    sdsfree(keyDns);
                    sdsfree(keyGateway);
                }
                break;
            default:
                break;
        }
    }

    s = sdscat(s,
               "--------------------------------------------------------------------------------"
               "-----------------------------------------------------------------------\n");

    pResp = strdup(s);
    sdsfree(s);

    return pResp;
}

void config_item_dump(const char *titleMessage) {
    const char *pMsg = config_item_dump_fmt(titleMessage);
    LOG_MOD(info, ZLOG_MOD_CONFIG, "%s", pMsg);
    free((char *)pMsg);
#if 0
    PCONFIG_ITEM pItem, pTmp;
    //int i, k = ARRAY_SIZE(g_sysConfigMap);
    LOG_MOD(info, ZLOG_MOD_CONFIG, "================== %s ===================\n", titleMessage == NULL ? "" : titleMessage);
    HASH_ITER(hh, g_pConfigItem, pItem, pTmp) {
        switch (pItem->valType) {
            case VAL_BOOL:
                LOG_MOD(info, ZLOG_MOD_CONFIG, "%s%-25s: [%-45s] = %s\n",
                           cfg_is_upgrade(pItem) ? "*" : " ",
                           pItem->pStrId,
                           pItem->pcfgKey,
                           CFG_BOOL_VALUE(pItem) ? "True" : "False");
                break;
            case VAL_INT:
                LOG_MOD(info, ZLOG_MOD_CONFIG, "%s%-25s: [%-45s] = %lld\n",
                           cfg_is_upgrade(pItem) ? "*" : " ",
                           pItem->pStrId,
                           pItem->pcfgKey,
                           CFG_INT_VALUE(pItem));
                break;
            case VAL_FLOAT:
                LOG_MOD(info, ZLOG_MOD_CONFIG, "%s%-25s: [%-45s] = %Lf\n",
                           cfg_is_upgrade(pItem) ? "*" : " ",
                           pItem->pStrId,
                           pItem->pcfgKey,
                           CFG_FLOAT_VALUE(pItem));
                break;
            case VAL_STR:
                LOG_MOD(info, ZLOG_MOD_CONFIG, "%s%-25s: [%-45s] = \"%s\"\n",
                           cfg_is_upgrade(pItem) ? "*" : " ",
                           pItem->pStrId,
                           pItem->pcfgKey,
                           CFG_STRING_VALUE(pItem));
                break;
            default:
                break;
        }
    }
#endif
}

long double cfg_get_float_value(CONFIG_ITEM_ID id) {
    PCFG_VALUE pVal = cfg_get_value(id);

    if (pVal) {
        return pVal->floatValue;
    } else {
        return DEFAULT_INTEGRAL_ERR_VALUE;
    }
}

long long cfg_get_integral_value(CONFIG_ITEM_ID id) {
    PCFG_VALUE pVal = cfg_get_value(id);

    if (pVal) {
        return pVal->longValue;
    } else {
        return DEFAULT_INTEGRAL_ERR_VALUE;
    }
}

int cfg_get_bool_value(CONFIG_ITEM_ID id) {
    PCFG_VALUE pVal = cfg_get_value(id);

    if (pVal) {
        return pVal->longValue == FALSE ? FALSE : TRUE;
    } else {
        return DEFAULT_INTEGRAL_ERR_VALUE;
    }
}

const char *cfg_get_string_value(CONFIG_ITEM_ID id) {
    PCFG_VALUE pVal = cfg_get_value(id);

    if (pVal) {
        return pVal->strValue;
    } else {
        return NULL;
    }
}

c_vector cfg_get_vector(CONFIG_ITEM_ID id) {
    PCFG_VALUE pVal = cfg_get_value(id);

    if (pVal) {
        return pVal->array;
    } else {
        return NULL;
    }
}

int init_config_system(const char *pCfgFile, const char *pKey) {
    int i;

    if (!file_exists(pCfgFile)) {
        LOG_MOD(error, ZLOG_MOD_CONFIG, "Configuration file [%s] not exists\n", pCfgFile);
        return -ERR_FILE_NOT_EXISTS;
    }

    g_pCfgKey = strdup(pKey == NULL ? "" : pKey);

    g_cfgFilePath = strdup(pCfgFile);

    config_init(&g_cfgContent);

    for (i = 0; i < ARRAY_SIZE(g_cfgItem); i++) {
        add_new_cfg_item(g_cfgItem[i].cfgId,
                         g_cfgItem[i].cfgPath,
                         g_cfgItem[i].valType,
                         g_cfgItem[i].defValue,
                         g_cfgItem[i].pStrId,
                         g_cfgItem[i].readme);
    }

    // clang-format on
    refreshCfgFileCb();

    return ERR_SUCCESS;
}

void uninit_config_system() {
    PCONFIG_ITEM pItem, pTmp;
    HASH_ITER(hh, g_pConfigItem, pItem, pTmp) {
        switch (pItem->valType) {
            case VAL_ARRAY_STR:
            case VAL_ARRAY_OBJ:
                vect_destroy(pItem->value.array);
                break;
            default:
                break;
        }
    }
    config_destroy(&g_cfgContent);
}

const char *get_config_keygen() {
    char     buf[MAX_PATH];
    CPU_INFO cpuInfo;

    get_cpu_info(&cpuInfo);

    memset(buf, 0, MAX_PATH);

    sprintf(buf, "%d#%s", cpuInfo.nLogicCores, cpuInfo.cpuCoreDesc.cpuName);

    return strdup(buf);
}

const char *get_config_key(const char *pKeygen) {
    int            outSize;
    unsigned char *buf = NULL;
    const char    *strRet;
    const char    *pKey = get_config_keygen();

    unsigned char *pBase64 = (unsigned char *)base64_decode(pKeygen, (unsigned int *)&outSize);

    if (pBase64 == NULL) {
        LOG_MOD(error, ZLOG_MOD_CONFIG, "Base64 decode error: %s\n", pKeygen);
        return NULL;
    }

    if (symmetric_decrypto(DES3_CBC_PKCS7PADDING, pBase64, outSize, &buf, &outSize, pKey) != ERR_SUCCESS) {
        free((void *)pKey);
        free((void *)pBase64);
        if (buf) {
            free(buf);
        }
        return NULL;
    } else {
        buf[outSize] = 0;
        strRet       = strdup((const char *)buf);
    }

    free(buf);
    free((void *)pKey);
    free((void *)pBase64);
    return strRet;
}