//
// Created by HuangXin on 2023/4/24.
//

#include <sqlite3.h>
#include "user_errno.h"
#include "database.h"
#include "dhcp_network.h"
#include "rfc2131.h"
#include "ip_pool.h"
#include "db_interface.h"
#include "misc.h"
#include "zlog_module.h"
#include "lease.h"

// 10小时以上清理无效的预分配IP
#define DCHP_STEP_CLEANUP_TIMEOUT (36000)
// Discover 预分配IP 和Request 请求间的超时时间
#define DCHP_STEP_REQUEST_TIMEOUT (600000)

#define CREATE_LEASE_TABLE()                                                   \
    "CREATE TABLE IF NOT EXISTS lease "                                        \
    " (  id       INTEGER PRIMARY KEY AUTOINCREMENT,"                          \
    "    uid      INTEGER         NOT NULL,"                                   \
    "    mac      CHAR(20)        NOT NULL,"                                   \
    "    ip       INTEGER         NOT NULL,"                                   \
    "    lease    INTEGER         NOT NULL,"                                   \
    "    netmask  INTEGER,"                                                    \
    "    gateway  INTEGER,"                                                    \
    "    dns1     INTEGER,"                                                    \
    "    dns2     INTEGER,"                                                    \
    "    server   INTEGER         NOT NULL,"                                   \
    "    hostname CHAR(64)        DEFAULT '' NOT NULL,"                        \
    "    createTm TIMESTAMP   DEFAULT (datetime('now', 'localtime')) NOT NULL" \
    ");"                                                                       \
    "CREATE INDEX IF NOT EXISTS "                                              \
    "lease_index ON lease (uid, mac);"

#define CREATE_PRE_ASSIGN_TABLE()                                              \
    "CREATE TABLE IF NOT EXISTS pre_assign"                                    \
    " (  id       INTEGER PRIMARY KEY AUTOINCREMENT,"                          \
    "    uid      INTEGER         NOT NULL,"                                   \
    "    xid      INTEGER         NOT NULL,"                                   \
    "    hostname CHAR(64)        DEFAULT '' NOT NULL,"                        \
    "    mac      CHAR(20)        NOT NULL,"                                   \
    "    ip       CHAR(24)        NOT NULL,"                                   \
    "    lease    INTEGER         NOT NULL,"                                   \
    "    netmask  CHAR(24),"                                                   \
    "    gateway  CHAR(24),"                                                   \
    "    dns1     CHAR(24),"                                                   \
    "    dns2     CHAR(24),"                                                   \
    "    server   CHAR(24)        NOT NULL,"                                   \
    "    createTm TIMESTAMP   DEFAULT (datetime('now', 'localtime')) NOT NULL" \
    "); CREATE INDEX IF NOT EXISTS pre_assign_index ON pre_assign(ip, uid);"

#define INSERT_PRE_ASSIGN_ROW_FMT                                                                        \
    "INSERT INTO pre_assign (uid, xid, hostname, mac, ip, lease, netmask, gateway, dns1, dns2, server) " \
    "VALUES (%d, %d, '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s');"

#define GET_PRE_ASSIGN_EXISTS_ROW_FMT \
    "SELECT ip, id FROM pre_assign WHERE mac = '%s' AND hostname = '%s' AND uid = %d ORDER BY createTm DESC LIMIT 1;"

#define GET_ASSIGN_IP_INFO_FMT                                                                                      \
    "SELECT id, ip, lease, netmask, gateway, dns1, dns2  FROM pre_assign WHERE mac = '%s' AND hostname = '%s' AND " \
    "uid = %d AND server = '%s' AND (strftime('%%s', 'now', 'localtime') - strftime('%%s', createTm)) < "           \
    "%d ORDER BY createTm DESC LIMIT 1;"

#define IP_IS_PRE_ASSIGN_NOT_TIMEOUT_FMT                                                                         \
    "SELECT (strftime('%%s', 'now', 'localtime') - strftime('%%s', createTm)) as tm FROM pre_assign WHERE ip = " \
    "'%s' AND tm < %d AND uid = %d ORDER BY tm DESC LIMIT 1;"

#define IP_IS_PRE_ASSIGN "SELECT ip FROM pre_assign WHERE ip = '%s' AND uid = %d"

#define IP_IS_PRE_ASSIGN_TIMEOUT_FMT                                                                                 \
    "SELECT (strftime('%%s', 'now', 'localtime') - strftime('%%s', createTm)) as tm, id FROM pre_assign WHERE ip = " \
    "'%s' AND tm >= %d AND uid = %d ORDER BY tm DESC LIMIT 1;"

