/*
* Copyright (c) 2022 Huawei Technologies Co.,Ltd.
*
* CM 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.
* -------------------------------------------------------------------------
*
* cma_status_check_res.cpp
*    cma send res isreg and status check msg
*
* IDENTIFICATION
*    src/cm_agent/cma_status_check_res.cpp
*
* -------------------------------------------------------------------------
*/
#include <cjson/cJSON.h>
#include "cma_common.h"
#include "cma_global_params.h"
#include "cma_instance_management_res.h"

typedef struct ResIsregCheckListSt {
    uint32 checkList[CM_MAX_RES_INST_COUNT];  // cmInstId
    uint32 checkCount;
} ResIsregCheckList;

typedef struct OneNodeResIsregInfoSt {
    pthread_rwlock_t rwlock;
    CmaToCmsIsregMsg isreg;
} OneNodeResIsregInfo;

static ResIsregCheckList g_isregCheckList;
static OneNodeResIsregInfo g_isregStatus;

void InitIsregCheckVar()
{
    errno_t rc = memset_s(&g_isregStatus, sizeof(OneNodeResIsregInfo), 0, sizeof(OneNodeResIsregInfo));
    securec_check_errno(rc, (void)rc);
    rc = memset_s(&g_isregCheckList, sizeof(ResIsregCheckList), 0, sizeof(ResIsregCheckList));
    securec_check_errno(rc, (void)rc);
    g_isregCheckList.checkCount = GetLocalResConfCount();
    for (uint32 i = 0; i < GetLocalResConfCount(); ++i) {
        g_isregCheckList.checkList[i] = g_resConf[i].cmInstanceId;
    }
    (void)pthread_rwlock_init(&g_isregStatus.rwlock, NULL);
    g_isregStatus.isreg.msgType = (int)MSG_AGENT_CM_ISREG_REPORT;
    g_isregStatus.isreg.nodeId = g_currentNode->node;
}

static int GetResInstId(const char *resName, uint32 cmInstId)
{
    for (uint32 i = 0; i < CusResCount(); ++i) {
        if (strcmp(resName, g_resStatus[i].status.resName) != 0) {
            continue;
        }
        for (uint32 j = 0; j < g_resStatus[i].status.instanceCount; ++j) {
            if (g_resStatus[i].status.resStat[j].cmInstanceId == cmInstId) {
                return (int)g_resStatus[i].status.resStat[j].resInstanceId;
            }
        }
        write_runlog(ERROR, "can't get res_inst_id, by cm_inst_id(%u).\n", cmInstId);
        break;
    }
    write_runlog(ERROR, "can't get res_inst_id, by res(%s) cm_inst_id(%u).\n", resName, cmInstId);
    return -1;
}

static void CheckIsregList(ResInstIsreg *newIsregList, uint32 listLen, uint32 *newIsregCount)
{
    (*newIsregCount) = 0;
    for (uint32 i = 0; i < g_isregCheckList.checkCount; ++i) {
        if (i >= listLen) {
            write_runlog(ERROR, "check list check count(%u) is invalid.\n", g_isregCheckList.checkCount);
            return;
        }
        char resName[CM_MAX_RES_NAME] = {0};
        if (GetResNameByCmInstId(g_isregCheckList.checkList[i], resName, CM_MAX_RES_NAME) != CM_SUCCESS) {
            continue;
        }
        CmResConfList *resConf = CmaGetResConfByResName(resName);
        if (resConf == NULL) {
            continue;
        }
        int destInstId = GetResInstId(resName, g_isregCheckList.checkList[i]);
        if (destInstId < 0) {
            continue;
        }
        newIsregList[(*newIsregCount)].cmInstId = g_isregCheckList.checkList[i];
        newIsregList[(*newIsregCount)].isreg = (int)IsregOneResInst(resConf, (uint32)destInstId);
        ++(*newIsregCount);
    }
}

static void StrcatIsregListStr(char *isregStr, uint32 isregStrLen, uint32 cmInstId, int isreg)
{
    const uint32 instIsregLen = 64;
    char instStr[instIsregLen] = {0};
    int ret = snprintf_s(instStr, instIsregLen, instIsregLen - 1, "%u:%s, ", cmInstId, GetIsregStatus(isreg));
    securec_check_intval(ret, (void)ret);
    errno_t rc = strcat_s(isregStr, isregStrLen, instStr);
    securec_check_errno(rc, (void)rc);
}

