/* -------------------------------------------------------------------------
 *  This file is part of the oGRAC project.
 * Copyright (c) 2024 Huawei Technologies Co.,Ltd.
 *
 * oGRAC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * -------------------------------------------------------------------------
 *
 * cm_dbs_ctrl.c
 *
 *
 * IDENTIFICATION
 * src/common/cm_dbs_ctrl.c
 *
 * -------------------------------------------------------------------------
 */
#include "cm_dbs_module.h"
#include "cm_dbs_ctrl.h"
#include "cm_log.h"
#include "cm_error.h"
#include "cm_dbs_intf.h"
#include "cm_dbs_map.h"
#include "cm_text.h"
#include "cm_dbs_iofence.h"
#include "cm_dbstor.h"
#include "srv_param_common.h"

typedef struct {
    NameSpaceId pgNsId;
    NameSpaceId ulogNsId;
    char pgNsName[DBS_NS_MAX_NAME_LEN];
    char ulogNsName[DBS_NS_MAX_NAME_LEN];
} cm_dbs_ns_mgr;

cm_dbs_ns_mgr g_dbs_ns_mgr;
static cm_dbs_cfg_s g_dbs_cfg = { OG_FALSE };

static status_t cm_dbs_set_ns_id(device_type_t type, char* nsName)
{
    int32 ret;
    if (type == DEV_TYPE_PGPOOL) {
        ret = strcpy_sp(g_dbs_ns_mgr.pgNsName, DBS_NS_MAX_NAME_LEN, nsName);
        if (SECUREC_UNLIKELY(ret != EOK)) {
            OG_THROW_ERROR(ERR_SYSTEM_CALL, ret);
            return OG_ERROR;
        }
        if (ret != OG_SUCCESS) {
            OG_LOG_RUN_ERR("Failed to set pgNsName %s, ret %d", nsName, ret);
            return OG_ERROR;
        }
    } else if (type == DEV_TYPE_ULOG) {
        ret = strcpy_sp(g_dbs_ns_mgr.ulogNsName, DBS_NS_MAX_NAME_LEN, nsName);
        if (SECUREC_UNLIKELY(ret != EOK)) {
            OG_THROW_ERROR(ERR_SYSTEM_CALL, ret);
            return OG_ERROR;
        }
        if (ret != OG_SUCCESS) {
            OG_LOG_RUN_ERR("Failed to set ulogNsName %s, ret %d", nsName, ret);
            return OG_ERROR;
        }
    }
    return OG_SUCCESS;
}

status_t cm_dbs_get_ns_name(device_type_t type, char** nsName)
{
    if (type == DEV_TYPE_PGPOOL) {
        *nsName = g_dbs_ns_mgr.pgNsName;
        return OG_SUCCESS;
    } else if (type == DEV_TYPE_ULOG) {
        *nsName = g_dbs_ns_mgr.ulogNsName;
        return OG_SUCCESS;
    }
    return OG_ERROR;
}

status_t cm_dbs_get_ns_id(device_type_t type, NameSpaceId *nsId)
{
    if (type == DEV_TYPE_PGPOOL) {
        *nsId = g_dbs_ns_mgr.pgNsId;
        return OG_SUCCESS;
    } else if (type == DEV_TYPE_ULOG) {
        *nsId = g_dbs_ns_mgr.ulogNsId;
        return OG_SUCCESS;
    }
    return OG_ERROR;
}

static status_t cm_dbs_create_ns(const char *name)
{
    NameSpaceAttr nsAttr;
    nsAttr.userId = 0;
    nsAttr.poolId = 0;
    nsAttr.app = 0;
    nsAttr.mod = 0;
    nsAttr.termId = 0;
    nsAttr.dbVersion = OGRAC_VERSION;
    return dbs_global_handle()->create_namespace((char *)name, &nsAttr) == 0 ? OG_SUCCESS : OG_ERROR;
}

status_t cm_dbs_create_all_ns(void)
{
    int32 ret;
    cm_dbs_cfg_s *cfg = cm_dbs_get_cfg();

    if (!cfg->enable) {
        return OG_SUCCESS;
    }

    ret = cm_dbs_create_ns(cfg->ns);
    if (ret != OG_SUCCESS) {
        OG_LOG_RUN_ERR("Failed to create namespace %s, ret %d", cfg->ns, ret);
        return OG_ERROR;
    }

    ret = cm_dbs_set_ns_id(DEV_TYPE_PGPOOL, cfg->ns);
    if (ret != OG_SUCCESS) {
        return OG_ERROR;
    }
    ret = cm_dbs_set_ns_id(DEV_TYPE_ULOG, cfg->ns);
    if (ret != OG_SUCCESS) {
        return OG_ERROR;
    }
    return OG_SUCCESS;
}