#define GET_PRE_ASSIGN_ROW_FMT \
    "SELECT ip, uid FROM pre_assign WHERE (strftime('%%s', 'now', 'localtime') - strftime('%%s', createTm)) < %d;"

#define CLS_TIMEOUT_PRE_ASSIGN_ROW_FMT \
    "DELETE FROM pre_assign WHERE (strftime('%%s', 'now', 'localtime') - strftime('%%s', createTm)) > %d;"

#define UPDATE_CREATE_TIME_BY_ID_FMT "UPDATE pre_assign SET createTm = datetime('now', 'localtime') WHERE id = %s"

#define INSERT_LEASE_FMT                                                                      \
    "INSERT INTO lease(uid, hostname, mac, ip, lease, netmask, gateway, dns1, dns2, server) " \
    "VALUES (%d, '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s');"

#define LEASE_FIND_ITEM_FMT                                                                                        \
    "SELECT id FROM lease WHERE mac = '%s' AND hostname = '%s' AND uid = %d AND ip = '%s' ORDER BY createTm DESC " \
    "LIMIT 1;"

#define LEASE_ITEM_UPDATE_FMT                                                                                   \
    "UPDATE lease SET lease = %d, netmask = '%s', gateway = '%s', dns1 = '%s', dns2 = '%s',  server = '%s',   " \
    "createTm = datetime('now', 'localtime') WHERE id = %s;"

#define CLS_TIMEOUT_LEASE_ROW_FMT \
    "DELETE FROM lease WHERE (strftime('%%s', 'now', 'localtime') - - strftime('%%s', createTm)) > lease.lease"

#define GET_LEASE_ROW_FMT \
    "SELECT ip, uid FROM lease WHERE (strftime('%%s', 'now', 'localtime') - strftime('%%s', createTm)) < lease.lease;"

#define GET_LEASE_EXISTS_ROW_FMT                                                                              \
    "SELECT ip, id FROM lease WHERE mac = '%s' AND hostname = '%s' AND uid = %d AND (strftime('%%s', 'now', " \
    "'localtime') - strftime('%%s', createTm)) < lease.lease ORDER BY createTm DESC LIMIT 1;"

#define GET_LEASE_INFO_FMT                                                                                           \
    "SELECT id, ip, lease, netmask, gateway, dns1, dns2  FROM lease WHERE mac = '%s' AND hostname = '%s' AND "       \
    "uid = %d AND server = '%s' AND (strftime('%%s', 'now','localtime') - strftime('%%s', createTm)) < lease.lease " \
    "ORDER BY createTm DESC LIMIT 1;"

#define UPDATE_LEASE_TIME_BY_ID_FMT "UPDATE lease SET createTm = datetime('now', 'localtime') WHERE id = %llu"

#define LEASE_RELEASE_FMT           "DELETE FROM lease WHERE uid = %d AND mac = '%s' AND ip = '%s'"


static int lease_add(PDHCP_REQ   pReq,
                     const char *ip,
                     const char *netmask,
                     const char *gw,
                     const char *dns1,
                     const char *dns2,
                     U32         leaseTime) {
    int    rc;
    char   buf[2048]  = {0};
    char   macStr[20] = {0};
    char **dbResult;
    int    nRow = 0, nColumn = 0;

    MAC_TO_STR(pReq->cliMac, macStr);
    snprintf(buf, 2048, LEASE_FIND_ITEM_FMT, macStr, pReq->hostName, pReq->uid, ip);

    rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL);
    if (rc == ERR_SUCCESS) {
        // 已经存在,则更新
        if (nRow > 0 && nColumn > 0) {
            memset(buf, 0, 2048);
            snprintf(buf, 2048, LEASE_ITEM_UPDATE_FMT, leaseTime, netmask, gw, dns1, dns2,
                     u32_to_str_ip(pReq->serverAddr), dbResult[nColumn]);
            rc = db_sqlite3_sql_exec(buf, NULL, NULL, NULL);
            if (rc != ERR_SUCCESS) {
                LOG_MOD(error, ZM_DHCP_DB, "Add upgrade to lease db error: id = %s\n", dbResult[nColumn]);
            }
        } else {
            //添加一个新的
            // 记录设备分配的DHCP信息到数据库
            memset(buf, 0, 2048);
            snprintf(buf, 2048, INSERT_LEASE_FMT, pReq->uid, pReq->hostName, macStr, ip, leaseTime, netmask, gw, dns1,
                     dns2, u32_to_str_ip(pReq->serverAddr));

            rc = db_sqlite3_sql_exec(buf, NULL, NULL, NULL);

            if (rc != ERR_SUCCESS) {
                LOG_MOD(error, ZM_DHCP_DB, "Add date to lease db error\n");
            }
        }
    }

    sqlite3_free_table(dbResult);
    return ERR_SUCCESS;
}