static void PrintIsregList(const ResInstIsreg *newIsregList, uint32 listLen, int logLevel)
{
    if (log_min_messages > logLevel) {
        return;
    }
    char isregList[MAX_PATH_LEN] = {0};
    for (uint32 i = 0; i < listLen; ++i) {
        StrcatIsregListStr(isregList, MAX_PATH_LEN, newIsregList[i].cmInstId, newIsregList[i].isreg);
    }
    write_runlog(logLevel, "node(%u) isreg list: %s.\n", g_currentNode->node, isregList);
}

static void CopyNewIsregStatus(const ResInstIsreg *newIsregList, uint32 newListLen, uint32 newIsregCount)
{
    (void)pthread_rwlock_wrlock(&g_isregStatus.rwlock);
    g_isregStatus.isreg.isregCount = newIsregCount;
    errno_t rc = memcpy_s(g_isregStatus.isreg.isregList, (sizeof(ResInstIsreg) * CM_MAX_RES_INST_COUNT),
        newIsregList, (sizeof(ResInstIsreg) * newListLen));
    (void)pthread_rwlock_unlock(&g_isregStatus.rwlock);
    securec_check_errno(rc, (void)rc);
}

void *ResourceIsregCheckMain(void *arg)
{
    thread_name = "ResIsregCheck";
    write_runlog(LOG, "resource isreg check thread start.\n");

    ResInstIsreg newIsregList[CM_MAX_RES_INST_COUNT] = {{0}};

    for (;;) {
        if (g_shutdownRequest) {
            cm_sleep(5);
            continue;
        }

        uint32 newIsregCount = 0;
        errno_t rc = memset_s(newIsregList, (sizeof(ResInstIsreg) * CM_MAX_RES_INST_COUNT),
            0, (sizeof(ResInstIsreg) * CM_MAX_RES_INST_COUNT));
        securec_check_errno(rc, (void)rc);

        CheckIsregList(newIsregList, CM_MAX_RES_INST_COUNT, &newIsregCount);

        PrintIsregList(newIsregList, newIsregCount, DEBUG5);

        CopyNewIsregStatus(newIsregList, CM_MAX_RES_INST_COUNT, newIsregCount);

        cm_sleep(agent_report_interval);
    }

    return NULL;
}

static void PrintItemCheckList(char *checkListStr, uint32 listLen, uint32 cmInstId)
{
    const uint32 itemLen = 32;
    char itemStr[itemLen] = {0};
    int ret = snprintf_s(itemStr, itemLen, itemLen - 1, "%u, ", cmInstId);
    securec_check_intval(ret, (void)ret);
    errno_t rc = strcat_s(checkListStr, listLen, itemStr);
    securec_check_errno(rc, (void)rc);
}

static void PrintCheckList(const uint32 *checkList, uint32 Len, int logLevel)
{
    char checkListStr[MAX_PATH_LEN] = {0};
    for (uint32 i = 0; i < Len; ++i) {
        PrintItemCheckList(checkListStr, MAX_PATH_LEN, checkList[i]);
    }

    write_runlog(logLevel, "check list: %s.\n", checkListStr);
}

void UpdateIsregCheckList(const uint32 *newCheckList, uint32 newCheckCount)
{
    if (newCheckCount > CM_MAX_RES_INST_COUNT) {
        write_runlog(ERROR, "new check list count(%u) is invalid.\n", newCheckCount);
        return;
    }

    write_runlog(LOG, "print old isreg check list.\n");
    PrintCheckList(g_isregCheckList.checkList, g_isregCheckList.checkCount, LOG);
    write_runlog(LOG, "print new isreg check list.\n");
    PrintCheckList(newCheckList, newCheckCount, LOG);

    size_t checkListSize = sizeof(uint32) * CM_MAX_RES_INST_COUNT;
    size_t newCheckListSize = sizeof(uint32) * newCheckCount;
    errno_t rc = memset_s(g_isregCheckList.checkList, checkListSize, 0, checkListSize);
    securec_check_errno(rc, (void)rc);
    rc = memcpy_s(g_isregCheckList.checkList, checkListSize, newCheckList, newCheckListSize);
    securec_check_errno(rc, (void)rc);

    g_isregCheckList.checkCount = newCheckCount;
}