static status_t cm_dbs_open_ns(const char *name)
{
    int32 ret;
    NameSpaceAttr attr;

    ret = dbs_global_handle()->open_namespace((char *)name, &attr);
    if (ret != 0) {
        OG_LOG_RUN_ERR("Failed to open namespace %s, ret %d", name, ret);
        return OG_ERROR;
    }
    OG_LOG_DEBUG_INF("current oGRAC version is %u, original version is %u", OGRAC_VERSION, attr.dbVersion);
    if (attr.dbVersion > OGRAC_VERSION) {
        OG_LOG_RUN_ERR("Failed to open namespace, current oGRAC version less than original version");
        return OG_ERROR;
    }
    return OG_SUCCESS;
}

status_t cm_dbs_open_all_ns(void)
{
    int32 ret;
    cm_dbs_cfg_s *cfg = cm_dbs_get_cfg();

    if (!cfg->enable) {
        return OG_SUCCESS;
    }

    ret = cm_dbs_open_ns(cfg->ns);
    if (ret != OG_SUCCESS) {
        OG_LOG_RUN_ERR("Failed to open namespace %s, ret %d", cfg->ns, ret);
        return OG_ERROR;
    }

    ret = cm_dbs_set_ns_id(DEV_TYPE_PGPOOL, cfg->ns);
    if (ret != OG_SUCCESS) {
        return OG_ERROR;
    }
    ret = cm_dbs_set_ns_id(DEV_TYPE_ULOG, cfg->ns);
    if (ret != OG_SUCCESS) {
        return OG_ERROR;
    }
    return OG_SUCCESS;
}

static status_t cm_dbs_set_ns_name(cm_dbs_cfg_s *cfg, const char *value)
{
    char str_tmp[DBS_NS_MAX_NAME_LEN] = { 0 };
    errno_t err = strcpy_s(str_tmp, sizeof(str_tmp), value);
    if (err != EOK) {
        OG_THROW_ERROR(ERR_SYSTEM_CALL, (err));
        return OG_ERROR;
    }
    text_t txt;
    txt.str = str_tmp;
    txt.len = (uint32)strlen(str_tmp);
    cm_trim_text(&txt);
    if (txt.len == 0) {
        return OG_ERROR;
    }
    err = strncpy_s(cfg->ns, sizeof(cfg->ns), txt.str, txt.len);
    if (err != EOK) {
        OG_THROW_ERROR(ERR_SYSTEM_CALL, (err));
        return OG_ERROR;
    }
    return OG_SUCCESS;
}

cm_dbs_cfg_s *cm_dbs_get_cfg(void)
{
    return &g_dbs_cfg;
}

status_t cm_dbs_set_cfg(bool32 enable, uint32 dataPgSize, uint32 ctrlPgSize, const char *ns_name, uint32 partition_num,
    bool32 enable_batch_flush, uint32 deploy_mode)
{
    status_t ret;

    g_dbs_cfg.enable = enable;
    if (enable) {
        g_dbs_cfg.dataFilePgSize = dataPgSize;
        g_dbs_cfg.ctrlFilePgSize = ctrlPgSize;
        g_dbs_cfg.partition_num = partition_num;
        g_dbs_cfg.enable_batch_flush = enable_batch_flush;
        g_dbs_cfg.deploy_mode = deploy_mode;
        OG_LOG_RUN_INF("date page size is %d, ctrl page size is %d, partition num %d, enable_batch_flush %d, mode %u",
            dataPgSize, ctrlPgSize, partition_num, enable_batch_flush, deploy_mode);
        if (ns_name == NULL || strlen(ns_name) == 0) {
            OG_LOG_RUN_ERR("DBStor namespace param error");
            return OG_ERROR;
        }
        ret = cm_dbs_set_ns_name(&g_dbs_cfg, ns_name);
        if (ret != OG_SUCCESS) {
            return OG_ERROR;
        }
    }
    return OG_SUCCESS;
}