int db_add_lease(PDHCP_REQ pReq, PPOOL_CTX pCtx) {
    const char *pNetmask = u32_to_str_ip_safe(htonl(pCtx->netMask));
    const char *pGateway = u32_to_str_ip_safe(htonl(pCtx->gwAddr));
    const char *pDns1    = u32_to_str_ip_safe(htonl(pCtx->primeDNS));
    const char *pDns2    = u32_to_str_ip_safe(htonl(pCtx->salveDNS));
    const char *pIp      = u32_to_str_ip_safe(htonl(pReq->cliAddr));

    // 添加租约信息到lease数据库
    lease_add(pReq, pIp, pNetmask, pGateway, pDns1, pDns2, pCtx->leaseTime);

    free((void *)pNetmask);
    free((void *)pGateway);
    free((void *)pDns1);
    free((void *)pDns2);
    free((void *)pIp);
    return ERR_SUCCESS;
}

int db_release_lease(PDHCP_REQ pReq) {
    int  rc;
    char buf[1024]  = {0};
    char macStr[20] = {0};
    MAC_TO_STR(pReq->cliMac, macStr);
    snprintf(buf, 1024, LEASE_RELEASE_FMT, pReq->uid, macStr, u32_to_str_ip(htonl(pReq->reqIpAddr)));

    rc = db_sqlite3_sql_exec(buf, NULL, NULL, NULL);

    if (rc != ERR_SUCCESS) {
        LOG_MOD(error, ZM_DHCP_DB, "DB remove lease item error: %s\n", buf);
    }
    return ERR_SUCCESS;
}

int db_lock_pre_assign_ip() {
    int    rc;
    char   buf[1024] = {0};
    char **dbResult;
    int    nRow = 0, nColumn = 0;

    snprintf(buf, 1024, GET_PRE_ASSIGN_ROW_FMT, DCHP_STEP_CLEANUP_TIMEOUT);

    rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL);
    if (rc == ERR_SUCCESS && nRow > 0 && nColumn > 0) {
        int i;
        for (i = 1; i <= nRow; i++) {
            U32        uid   = strtoul(dbResult[i * nColumn + 1], NULL, 10);
            U32        ip    = ntohl(inet_addr(dbResult[i * nColumn]));
            PDHCP_USER pUser = dhcp_user_create(uid);

            if (pUser) {
                usr_lease_lock_ip(pUser, ip);
                LOG_MOD(debug, ZM_DHCP_DB, "Lock prepare assign ip %s for user %u\n", dbResult[i * nColumn], uid);
            }
            //            printf("-- Row %d value %s, %s, %s\n", i, dbResult[i * nColumn], dbResult[i * nColumn + 1], dbResult[i * nColumn + 2]);
        }
    }
    sqlite3_free_table(dbResult);

    memset(buf, 0, 1024);
    snprintf(buf, 1024, GET_LEASE_ROW_FMT);
    rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL);
    if (rc == ERR_SUCCESS && nRow > 0 && nColumn > 0) {
        int i;
        for (i = 1; i <= nRow; i++) {
            U32        uid   = strtoul(dbResult[i * nColumn + 1], NULL, 10);
            U32        ip    = ntohl(inet_addr(dbResult[i * nColumn]));
            PDHCP_USER pUser = dhcp_user_create(uid);
            if (pUser) {
                usr_lease_lock_ip(pUser, ip);
                LOG_MOD(debug, ZM_DHCP_DB, "Lock prepare assign ip %s for user %u\n", dbResult[i * nColumn], uid);
            }
        }
    }
    sqlite3_free_table(dbResult);

    return ERR_SUCCESS;
}