void SendResIsregReportMsg()
{
    (void)pthread_rwlock_rdlock(&g_isregStatus.rwlock);
    PrintIsregList(g_isregStatus.isreg.isregList, g_isregStatus.isreg.isregCount, DEBUG5);
    PushMsgToCmsSendQue((char *)&g_isregStatus.isreg, (uint32)sizeof(CmaToCmsIsregMsg), "res isreg");
    (void)pthread_rwlock_unlock(&g_isregStatus.rwlock);
}

// check res status
static void DoCheckResourceStatus(CmResConfList *resConf, CmResourceStatus *resStat)
{
    long curTime = GetCurMonotonicTimeSec();
    static uint32 latestStat = (uint32)CUS_RES_CHECK_STAT_UNKNOWN;
    if (resConf->checkInfo.checkTime == 0) {
        resStat->status = (uint32)CheckOneResInst(resConf);
        resConf->checkInfo.checkTime = curTime;
        latestStat = resStat->status;
        return;
    }
    if ((curTime - resConf->checkInfo.checkTime) < resConf->checkInfo.checkInterval) {
        resStat->status = latestStat;
        return;
    }
    resStat->status = (uint32)CheckOneResInst(resConf);
    resConf->checkInfo.checkTime = curTime;
    latestStat = resStat->status;
}

void InitResStatCommInfo(OneNodeResourceStatus *nodeStat)
{
    nodeStat->node = g_currentNode->node;
    for (uint32 i = 0; i < GetLocalResConfCount(); ++i) {
        errno_t rc = strcpy_s(nodeStat->status[i].resName, CM_MAX_RES_NAME, g_resConf[i].resName);
        securec_check_errno(rc, (void)rc);
        nodeStat->status[i].nodeId = g_resConf[i].nodeId;
        nodeStat->status[i].cmInstanceId = g_resConf[i].cmInstanceId;
        nodeStat->status[i].resInstanceId = g_resConf[i].resInstanceId;
        nodeStat->status[i].status = CUS_RES_CHECK_STAT_UNKNOWN;
        nodeStat->status[i].workStatus = RES_INST_WORK_STATUS_UNKNOWN;
    }
    nodeStat->count = GetLocalResConfCount();
}

void CheckResourceState(OneNodeResourceStatus *nodeStat)
{
    for (uint32 i = 0; i < GetLocalResConfCount(); ++i) {
        DoCheckResourceStatus(&g_resConf[i], &nodeStat->status[i]);
        bool isInstStopped = IsInstManualStopped(nodeStat->status[i].cmInstanceId);
        nodeStat->status[i].workStatus = (isInstStopped ? RES_INST_WORK_STATUS_UNAVAIL : RES_INST_WORK_STATUS_AVAIL);
    }
}

void *ResourceStatusCheckMain(void *arg)
{
    errno_t rc;
    OneNodeResourceStatus nodeStat = {0};

    InitResStatCommInfo(&nodeStat);

    thread_name = "ResStatCheck";
    write_runlog(LOG, "Resource status check thread start.\n");

    for (;;) {
        if (g_shutdownRequest) {
            cm_sleep(5);
            continue;
        }

        CheckResourceState(&nodeStat);

        (void)pthread_rwlock_wrlock(&g_resReportMsg.rwlock);
        rc = memcpy_s(&g_resReportMsg.resStat, sizeof(OneNodeResourceStatus), &nodeStat, sizeof(OneNodeResourceStatus));
        securec_check_errno(rc, (void)pthread_rwlock_unlock(&g_resReportMsg.rwlock));
        (void)pthread_rwlock_unlock(&g_resReportMsg.rwlock);

        cm_sleep(agent_report_interval);
    }

    return NULL;
}

void SendResStatReportMsg()
{
    ReportResStatus reportMsg = {0};
    reportMsg.msgType = (int)MSG_AGENT_CM_RESOURCE_STATUS;

    (void)pthread_rwlock_rdlock(&g_resReportMsg.rwlock);
    errno_t rc = memcpy_s(&reportMsg.nodeStat, sizeof(OneNodeResourceStatus),
        &g_resReportMsg.resStat, sizeof(OneNodeResourceStatus));
    securec_check_errno(rc, (void)pthread_rwlock_unlock(&g_resReportMsg.rwlock));
    (void)pthread_rwlock_unlock(&g_resReportMsg.rwlock);

    PushMsgToCmsSendQue((char *)&reportMsg, (uint32)sizeof(ReportResStatus), "res status");
}