bool32 cm_dbs_is_enable_dbs(void)
{
    cm_dbs_cfg_s *cfg = cm_dbs_get_cfg();
    return cfg->enable;
}

uint32 cm_dbs_get_deploy_mode(void)
{
    cm_dbs_cfg_s *cfg = cm_dbs_get_cfg();
    return cfg->deploy_mode;
}

#ifndef DB_DEBUG_VERSION
static void exit_panic(void)
{
    OG_LOG_RUN_ERR("OGRACD EXIT!");
    cm_panic(0);
}
#endif

void cm_set_dbs_uuid_lsid(const char* uuid, uint32 lsid)
{
    dbs_global_handle()->dbs_client_set_uuid_lsid(uuid, lsid);
    OG_LOG_RUN_INF("set dbstor uuid %s and lsid %u", uuid, lsid);
    return;
}

status_t cm_dbs_init(const char *home_path, char *cfg_name, dbs_init_mode init_mode)
{
#ifndef DB_DEBUG_VERSION
    if (init_mode == DBS_RUN_CMS_SERVER) {
        atexit(exit_panic);
    }
#endif
    int32 ret;
    cm_dbs_cfg_s *cfg = cm_dbs_get_cfg();
    if (!cfg->enable) {
        OG_LOG_RUN_INF("DBStor is not enabled");
        return OG_SUCCESS;
    }
    char dbstor_work_path[OG_FILE_NAME_BUFFER_SIZE] = { 0 };
    int32 cnt = sprintf_s(dbstor_work_path, sizeof(dbstor_work_path), "%s/dbstor/", home_path);
    if (cnt == -1) {
        OG_LOG_RUN_ERR("Failed to assemble the dbstor work path by instance home(%s).", home_path);
        return OG_ERROR;
    }
    dbs_global_handle()->dbs_set_init_mode(init_mode);

    ret = dbs_global_handle()->dbs_client_lib_init(dbstor_work_path, cfg_name);
    if (ret != 0) {
        (void)dbs_global_handle()->dbs_client_flush_log();
        OG_LOG_RUN_ERR("Failed(%d) to init dbstor client at %s.", ret, dbstor_work_path);
        return OG_ERROR;
    }
    OG_LOG_RUN_INF("START WAIT DBSTOR INIT");
    cm_dbs_map_init();
    OG_LOG_RUN_INF("END WAIT DBSTOR INIT");
    return OG_SUCCESS;
}

status_t cm_dbs_iof_reg_all_ns(uint32 inst_id)
{
    int32 ret;
    cm_dbs_cfg_s *cfg = cm_dbs_get_cfg();
    iof_info_t iof = {0};

    if (!cfg->enable) {
        OG_LOG_RUN_INF("dbstor is not enabled");
        return OG_SUCCESS;
    }

    cm_dbs_get_ns_name(DEV_TYPE_PGPOOL, &iof.nsName);
    iof.nodeid = inst_id;
    iof.sn = 0;
    iof.termid = 0;
    ret = cm_dbs_iof_register(&iof);
    if (ret != OG_SUCCESS) {
        OG_LOG_RUN_ERR("Failed to iof reg, ret %d, node id %u, sn %llu, termid %u, namespace %s", ret,
            iof.nodeid, iof.sn, iof.termid, iof.nsName);
        return OG_ERROR;
    }

    cm_dbs_get_ns_name(DEV_TYPE_ULOG, &iof.nsName);
    ret = cm_dbs_iof_register(&iof);
    if (ret != OG_SUCCESS) {
        OG_LOG_RUN_ERR("Failed to iof reg, ret %d, node id %u, sn %llu, termid %u, namespace %s", ret,
            iof.nodeid, iof.sn, iof.termid, cfg->ns);
        return OG_ERROR;
    }

    OG_LOG_DEBUG_INF("iof reg ns succ, node id %u, sn %llu, termid %u, namespace %s, memory usage=%lu",
        iof.nodeid, iof.sn, iof.termid, cfg->ns, cm_print_memory_usage());
    return OG_SUCCESS;
}

uint32 cm_dbs_get_part_num(void)
{
    return g_dbs_cfg.partition_num;
}


bool32 cm_dbs_is_enable_batch_flush(void)
{
    cm_dbs_cfg_s *cfg = cm_dbs_get_cfg();
    return cfg->enable_batch_flush;
}