int db_clearup_timeout_lease() {
    int  rc;
    char buf[1024] = {0};

    snprintf(buf, 1024, CLS_TIMEOUT_PRE_ASSIGN_ROW_FMT, DCHP_STEP_CLEANUP_TIMEOUT);

    rc = db_sqlite3_sql_exec(buf, NULL, NULL, NULL);

    LOG_MOD(trace, ZM_DHCP_DB, "Cleanup pre_assign database timeout(%u seconds) data\n", DCHP_STEP_CLEANUP_TIMEOUT);

    if (rc != ERR_SUCCESS) {
        LOG_MOD(error, ZM_DHCP_DB, "Cleanup pre_assign database error: %s\n", buf);
    }

    rc = db_sqlite3_sql_exec(CLS_TIMEOUT_LEASE_ROW_FMT, NULL, NULL, NULL);

    LOG_MOD(trace, ZM_DHCP_DB, "Cleanup assign database lease expire data\n");
    if (rc != ERR_SUCCESS) {
        LOG_MOD(error, ZM_DHCP_DB, "Cleanup lease database error: %s\n", buf);
    }

    return ERR_SUCCESS;
}

int db_ip_is_pre_assign(U32 uid, U32 ip) {
    int    rc;
    char   buf[1024] = {0};
    char **dbResult;
    int    nRow = 0, nColumn = 0;

    // 判断IP是否存曾经被预分配
    snprintf(buf, 1024, IP_IS_PRE_ASSIGN, u32_to_str_ip(htonl(ip)), uid);
    rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL);
    sqlite3_free_table(dbResult);
    if (rc == ERR_SUCCESS && nRow == 0) {
        // 数据库没有相关记录,直接返回
        LOG_MOD(trace, ZM_DHCP_DB, "New prepare assign ipaddr %s form user %u\n", u32_to_str_ip(htonl(ip)), uid);
        return FALSE;
    }

    // 判断数据库中该IP是否未超时
    memset(buf, 0, 1024);
    snprintf(buf, 1024, IP_IS_PRE_ASSIGN_NOT_TIMEOUT_FMT, u32_to_str_ip(htonl(ip)), DCHP_STEP_CLEANUP_TIMEOUT, uid);
    rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL);
    sqlite3_free_table(dbResult);

    if (rc == ERR_SUCCESS && nRow > 0 && nColumn > 0) {
        // 如果数据库存在记录说明该IP暂时不可以用
        LOG_MOD(trace, ZM_DHCP_DB, "No free ip address form user %u\n", uid);
        return TRUE;
    }

    // 判断是否有超时预分配的IP, 存在的话可以预分配给其它请求
    memset(buf, 0, 1024);
    snprintf(buf, 1024, IP_IS_PRE_ASSIGN_TIMEOUT_FMT, u32_to_str_ip(htonl(ip)), DCHP_STEP_CLEANUP_TIMEOUT, uid);
    rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL);
    if (rc == ERR_SUCCESS && nRow > 0 && nColumn > 0) {
        // 清理超时资源并预分配
        // 更新时间戳
        memset(buf, 0, 1024);
        snprintf(buf, 1024, UPDATE_CREATE_TIME_BY_ID_FMT, dbResult[3]);
        db_sqlite3_sql_exec(buf, NULL, NULL, NULL);
        sqlite3_free_table(dbResult);
        LOG_MOD(trace, ZM_DHCP_DB, "New prepare assign ipaddr %s form user %u by clearup resource\n",
                u32_to_str_ip(htonl(ip)), uid);
        return FALSE;
    }
    sqlite3_free_table(dbResult);

    // 处理失败,默认返回
    return TRUE;
}

static int db_get_table_items(PPOOL_CTX pAssign, const char *pRunSql, U64 *pDbId) {
    int    rc;
    char **dbResult;
    int    nRow = 0, nColumn = 0;

    rc = db_sqlite3_get_rows(pRunSql, &dbResult, &nRow, &nColumn, NULL);
    if (rc == ERR_SUCCESS && nRow > 0 && nColumn > 0) {
        char *pNetmask = dbResult[nColumn + 3];
        char *pGateway = dbResult[nColumn + 4];
        char *pDns1    = dbResult[nColumn + 5];
        char *pDns2    = dbResult[nColumn + 6];

        // minAddr 记录可分配的IP
        pAssign->minAddr   = ntohl(inet_addr(dbResult[nColumn + 1]));
        pAssign->leaseTime = strtoul(dbResult[nColumn + 2], NULL, 10);

        if (strlen(pNetmask) > 0) {
            pAssign->netMask = ntohl(inet_addr(pNetmask));
        }

        if (strlen(pGateway) > 0) {
            pAssign->gwAddr = ntohl(inet_addr(pGateway));
        }

        if (strlen(pDns1) > 0) {
            pAssign->primeDNS = ntohl(inet_addr(pDns1));
        }

        if (strlen(pDns2) > 0) {
            pAssign->salveDNS = ntohl(inet_addr(pDns2));
        }

        if (pDbId) {
            *pDbId = strtoull(dbResult[nColumn], NULL, 10);
        }

        sqlite3_free_table(dbResult);
        return ERR_SUCCESS;
    } else {
        sqlite3_free_table(dbResult);
        return -ERR_ITEM_UNEXISTS;
    }
}

