/*
 * Copyright (C) 2022-2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "idm_database.h"

#include "inttypes.h"
#include "securec.h"

#include "adaptor_algorithm.h"
#include "adaptor_log.h"
#include "adaptor_time.h"
#include "idm_file_manager.h"

#define MAX_DUPLICATE_CHECK 100
#define PRE_APPLY_NUM 5
#define MEM_GROWTH_FACTOR 2
#define MAX_CREDENTIAL_RETURN 5000

#ifdef IAM_TEST_ENABLE
#define IAM_STATIC
#else
#define IAM_STATIC static
#endif

// Caches IDM user information.
IAM_STATIC LinkedList *g_userInfoList = NULL;

// Caches the current user to reduce the number of user list traversal times.
IAM_STATIC UserInfo *g_currentUser = NULL;

// Caches global config info.
IAM_STATIC GlobalConfigParamHal g_globalConfigArray[MAX_GLOBAL_CONFIG_NUM];
IAM_STATIC uint32_t g_globalConfigInfoNum = 0;

typedef bool (*DuplicateCheckFunc)(LinkedList *collection, uint64_t value);

IAM_STATIC UserInfo *QueryUserInfo(int32_t userId);
IAM_STATIC ResultCode GetAllEnrolledInfoFromUser(UserInfo *userInfo, EnrolledInfoHal **enrolledInfos, uint32_t *num);
IAM_STATIC ResultCode DeleteUser(int32_t userId);
IAM_STATIC CredentialInfoHal *QueryCredentialById(uint64_t credentialId, LinkedList *credentialList);
IAM_STATIC CredentialInfoHal *QueryCredentialByAuthType(uint32_t authType, LinkedList *credentialList);
IAM_STATIC bool MatchCredentialById(const void *data, const void *condition);
IAM_STATIC ResultCode GenerateDeduplicateUint64(LinkedList *collection, uint64_t *destValue, DuplicateCheckFunc func);
IAM_STATIC bool IsUserValid(UserInfo *user);
IAM_STATIC ResultCode GetInvalidUser(int32_t *invalidUserId, uint32_t maxUserCount, uint32_t *userCount,
    bool *cachePinRemoved);
IAM_STATIC void RemoveCachePin(UserInfo *user, bool *isRemoved);
IAM_STATIC ResultCode ClearInvalidData(void);

ResultCode InitUserInfoList(void)
{
    LOG_INFO("InitUserInfoList start");
    if (g_userInfoList != NULL) {
        DestroyUserInfoList();
        g_userInfoList = NULL;
    }
    g_userInfoList = LoadFileInfo();
    if (g_userInfoList == NULL) {
        LOG_ERROR("load file info failed");
        return RESULT_NEED_INIT;
    }
    ResultCode ret = ClearInvalidData();
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("clear invalid user failed");
        DestroyUserInfoList();
        return ret;
    }
    ret = LoadGlobalConfigInfo(g_globalConfigArray, MAX_GLOBAL_CONFIG_NUM, &g_globalConfigInfoNum);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("load global config info failed");
    }
    LOG_INFO("InitUserInfoList end");
    return RESULT_SUCCESS;
}

void DestroyUserInfoList(void)
{
    DestroyLinkedList(g_userInfoList);
    g_userInfoList = NULL;
}

IAM_STATIC bool MatchUserInfo(const void *data, const void *condition)
{
    if (data == NULL || condition == NULL) {
        LOG_ERROR("please check invalid node");
        return false;
    }
    const UserInfo *userInfo = (const UserInfo *)data;
    int32_t userId = *(const int32_t *)condition;
    if (userInfo->userId == userId) {
        return true;
    }
    return false;
}

IAM_STATIC bool IsUserInfoValid(UserInfo *userInfo)
{
    if (userInfo == NULL) {
        LOG_ERROR("userInfo is null");
        return false;
    }
    if (userInfo->credentialInfoList == NULL) {
        LOG_ERROR("credentialInfoList is null");
        return false;
    }
    if (userInfo->enrolledInfoList == NULL) {
        LOG_ERROR("enrolledInfoList is null");
        return false;
    }
    return true;
}

ResultCode GetSecureUid(int32_t userId, uint64_t *secUid)
{
    if (secUid == NULL) {
        LOG_ERROR("secUid is null");
        return RESULT_BAD_PARAM;
    }
    UserInfo *user = QueryUserInfo(userId);
    if (user == NULL) {
        LOG_ERROR("can't find this user");
        return RESULT_NOT_FOUND;
    }
    *secUid = user->secUid;
    return RESULT_SUCCESS;
}

ResultCode GetEnrolledInfoAuthType(int32_t userId, uint32_t authType, EnrolledInfoHal *enrolledInfo)
{
    if (enrolledInfo == NULL) {
        LOG_ERROR("enrolledInfo is null");
        return RESULT_BAD_PARAM;
    }
    UserInfo *user = QueryUserInfo(userId);
    if (user == NULL) {
        LOG_ERROR("can't find this user");
        return RESULT_NOT_FOUND;
    }
    if (user->enrolledInfoList == NULL) {
        LOG_ERROR("enrolledInfoList is null");
        return RESULT_UNKNOWN;
    }

    LinkedListNode *temp = user->enrolledInfoList->head;
    while (temp != NULL) {
        EnrolledInfoHal *nodeInfo = temp->data;
        if (nodeInfo != NULL && nodeInfo->authType == authType) {
            *enrolledInfo = *nodeInfo;
            return RESULT_SUCCESS;
        }
        temp = temp->next;
    }

    return RESULT_NOT_FOUND;
}

ResultCode GetEnrolledInfo(int32_t userId, EnrolledInfoHal **enrolledInfos, uint32_t *num)
{
    if (enrolledInfos == NULL || num == NULL) {
        LOG_ERROR("param is invalid");
        return RESULT_BAD_PARAM;
    }
    UserInfo *user = QueryUserInfo(userId);
    if (!IsUserInfoValid(user)) {
        LOG_ERROR("can't find this user");
        return RESULT_NOT_FOUND;
    }
    return GetAllEnrolledInfoFromUser(user, enrolledInfos, num);
}

ResultCode DeleteUserInfo(int32_t userId, LinkedList **creds)
{
    if (creds == NULL) {
        LOG_ERROR("param is invalid");
        return RESULT_BAD_PARAM;
    }
    UserInfo *user = QueryUserInfo(userId);
    if (!IsUserInfoValid(user)) {
        LOG_ERROR("can't find this user");
        return RESULT_NOT_FOUND;
    }
    CredentialCondition condition = {};
    SetCredentialConditionUserId(&condition, userId);
    *creds = QueryCredentialLimit(&condition);
    if (*creds == NULL) {
        LOG_ERROR("query credential failed");
        return RESULT_UNKNOWN;
    }
    g_currentUser = NULL;

    ResultCode ret = DeleteUser(userId);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("deleteUser failed");
        DestroyLinkedList(*creds);
        *creds = NULL;
        return ret;
    }
    ret = UpdateFileInfo(g_userInfoList);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("update file info failed");
        DestroyLinkedList(*creds);
        *creds = NULL;
        return ret;
    }
    return ret;
}

IAM_STATIC UserInfo *QueryUserInfo(int32_t userId)
{
    UserInfo *user = g_currentUser;
    if (user != NULL && user->userId == userId) {
        return user;
    }
    if (g_userInfoList == NULL) {
        return NULL;
    }
    LinkedListNode *temp = g_userInfoList->head;
    while (temp != NULL) {
        user = (UserInfo *)temp->data;
        if (user != NULL && user->userId == userId) {
            break;
        }
        temp = temp->next;
    }
    if (temp == NULL) {
        return NULL;
    }
    if (IsUserInfoValid(user)) {
        g_currentUser = user;
        return user;
    }
    return NULL;
}

IAM_STATIC ResultCode GetAllEnrolledInfoFromUser(UserInfo *userInfo, EnrolledInfoHal **enrolledInfos, uint32_t *num)
{
    LinkedList *enrolledInfoList = userInfo->enrolledInfoList;
    uint32_t size = enrolledInfoList->getSize(enrolledInfoList);
    *enrolledInfos = Malloc(sizeof(EnrolledInfoHal) * size);
    if (*enrolledInfos == NULL) {
        LOG_ERROR("enrolledInfos malloc failed");
        return RESULT_NO_MEMORY;
    }
    (void)memset_s(*enrolledInfos, sizeof(EnrolledInfoHal) * size, 0, sizeof(EnrolledInfoHal) * size);
    LinkedListNode *temp = enrolledInfoList->head;
    ResultCode result = RESULT_SUCCESS;
    for (*num = 0; *num < size; (*num)++) {
        if (temp == NULL) {
            LOG_ERROR("temp node is null, something wrong");
            result = RESULT_BAD_PARAM;
            goto EXIT;
        }
        EnrolledInfoHal *tempInfo = (EnrolledInfoHal *)temp->data;
        if (memcpy_s(*enrolledInfos + *num, sizeof(EnrolledInfoHal) * (size - *num),
            tempInfo, sizeof(EnrolledInfoHal)) != EOK) {
            LOG_ERROR("copy the %u information failed", *num);
            result = RESULT_NO_MEMORY;
            goto EXIT;
        }
        temp = temp->next;
    }

EXIT:
    if (result != RESULT_SUCCESS) {
        Free(*enrolledInfos);
        *enrolledInfos = NULL;
        *num = 0;
    }
    return result;
}

IAM_STATIC bool IsSecureUidDuplicate(LinkedList *userInfoList, uint64_t secureUid)
{
    if (userInfoList == NULL) {
        LOG_ERROR("the user list is empty, and the branch is abnormal");
        return false;
    }

    LinkedListNode *temp = userInfoList->head;
    UserInfo *userInfo = NULL;
    while (temp != NULL) {
        userInfo = (UserInfo *)temp->data;
        if (userInfo != NULL && userInfo->secUid == secureUid) {
            return true;
        }
        temp = temp->next;
    }

    return false;
}

IAM_STATIC UserInfo *CreateUser(int32_t userId, int32_t userType)
{
    UserInfo *user = InitUserInfoNode();
    if (!IsUserInfoValid(user)) {
        LOG_ERROR("user is invalid");
        DestroyUserInfoNode(user);
        return NULL;
    }
    user->userId = userId;
    user->userType = userType;
    ResultCode ret = GenerateDeduplicateUint64(g_userInfoList, &user->secUid, IsSecureUidDuplicate);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("generate secureUid failed");
        DestroyUserInfoNode(user);
        return NULL;
    }
    return user;
}

IAM_STATIC ResultCode DeleteUser(int32_t userId)
{
    if (g_userInfoList == NULL) {
        return RESULT_BAD_PARAM;
    }
    return g_userInfoList->remove(g_userInfoList, &userId, MatchUserInfo, true);
}

IAM_STATIC bool IsCredentialIdDuplicate(LinkedList *userInfoList, uint64_t credentialId)
{
    (void)userInfoList;
    CredentialCondition condition = {};
    SetCredentialConditionCredentialId(&condition, credentialId);
    LinkedList *credList = QueryCredentialLimit(&condition);
    if (credList == NULL) {
        LOG_ERROR("query failed");
        return true;
    }
    if (credList->getSize(credList) != 0) {
        LOG_ERROR("duplicate credential id");
        DestroyLinkedList(credList);
        return true;
    }
    DestroyLinkedList(credList);
    return false;
}

IAM_STATIC bool IsEnrolledIdDuplicate(LinkedList *enrolledList, uint64_t enrolledId)
{
    LinkedListNode *temp = enrolledList->head;
    EnrolledInfoHal *enrolledInfo = NULL;
    const static uint16_t num = 0xFFFF;
    while (temp != NULL) {
        enrolledInfo = (EnrolledInfoHal *)temp->data;
        if ((enrolledInfo != NULL) && (enrolledInfo->enrolledId & num) == (enrolledId & num)) {
            return true;
        }
        temp = temp->next;
    }

    return false;
}

IAM_STATIC ResultCode GenerateDeduplicateUint64(LinkedList *collection, uint64_t *destValue, DuplicateCheckFunc func)
{
    if (collection == NULL || destValue == NULL || func == NULL) {
        LOG_ERROR("param is null");
        return RESULT_BAD_PARAM;
    }

    for (uint32_t i = 0; i < MAX_DUPLICATE_CHECK; ++i) {
        uint64_t tempRandom;
        if (SecureRandom((uint8_t *)&tempRandom, sizeof(uint64_t)) != RESULT_SUCCESS) {
            LOG_ERROR("get random failed");
            return RESULT_GENERAL_ERROR;
        }
        if (!func(collection, tempRandom)) {
            *destValue = tempRandom;
            return RESULT_SUCCESS;
        }
    }

    LOG_ERROR("generate random failed");
    return RESULT_GENERAL_ERROR;
}

IAM_STATIC ResultCode UpdateEnrolledId(LinkedList *enrolledList, uint32_t authType)
{
    LinkedListNode *temp = enrolledList->head;
    EnrolledInfoHal *enrolledInfo = NULL;
    while (temp != NULL) {
        EnrolledInfoHal *nodeData = (EnrolledInfoHal *)temp->data;
        if (nodeData != NULL && nodeData->authType == authType) {
            enrolledInfo = nodeData;
            break;
        }
        temp = temp->next;
    }

    if (enrolledInfo != NULL) {
        return GenerateDeduplicateUint64(enrolledList, &enrolledInfo->enrolledId, IsEnrolledIdDuplicate);
    }

    enrolledInfo = Malloc(sizeof(EnrolledInfoHal));
    if (enrolledInfo == NULL) {
        LOG_ERROR("enrolledInfo malloc failed");
        return RESULT_NO_MEMORY;
    }
    enrolledInfo->authType = authType;
    ResultCode ret = GenerateDeduplicateUint64(enrolledList, &enrolledInfo->enrolledId, IsEnrolledIdDuplicate);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("generate enrolledId failed");
        Free(enrolledInfo);
        return ret;
    }
    ret = enrolledList->insert(enrolledList, enrolledInfo);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("enrolledInfo insert failed");
        Free(enrolledInfo);
    }
    return ret;
}

// add for reliable pin updates
IAM_STATIC uint32_t GetRealAuthTypeForEnrolledId(uint32_t authType)
{
    if (authType == DEFAULT_AUTH_TYPE) {
        return PIN_AUTH;
    }
    return authType;
}

IAM_STATIC ResultCode AddCredentialToUser(UserInfo *user, CredentialInfoHal *credentialInfo)
{
    if (g_userInfoList == NULL) {
        LOG_ERROR("g_userInfoList is uninitialized");
        return RESULT_NEED_INIT;
    }
    LinkedList *credentialList = user->credentialInfoList;
    LinkedList *enrolledList = user->enrolledInfoList;
    if (credentialList->getSize(credentialList) >= MAX_CREDENTIAL) {
        LOG_ERROR("the number of credentials reaches the maximum");
        return RESULT_EXCEED_LIMIT;
    }

    ResultCode ret = UpdateEnrolledId(enrolledList, GetRealAuthTypeForEnrolledId(credentialInfo->authType));
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("update enrolledId failed");
        return ret;
    }
    ret = GenerateDeduplicateUint64(g_userInfoList, &credentialInfo->credentialId, IsCredentialIdDuplicate);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("GenerateDeduplicateUint64 failed");
        return ret;
    }
    if (credentialInfo->authType == DEFAULT_AUTH_TYPE) {
        bool isRemoved = false;
        RemoveCachePin(user, &isRemoved);
    }
    CredentialInfoHal *credential = Malloc(sizeof(CredentialInfoHal));
    if (credential == NULL) {
        LOG_ERROR("credential malloc failed");
        return RESULT_NO_MEMORY;
    }
    if (memcpy_s(credential, sizeof(CredentialInfoHal), credentialInfo, sizeof(CredentialInfoHal)) != EOK) {
        LOG_ERROR("credential copy failed");
        Free(credential);
        return RESULT_BAD_COPY;
    }
    credential->enrolledSysTime = GetReeTime();
    ret = credentialList->insert(credentialList, credential);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("insert credential failed");
        Free(credential);
    }
    return ret;
}

IAM_STATIC ResultCode AddUser(int32_t userId, CredentialInfoHal *credentialInfo, int32_t userType)
{
    if (g_userInfoList == NULL) {
        LOG_ERROR("please init");
        return RESULT_NEED_INIT;
    }
    if (g_userInfoList->getSize(g_userInfoList) >= MAX_USER) {
        LOG_ERROR("the number of users reaches the maximum");
        return RESULT_EXCEED_LIMIT;
    }

    UserInfo *user = QueryUserInfo(userId);
    if (user != NULL) {
        LOG_ERROR("Please check pin");
        return RESULT_BAD_PARAM;
    }

    user = CreateUser(userId, userType);
    if (user == NULL) {
        LOG_ERROR("create user failed");
        return RESULT_UNKNOWN;
    }
    LOG_INFO("user userType %{public}d", user->userType);
    ResultCode ret = AddCredentialToUser(user, credentialInfo);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("add credential to user failed");
        goto FAIL;
    }

    ret = g_userInfoList->insert(g_userInfoList, user);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("insert failed");
        goto FAIL;
    }
    return ret;

FAIL:
    DestroyUserInfoNode(user);
    return ret;
}

ResultCode AddCredentialInfo(int32_t userId, CredentialInfoHal *credentialInfo, int32_t userType)
{
    if ((credentialInfo == NULL) || (credentialInfo->authType == DEFAULT_AUTH_TYPE)) {
        LOG_ERROR("credentialInfo is invalid");
        return RESULT_BAD_PARAM;
    }
    UserInfo *user = QueryUserInfo(userId);
    if (user == NULL && credentialInfo->authType == PIN_AUTH) {
        ResultCode ret = AddUser(userId, credentialInfo, userType);
        if (ret != RESULT_SUCCESS) {
            LOG_ERROR("add user failed");
            return ret;
        }
        ret = UpdateFileInfo(g_userInfoList);
        if (ret != RESULT_SUCCESS) {
            LOG_ERROR("updateFileInfo failed");
        }
        return ret;
    }
    if (user == NULL) {
        LOG_ERROR("user is null");
        return RESULT_BAD_PARAM;
    }
    if (credentialInfo->authType == PIN_AUTH) {
        CredentialCondition condition = {};
        SetCredentialConditionAuthType(&condition, PIN_AUTH);
        SetCredentialConditionUserId(&condition, userId);
        LinkedList *credList = QueryCredentialLimit(&condition);
        if (credList == NULL) {
            LOG_ERROR("query credential failed");
            return RESULT_UNKNOWN;
        }
        if (credList->getSize(credList) != 0) {
            LOG_INFO("cache pin");
            // add for reliable pin updates
            credentialInfo->authType = DEFAULT_AUTH_TYPE;
        }
        DestroyLinkedList(credList);
    }
    ResultCode ret = AddCredentialToUser(user, credentialInfo);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("add credential to user failed");
        return ret;
    }
    ret = UpdateFileInfo(g_userInfoList);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("updateFileInfo failed");
    }
    return ret;
}

IAM_STATIC bool MatchCredentialById(const void *data, const void *condition)
{
    if (data == NULL || condition == NULL) {
        return false;
    }
    const CredentialInfoHal *credentialInfo = (const CredentialInfoHal *)data;
    uint64_t credentialId = *(const uint64_t *)condition;
    if (credentialInfo->credentialId == credentialId) {
        return true;
    }
    return false;
}

IAM_STATIC bool MatchEnrolledInfoByType(const void *data, const void *condition)
{
    if (data == NULL || condition == NULL) {
        return false;
    }
    const EnrolledInfoHal *enrolledInfo = (const EnrolledInfoHal *)data;
    uint32_t authType = *(const uint32_t *)condition;
    if (enrolledInfo->authType == authType) {
        return true;
    }
    return false;
}

IAM_STATIC void RemoveCachePin(UserInfo *user, bool *isRemoved)
{
    LOG_INFO("RemoveCachePin start");
    LinkedListNode *temp = user->credentialInfoList->head;
    CredentialInfoHal *credentialInfoCache = NULL;
    while (temp != NULL) {
        CredentialInfoHal *nodeData = (CredentialInfoHal*)temp->data;
        if (nodeData != NULL && nodeData->authType == DEFAULT_AUTH_TYPE) {
            credentialInfoCache = nodeData;
            break;
        }
        temp = temp->next;
    }
    if (credentialInfoCache == NULL) {
        LOG_INFO("RemoveCachePin no cache pin");
        *isRemoved = false;
        return;
    }
    uint64_t credentialId = credentialInfoCache->credentialId;
    ResultCode ret = (user->credentialInfoList)->remove(
        user->credentialInfoList, &credentialId, MatchCredentialById, true);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("remove credential failed");
        *isRemoved = false;
        return;
    }
    LOG_ERROR("remove credential success");
    *isRemoved = true;
}

void ClearCachePin(int32_t userId)
{
    LOG_INFO("ClearCachePin start");
    UserInfo *user = QueryUserInfo(userId);
    if (user == NULL) {
        LOG_ERROR("can't find this user");
        return;
    }

    bool isRemoved = false;
    RemoveCachePin(user, &isRemoved);
    if (isRemoved && UpdateFileInfo(g_userInfoList) != RESULT_SUCCESS) {
        LOG_ERROR("ClearCachePin save fail");
        return;
    }
}

IAM_STATIC void SwitchSubType(UserInfo *user)
{
    uint64_t tmpSubType = user->pinSubType;
    user->pinSubType = user->cachePinSubType;
    user->cachePinSubType = tmpSubType;
}

// add for reliable pin updates
IAM_STATIC ResultCode DeletePinCredentialInfo(UserInfo *user)
{
    LOG_INFO("DeletePinCredentialInfo start");
    LinkedListNode *temp = user->credentialInfoList->head;
    CredentialInfoHal *credentialInfoOld = NULL;
    CredentialInfoHal *credentialInfoCache = NULL;
    while (temp != NULL) {
        CredentialInfoHal *nodeData = (CredentialInfoHal*)temp->data;
        if (nodeData != NULL && nodeData->authType == PIN_AUTH) {
            credentialInfoOld = nodeData;
        }
        if (nodeData != NULL && nodeData->authType == DEFAULT_AUTH_TYPE) {
            credentialInfoCache = nodeData;
        }
        temp = temp->next;
    }
    if (credentialInfoOld == NULL || credentialInfoCache == NULL) {
        LOG_ERROR("no cache pin exist");
        return RESULT_GENERAL_ERROR;
    }
    credentialInfoOld->authType = DEFAULT_AUTH_TYPE;
    credentialInfoCache->authType = PIN_AUTH;
    SwitchSubType(user);
    ResultCode result = UpdateFileInfo(g_userInfoList);
    if (result == RESULT_SUCCESS) {
        LOG_INFO("switch cache pin success");
        ClearCachePin(user->userId);
        return result;
    }
    LOG_ERROR("switch cache pin fail");
    credentialInfoOld->authType = PIN_AUTH;
    credentialInfoCache->authType = DEFAULT_AUTH_TYPE;
    SwitchSubType(user);
    return RESULT_GENERAL_ERROR;
}

ResultCode DeleteCredentialInfo(int32_t userId, uint64_t credentialId, CredentialInfoHal *credentialInfo)
{
    if (credentialInfo == NULL) {
        LOG_ERROR("param is invalid");
        return RESULT_BAD_PARAM;
    }

    UserInfo *user = QueryUserInfo(userId);
    if (user == NULL) {
        LOG_ERROR("can't find this user");
        return RESULT_BAD_PARAM;
    }

    LinkedList *credentialList = user->credentialInfoList;
    CredentialInfoHal *credentialQuery = QueryCredentialById(credentialId, credentialList);
    if (credentialQuery == NULL) {
        LOG_ERROR("credentialQuery is null");
        return RESULT_UNKNOWN;
    }
    if (memcpy_s(credentialInfo, sizeof(CredentialInfoHal), credentialQuery, sizeof(CredentialInfoHal)) != EOK) {
        LOG_ERROR("copy failed");
        return RESULT_BAD_COPY;
    }
    if (credentialInfo->authType == PIN_AUTH) {
        return DeletePinCredentialInfo(user);
    }
    ResultCode ret = credentialList->remove(credentialList, &credentialId, MatchCredentialById, true);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("remove credential failed");
        return ret;
    }
    if (credentialInfo->authType == DEFAULT_AUTH_TYPE) {
        return UpdateFileInfo(g_userInfoList);
    }
    credentialQuery = QueryCredentialByAuthType(credentialInfo->authType, credentialList);
    if (credentialQuery != NULL) {
        return UpdateFileInfo(g_userInfoList);
    }

    LinkedList *enrolledInfoList = user->enrolledInfoList;
    if (enrolledInfoList == NULL) {
        LOG_ERROR("enrolledInfoList is null");
        return RESULT_UNKNOWN;
    }
    ret = enrolledInfoList->remove(enrolledInfoList, &credentialInfo->authType, MatchEnrolledInfoByType, true);
    if (ret != RESULT_SUCCESS) {
        LOG_ERROR("remove enrolledInfo failed");
        return ret;
    }

    return UpdateFileInfo(g_userInfoList);
}

IAM_STATIC CredentialInfoHal *QueryCredentialById(uint64_t credentialId, LinkedList *credentialList)
{
    if (credentialList == NULL) {
        return NULL;
    }
    LinkedListNode *temp = credentialList->head;
    CredentialInfoHal *credentialInfo = NULL;
    while (temp != NULL) {
        CredentialInfoHal *nodeData = (CredentialInfoHal *)temp->data;
        if (nodeData != NULL && nodeData->credentialId == credentialId) {
            credentialInfo = nodeData;
            break;
        }
        temp = temp->next;
    }
    return credentialInfo;
}

IAM_STATIC CredentialInfoHal *QueryCredentialByAuthType(uint32_t authType, LinkedList *credentialList)
{
    if (credentialList == NULL) {
        return NULL;
    }
    LinkedListNode *temp = credentialList->head;
    CredentialInfoHal *credentialInfo = NULL;
    while (temp != NULL) {
        CredentialInfoHal *nodeData = (CredentialInfoHal*)temp->data;
        if (nodeData != NULL && nodeData->authType == authType) {
            credentialInfo = nodeData;
            break;
        }
        temp = temp->next;
    }
    return credentialInfo;
}

// do not cotain cache pin credential
IAM_STATIC bool IsCredMatch(const CredentialCondition *limit, const CredentialInfoHal *credentialInfo)
{
    if ((limit->conditionFactor & CREDENTIAL_CONDITION_CREDENTIAL_ID) != 0 &&
        limit->credentialId != credentialInfo->credentialId) {
        return false;
    }
    if (credentialInfo->authType == DEFAULT_AUTH_TYPE) {
        if ((limit->conditionFactor & CREDENTIAL_CONDITION_NEED_CACHE_PIN) == 0) {
            return false;
        }
    } else {
        if ((limit->conditionFactor & CREDENTIAL_CONDITION_AUTH_TYPE) != 0 &&
            limit->authType != credentialInfo->authType) {
            return false;
        }
    }
    if ((limit->conditionFactor & CREDENTIAL_CONDITION_TEMPLATE_ID) != 0 &&
        limit->templateId != credentialInfo->templateId) {
        return false;
    }
    if ((limit->conditionFactor & CREDENTIAL_CONDITION_SENSOR_HINT) != 0 &&
        limit->executorSensorHint != INVALID_SENSOR_HINT &&
        limit->executorSensorHint != credentialInfo->executorSensorHint) {
        return false;
    }
    if ((limit->conditionFactor & CREDENTIAL_CONDITION_EXECUTOR_MATCHER) != 0 &&
        limit->executorMatcher != credentialInfo->executorMatcher) {
        return false;
    }
    return true;
}

IAM_STATIC bool IsUserMatch(const CredentialCondition *limit, const UserInfo *user)
{
    if ((limit->conditionFactor & CREDENTIAL_CONDITION_USER_ID) != 0 && limit->userId != user->userId) {
        return false;
    }
    return true;
}

// do not cotain cache pin credential
IAM_STATIC ResultCode TraverseCredentialList(const CredentialCondition *limit, const LinkedList *credentialList,
    LinkedList *credListGet)
{
    if (credentialList == NULL) {
        LOG_ERROR("credentialList is null");
        return RESULT_GENERAL_ERROR;
    }
    LinkedListNode *temp = credentialList->head;
    while (temp != NULL) {
        CredentialInfoHal *nodeData = (CredentialInfoHal*)temp->data;
        if (nodeData == NULL) {
            LOG_ERROR("nodeData is null");
            return RESULT_UNKNOWN;
        }
        if (!IsCredMatch(limit, nodeData)) {
            temp = temp->next;
            continue;
        }
        CredentialInfoHal *copy = (CredentialInfoHal *)Malloc(sizeof(CredentialInfoHal));
        if (copy == NULL) {
            LOG_ERROR("copy malloc failed");
            return RESULT_NO_MEMORY;
        }
        *copy = *nodeData;
        ResultCode ret = credListGet->insert(credListGet, copy);
        if (ret != RESULT_SUCCESS) {
            LOG_ERROR("insert failed");
            Free(copy);
            return ret;
        }
        temp = temp->next;
    }
    return RESULT_SUCCESS;
}

// do not cotain cache pin credential
LinkedList *QueryCredentialLimit(const CredentialCondition *limit)
{
    if (limit == NULL) {
        LOG_ERROR("limit is null");
        return NULL;
    }
    if (g_userInfoList == NULL) {
        LOG_ERROR("g_userInfoList is null");
        return NULL;
    }
    LinkedList *credList = CreateLinkedList(DestroyCredentialNode);
    if (credList == NULL) {
        LOG_ERROR("credList is null");
        return NULL;
    }
    LinkedListNode *temp = g_userInfoList->head;
    while (temp != NULL) {
        UserInfo *user = (UserInfo *)temp->data;
        if (user == NULL) {
            LOG_ERROR("node data is null");
            DestroyLinkedList(credList);
            return NULL;
        }
        if (IsUserMatch(limit, user)) {
            ResultCode ret = TraverseCredentialList(limit, user->credentialInfoList, credList);
            if (ret != RESULT_SUCCESS) {
                LOG_ERROR("TraverseCredentialList failed");
                DestroyLinkedList(credList);
                return NULL;
            }
        }
        temp = temp->next;
    }
    return credList;
}

ResultCode QueryCredentialUserId(uint64_t credentialId, int32_t *userId)
{
    if (userId == NULL) {
        LOG_ERROR("userId is null");
        return RESULT_BAD_PARAM;
    }
    if (g_userInfoList == NULL) {
        LOG_ERROR("g_userInfoList is null");
        return RESULT_NEED_INIT;
    }
    LinkedList *credList = CreateLinkedList(DestroyCredentialNode);
    if (credList == NULL) {
        LOG_ERROR("credList is null");
        return RESULT_NO_MEMORY;
    }
    LinkedListNode *temp = g_userInfoList->head;
    CredentialCondition condition = {};
    SetCredentialConditionCredentialId(&condition, credentialId);
    while (temp != NULL) {
        UserInfo *user = (UserInfo *)temp->data;
        if (user == NULL) {
            LOG_ERROR("user is null");
            DestroyLinkedList(credList);
            return RESULT_UNKNOWN;
        }
        ResultCode ret = TraverseCredentialList(&condition, user->credentialInfoList, credList);
        if (ret != RESULT_SUCCESS) {
            LOG_ERROR("TraverseCredentialList failed");
            DestroyLinkedList(credList);
            return RESULT_UNKNOWN;
        }
        if (credList->getSize(credList) != 0) {
            DestroyLinkedList(credList);
            *userId = user->userId;
            return RESULT_SUCCESS;
        }
        temp = temp->next;
    }
    DestroyLinkedList(credList);
    LOG_ERROR("can't find this credential");
    return RESULT_NOT_FOUND;
}

ResultCode SetPinSubType(int32_t userId, uint64_t pinSubType)
{
    UserInfo *user = QueryUserInfo(userId);
    if (user == NULL) {
        LOG_ERROR("can't find this user");
        return RESULT_NOT_FOUND;
    }
    user->pinSubType = pinSubType;
    return RESULT_SUCCESS;
}

ResultCode GetPinSubType(int32_t userId, uint64_t *pinSubType)
{
    if (pinSubType == NULL) {
        LOG_ERROR("pinSubType is null");
        return RESULT_BAD_PARAM;
    }
    UserInfo *user = QueryUserInfo(userId);
    if (user == NULL) {
        LOG_ERROR("can't find this user");
        return RESULT_NOT_FOUND;
    }
    *pinSubType = user->pinSubType;
    return RESULT_SUCCESS;
}

void SetCredentialConditionCredentialId(CredentialCondition *condition, uint64_t credentialId)
{
    if (condition == NULL) {
        LOG_ERROR("condition is null");
        return;
    }
    condition->credentialId = credentialId;
    condition->conditionFactor |= CREDENTIAL_CONDITION_CREDENTIAL_ID;
}

void SetCredentialConditionTemplateId(CredentialCondition *condition, uint64_t templateId)
{
    if (condition == NULL) {
        LOG_ERROR("condition is null");
        return;
    }
    condition->templateId = templateId;
    condition->conditionFactor |= CREDENTIAL_CONDITION_TEMPLATE_ID;
}

void SetCredentialConditionAuthType(CredentialCondition *condition, uint32_t authType)
{
    if (condition == NULL) {
        LOG_ERROR("condition is null");
        return;
    }
    condition->authType = authType;
    condition->conditionFactor |= CREDENTIAL_CONDITION_AUTH_TYPE;
}

void SetCredentialConditionExecutorSensorHint(CredentialCondition *condition, uint32_t executorSensorHint)
{
    if (condition == NULL) {
        LOG_ERROR("condition is null");
        return;
    }
    condition->executorSensorHint = executorSensorHint;
    condition->conditionFactor |= CREDENTIAL_CONDITION_SENSOR_HINT;
}

void SetCredentialConditionExecutorMatcher(CredentialCondition *condition, uint32_t executorMatcher)
{
    if (condition == NULL) {
        LOG_ERROR("condition is null");
        return;
    }
    condition->executorMatcher = executorMatcher;
    condition->conditionFactor |= CREDENTIAL_CONDITION_EXECUTOR_MATCHER;
}

void SetCredentialConditionUserId(CredentialCondition *condition, int32_t userId)
{
    if (condition == NULL) {
        LOG_ERROR("condition is null");
        return;
    }
    condition->userId = userId;
    condition->conditionFactor |= CREDENTIAL_CONDITION_USER_ID;
}

void SetCredentiaConditionNeedCachePin(CredentialCondition *condition)
{
    if (condition == NULL) {
        LOG_ERROR("condition is null");
        return;
    }
    condition->conditionFactor |= CREDENTIAL_CONDITION_NEED_CACHE_PIN;
}

IAM_STATIC bool IsUserValid(UserInfo *user)
{
    LinkedList *credentialInfoList = user->credentialInfoList;
    CredentialInfoHal *pinCredential = QueryCredentialByAuthType(PIN_AUTH, credentialInfoList);
    if (pinCredential == NULL) {
        LOG_INFO("user is invalid, userId: %{public}d", user->userId);
        return false;
    }
    return true;
}

IAM_STATIC ResultCode GetInvalidUser(int32_t *invalidUserId, uint32_t maxUserCount, uint32_t *userCount,
    bool *cachePinRemoved)
{
    LOG_INFO("get invalid user start");
    if (g_userInfoList == NULL) {
        LOG_ERROR("g_userInfoList is null");
        return RESULT_GENERAL_ERROR;
    }

    LinkedListIterator *iterator = g_userInfoList->createIterator(g_userInfoList);
    if (iterator == NULL) {
        LOG_ERROR("create iterator failed");
        return RESULT_NO_MEMORY;
    }

    UserInfo *user = NULL;
    *userCount = 0;
    while (iterator->hasNext(iterator)) {
        user = (UserInfo *)iterator->next(iterator);
        if (user == NULL) {
            LOG_ERROR("userinfo list node is null, please check");
            continue;
        }

        if (*userCount >= maxUserCount) {
            LOG_ERROR("too many users");
            break;
        }

        if (!IsUserValid(user)) {
            invalidUserId[*userCount] = user->userId;
            (*userCount)++;
            continue;
        }

        bool isRemoved = false;
        RemoveCachePin(user, &isRemoved);
        if (isRemoved) {
            *cachePinRemoved = true;
        }
    }

    g_userInfoList->destroyIterator(iterator);
    return RESULT_SUCCESS;
}

IAM_STATIC ResultCode ClearInvalidData(void)
{
    LOG_INFO("clear invalid user start");
    int32_t invalidUserId[MAX_USER] = {0};
    uint32_t userCount = 0;
    bool cachePinRemoved = false;
    if (GetInvalidUser(invalidUserId, MAX_USER, &userCount, &cachePinRemoved) != RESULT_SUCCESS) {
        LOG_ERROR("GetInvalidUser fail");
        return RESULT_GENERAL_ERROR;
    }
    ResultCode ret = RESULT_SUCCESS;
    for (uint32_t i = 0; i < userCount; ++i) {
        ret = DeleteUser(invalidUserId[i]);
        if (ret != RESULT_SUCCESS) {
            LOG_ERROR("delete invalid user fail, userId: %{public}d, ret: %{public}d", invalidUserId[i], ret);
            return ret;
        }
        LOG_INFO("delete invalid user success, userId: %{public}d, ret: %{public}d", invalidUserId[i], ret);
    }

    if ((userCount != 0) || cachePinRemoved) {
        ret = UpdateFileInfo(g_userInfoList);
        if (ret != RESULT_SUCCESS) {
            LOG_ERROR("UpdateFileInfo fail, ret: %{public}d", ret);
            return ret;
        }
    }
    return RESULT_SUCCESS;
}

ResultCode GetAllExtUserInfo(UserInfoResult *userInfos, uint32_t userInfoLen, uint32_t *userInfocount)
{
    LOG_INFO("get all user info start");
    if (userInfos == NULL || userInfocount == NULL || g_userInfoList == NULL) {
        LOG_ERROR("param is null");
        return RESULT_BAD_PARAM;
    }

    LinkedListIterator *iterator = g_userInfoList->createIterator(g_userInfoList);
    if (iterator == NULL) {
        LOG_ERROR("create iterator failed");
        return RESULT_NO_MEMORY;
    }

    UserInfo *user = NULL;
    EnrolledInfoHal *enrolledInfoHal = NULL;
    *userInfocount = 0;
    while (iterator->hasNext(iterator)) {
        user = (UserInfo *)iterator->next(iterator);
        if (user == NULL) {
            LOG_ERROR("userinfo list node is null, please check");
            continue;
        }

        if (*userInfocount >= userInfoLen) {
            LOG_ERROR("too many users");
            goto ERROR;
        }

        userInfos[*userInfocount].userId = user->userId;
        userInfos[*userInfocount].secUid = user->secUid;
        userInfos[*userInfocount].pinSubType = (uint32_t)user->pinSubType;

        ResultCode ret = GetAllEnrolledInfoFromUser(user, &enrolledInfoHal, &(userInfos[*userInfocount].enrollNum));
        if (ret != RESULT_SUCCESS) {
            LOG_ERROR("get enrolled info fail");
            goto ERROR;
        }
        if (userInfos[*userInfocount].enrollNum > MAX_ENROLL_OUTPUT) {
            LOG_ERROR("too many elements");
            free(enrolledInfoHal);
            goto ERROR;
        }

        for (uint32_t i = 0; i < userInfos[*userInfocount].enrollNum; i++) {
            userInfos[*userInfocount].enrolledInfo[i].authType = enrolledInfoHal[i].authType;
            userInfos[*userInfocount].enrolledInfo[i].enrolledId = enrolledInfoHal[i].enrolledId;
        }

        free(enrolledInfoHal);
        (*userInfocount)++;
    }

    g_userInfoList->destroyIterator(iterator);
    return RESULT_SUCCESS;

ERROR:
    g_userInfoList->destroyIterator(iterator);
    return RESULT_GENERAL_ERROR;
}

ResultCode GetEnrolledState(int32_t userId, uint32_t authType, EnrolledStateHal *enrolledStateHal)
{
    LOG_INFO("get enrolled id info start, userId:%{public}d, authType:%{public}d", userId, authType);

    UserInfo *user = QueryUserInfo(userId);
    if (user == NULL) {
        LOG_ERROR("user is null");
        return RESULT_NOT_ENROLLED;
    }
    if (user->credentialInfoList == NULL) {
        LOG_ERROR("credentialInfoList is null");
        return RESULT_NOT_ENROLLED;
    }

    uint16_t credentialCount = 0;
    LinkedListNode *credentialInfoTemp = user->credentialInfoList->head;
    while (credentialInfoTemp != NULL) {
        CredentialInfoHal *nodeInfo = credentialInfoTemp->data;
        if (nodeInfo != NULL && nodeInfo->authType == authType) {
            ++credentialCount;
        }
        credentialInfoTemp = credentialInfoTemp->next;
    }
    if (credentialCount == 0) {
        LOG_ERROR("not enrolled");
        return RESULT_NOT_ENROLLED;
    }
    enrolledStateHal->credentialCount = credentialCount;
    if (user->enrolledInfoList == NULL) {
        LOG_ERROR("enrolledInfoList is null");
        return RESULT_NOT_ENROLLED;
    }
    LinkedListNode *enrolledInfoTemp = user->enrolledInfoList->head;
    while (enrolledInfoTemp != NULL) {
        EnrolledInfoHal *nodeInfo = enrolledInfoTemp->data;
        if (nodeInfo != NULL && nodeInfo->authType == authType) {
            enrolledStateHal->credentialDigest = nodeInfo->enrolledId;
            break;
        }
        enrolledInfoTemp = enrolledInfoTemp->next;
    }
    return RESULT_SUCCESS;
}

IAM_STATIC ResultCode SavePinExpiredPeriod(int64_t pinExpiredPeriod)
{
    if (pinExpiredPeriod < 0) {
        pinExpiredPeriod = NO_CHECK_PIN_EXPIRED_PERIOD;
    }
    for (uint32_t i = 0; i < g_globalConfigInfoNum; i++) {
        if (g_globalConfigArray[i].type == PIN_EXPIRED_PERIOD) {
            g_globalConfigArray[i].value.pinExpiredPeriod = pinExpiredPeriod;
            return UpdateGlobalConfigFile(g_globalConfigArray, g_globalConfigInfoNum);
        }
    }
    if (g_globalConfigInfoNum < MAX_GLOBAL_CONFIG_NUM) {
        g_globalConfigInfoNum++;
        g_globalConfigArray[g_globalConfigInfoNum - 1].type = PIN_EXPIRED_PERIOD;
        g_globalConfigArray[g_globalConfigInfoNum - 1].value.pinExpiredPeriod = pinExpiredPeriod;
        return UpdateGlobalConfigFile(g_globalConfigArray, g_globalConfigInfoNum);
    }
    LOG_ERROR("SavePinExpiredPeriod failed");
    return RESULT_GENERAL_ERROR;
}


ResultCode SaveGlobalConfigParam(GlobalConfigParamHal *param)
{
    if (param == NULL) {
        LOG_ERROR("bad param");
        return RESULT_BAD_PARAM;
    }
    if (param->type == PIN_EXPIRED_PERIOD) {
        return SavePinExpiredPeriod(param->value.pinExpiredPeriod);
    }
    LOG_ERROR("SaveGlobalConfigParam type %{public}d failed", param->type);
    return RESULT_GENERAL_ERROR;
}

IAM_STATIC ResultCode QueryPinCredential(int32_t userId, CredentialInfoHal *pinCredential)
{
    if (pinCredential == NULL) {
        LOG_ERROR("no need to get pin credential");
        return RESULT_BAD_PARAM;
    }
    CredentialCondition condition = {};
    SetCredentialConditionUserId(&condition, userId);
    SetCredentialConditionAuthType(&condition, PIN_AUTH);
    LinkedList *credList = QueryCredentialLimit(&condition);
    if (credList == NULL || credList->getSize(credList) == 0) {
        LOG_ERROR("pin credential is null");
        DestroyLinkedList(credList);
        return RESULT_NOT_ENROLLED;
    }
    if (credList->head == NULL || credList->head->data == NULL) {
        LOG_ERROR("pin credList node is invalid");
        DestroyLinkedList(credList);
        return RESULT_GENERAL_ERROR;
    }
    if (memcpy_s(pinCredential, sizeof(CredentialInfoHal), credList->head->data, sizeof(CredentialInfoHal)) != EOK) {
        LOG_ERROR("credential copy fail");
        DestroyLinkedList(credList);
        return RESULT_GENERAL_ERROR;
    }
    DestroyLinkedList(credList);
    return RESULT_SUCCESS;
}

ResultCode GetPinExpiredInfo(int32_t userId, PinExpiredInfo *expiredInfo)
{
    if (expiredInfo == NULL) {
        LOG_ERROR("bad param");
        return RESULT_BAD_PARAM;
    }
    (void)memset_s(expiredInfo, sizeof(PinExpiredInfo), 0, sizeof(PinExpiredInfo));
    for (uint32_t i = 0; i < g_globalConfigInfoNum; i++) {
        if (g_globalConfigArray[i].type == PIN_EXPIRED_PERIOD) {
            expiredInfo->pinExpiredPeriod = g_globalConfigArray[i].value.pinExpiredPeriod;
            break;
        }
    }
    if (expiredInfo->pinExpiredPeriod <= 0) {
        expiredInfo->pinExpiredPeriod = NO_CHECK_PIN_EXPIRED_PERIOD;
        LOG_INFO("no need check pinExpiredPeriod");
        return RESULT_SUCCESS;
    }
    CredentialInfoHal pinCredential = {};
    if (QueryPinCredential(userId, &pinCredential) != RESULT_SUCCESS) {
        LOG_INFO("not enrolled pin");
        return RESULT_NOT_ENROLLED;
    }
    expiredInfo->pinEnrolledSysTime = pinCredential.enrolledSysTime;
    return RESULT_SUCCESS;
}