int db_get_lease(PDHCP_REQ pReq, PPOOL_CTX pAssign) {
    int  rc;
    U64  id;
    char buf[2048]  = {0};
    char macStr[20] = {0};

    MAC_TO_STR(pReq->cliMac, macStr);
    snprintf(buf, 2048, GET_LEASE_INFO_FMT, macStr, pReq->hostName, pReq->uid, u32_to_str_ip(pReq->serverAddr));

    rc = db_get_table_items(pAssign, buf, &id);
    if (rc == ERR_SUCCESS) {
        // 更新租约时间信息
        memset(buf, 0, 1024);
        snprintf(buf, 1024, UPDATE_LEASE_TIME_BY_ID_FMT, id);

        if (db_sqlite3_sql_exec(buf, NULL, NULL, NULL) != ERR_SUCCESS) {
            LOG_MOD(warn, ZM_DHCP_DB, "Update %llu lease time error: %s\n", id, buf);
        }
        return ERR_SUCCESS;
    }

    return -ERR_ITEM_UNEXISTS;
}

int db_get_pre_lease(PDHCP_REQ pReq, PPOOL_CTX pAssign) {
    int  rc;
    char buf[2048]  = {0};
    char macStr[20] = {0};
    U64  id;

    MAC_TO_STR(pReq->cliMac, macStr);
    snprintf(buf, 2048, GET_ASSIGN_IP_INFO_FMT, macStr, pReq->hostName, pReq->uid, u32_to_str_ip(pReq->serverAddr),
             DCHP_STEP_REQUEST_TIMEOUT);

    rc = db_get_table_items(pAssign, buf, &id);

    if (rc == ERR_SUCCESS) {
        const char *pNetmask = u32_to_str_ip_safe(htonl(pAssign->netMask));
        const char *pGateway = u32_to_str_ip_safe(htonl(pAssign->gwAddr));
        const char *pDns1    = u32_to_str_ip_safe(htonl(pAssign->primeDNS));
        const char *pDns2    = u32_to_str_ip_safe(htonl(pAssign->salveDNS));
        const char *pIp      = u32_to_str_ip_safe(htonl(pAssign->minAddr));

        // 删除预分配IP信息
        memset(buf, 0, 2048);
        sprintf(buf, "DELETE FROM pre_assign WHERE id = %llu;", id);
        db_sqlite3_sql_exec(buf, NULL, NULL, NULL);

        // 添加租约信息到lease数据库
        lease_add(pReq, pIp, pNetmask, pGateway, pDns1, pDns2, pAssign->leaseTime);

        free((void *)pNetmask);
        free((void *)pGateway);
        free((void *)pDns1);
        free((void *)pDns2);
        free((void *)pIp);
        return ERR_SUCCESS;
    }
    return -ERR_ITEM_UNEXISTS;
#if 0
    rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL);
    if (rc == ERR_SUCCESS && nRow > 0 && nColumn > 0) {
        char *pNetmask = dbResult[nColumn + 3];
        char *pGateway = dbResult[nColumn + 4];
        char *pDns1    = dbResult[nColumn + 5];
        char *pDns2    = dbResult[nColumn + 6];

        // minAddr 记录可分配的IP
        pAssign->minAddr   = ntohl(inet_addr(dbResult[nColumn + 1]));
        pAssign->leaseTime = strtoul(dbResult[nColumn + 2], NULL, 10);

        if (strlen(pNetmask) > 0) {
            pAssign->netMask = ntohl(inet_addr(pNetmask));
        }

        if (strlen(pGateway) > 0) {
            pAssign->gwAddr = ntohl(inet_addr(pGateway));
        }

        if (strlen(pDns1) > 0) {
            pAssign->primeDNS = ntohl(inet_addr(pDns1));
        }

        if (strlen(pDns2) > 0) {
            pAssign->salveDNS = ntohl(inet_addr(pDns2));
        }

        // 删除预分配IP信息
        memset(buf, 0, 2048);
        sprintf(buf, "DELETE FROM pre_assign WHERE id = %s;", dbResult[nColumn]);
        //db_sqlite3_sql_exec(buf, NULL, NULL, NULL);

        lease_add(pReq, dbResult[nColumn + 1], pNetmask, pGateway, pDns1, pDns2, pAssign->leaseTime);

        sqlite3_free_table(dbResult);
        return ERR_SUCCESS;
    } else {
        LOG_MOD(error, ZM_DHCP_DB, "No previously records: \n%s\n", buf);
    }

    sqlite3_free_table(dbResult);
    return -ERR_ITEM_UNEXISTS;
#endif
}

int db_get_pre_assign(U32 uid, const char *mac, const char *hostname, U32 *preAssign) {
    int    rc;
    char   buf[1024] = {0};
    char **dbResult;
    int    nRow = 0, nColumn = 0;

    snprintf(buf, 1024, GET_PRE_ASSIGN_EXISTS_ROW_FMT, mac, hostname, uid);

    rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL);
    if (rc == ERR_SUCCESS && nRow > 0 && nColumn > 0) {
        if (preAssign) {
            *preAssign = ntohl(inet_addr(dbResult[2]));
            // 更新时间戳
            memset(buf, 0, 1024);
            snprintf(buf, 1024, UPDATE_CREATE_TIME_BY_ID_FMT, dbResult[3]);
            db_sqlite3_sql_exec(buf, NULL, NULL, NULL);
        }

        sqlite3_free_table(dbResult);
        return ERR_ITEM_EXISTS;
    } else {
        memset(buf, 0, 1024);
        snprintf(buf, 1024, GET_LEASE_EXISTS_ROW_FMT, mac, hostname, uid);

        rc = db_sqlite3_get_rows(buf, &dbResult, &nRow, &nColumn, NULL);
        if (rc == ERR_SUCCESS && nRow > 0 && nColumn > 0) {
            if (preAssign) {
                *preAssign = ntohl(inet_addr(dbResult[2]));
                // 更新时间戳
                memset(buf, 0, 1024);
                snprintf(buf, 1024, UPDATE_CREATE_TIME_BY_ID_FMT, dbResult[3]);
                db_sqlite3_sql_exec(buf, NULL, NULL, NULL);
            }

            sqlite3_free_table(dbResult);
            return ERR_ITEM_EXISTS;
        }
    }

    sqlite3_free_table(dbResult);
    return ERR_ITEM_UNEXISTS;
}

int db_add_pre_assign(PDHCP_REQ pReq, U32 ip, PPOOL_CTX pPool) {
    int         rc;
    char        buf[1024]  = {0};
    char        macStr[20] = {0};
    const char *pIp        = u32_to_str_ip_safe(htonl(ip));
    const char *pMask      = u32_to_str_ip_safe(htonl(pPool->netMask));
    const char *pGw        = u32_to_str_ip_safe(htonl(pPool->gwAddr));
    const char *pDns1      = u32_to_str_ip_safe(htonl(pPool->primeDNS));
    const char *pDns2      = u32_to_str_ip_safe(htonl(pPool->salveDNS));
    const char *pServer    = u32_to_str_ip_safe(pReq->serverAddr);

    MAC_TO_STR(pReq->cliMac, macStr);
    snprintf(buf, 1024, INSERT_PRE_ASSIGN_ROW_FMT, pReq->uid, pReq->xid, pReq->hostName, macStr, pIp, pPool->leaseTime,
             pMask, pGw, pDns1, pDns2, pServer);

    free((void *)pIp);
    free((void *)pMask);
    free((void *)pGw);
    free((void *)pDns1);
    free((void *)pDns2);
    free((void *)pServer);

    rc = db_sqlite3_sql_exec(buf, NULL, NULL, NULL);

    if (rc != ERR_SUCCESS) {
        return rc;
    }

    return ERR_SUCCESS;
}

int db_init_lease_database() {
    int rc;

    rc = db_sqlite3_sql_exec(CREATE_LEASE_TABLE(), NULL, NULL, NULL);

    if (rc != ERR_SUCCESS) {
        return rc;
    }

    rc = db_sqlite3_sql_exec(CREATE_PRE_ASSIGN_TABLE(), NULL, NULL, NULL);

    if (rc != ERR_SUCCESS) {
        return rc;
    }

    return ERR_SUCCESS;
}