/*
 * Copyright (c) 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 "dm_softbus_cache.h"
#include "dm_anonymous.h"
#include "dm_crypto.h"
#include "dm_constants.h"
#include "dm_device_info.h"
#include "dm_log.h"
namespace OHOS {
namespace DistributedHardware {
DM_IMPLEMENT_SINGLE_INSTANCE(SoftbusCache);
bool g_online = false;
bool g_getLocalDevInfo = false;
DmDeviceInfo localDeviceInfo_;
std::mutex localDevInfoMutex_;
void SoftbusCache::SaveLocalDeviceInfo()
{
    LOGI("SoftbusCache::SaveLocalDeviceInfo");
    std::lock_guard<std::mutex> mutexLock(localDevInfoMutex_);
    if (g_online) {
        return;
    }
    NodeBasicInfo nodeBasicInfo;
    int32_t ret = GetLocalNodeDeviceInfo(DM_PKG_NAME, &nodeBasicInfo);
    if (ret != DM_OK) {
        LOGE("[SOFTBUS]GetLocalNodeDeviceInfo failed, ret: %{public}d.", ret);
        return;
    }
    ConvertNodeBasicInfoToDmDevice(nodeBasicInfo, localDeviceInfo_);
    LOGI("SoftbusCache::SaveLocalDeviceInfo networkid %{public}s.",
        GetAnonyString(std::string(localDeviceInfo_.networkId)).c_str());
    SaveDeviceInfo(localDeviceInfo_);
    SaveDeviceSecurityLevel(localDeviceInfo_.networkId);
    g_online = true;
    g_getLocalDevInfo = true;
}

void SoftbusCache::DeleteLocalDeviceInfo()
{
    LOGI("SoftbusCache::DeleteLocalDeviceInfo networkid %{public}s.",
        GetAnonyString(std::string(localDeviceInfo_.networkId)).c_str());
    std::lock_guard<std::mutex> mutexLock(localDevInfoMutex_);
    g_online = false;
    g_getLocalDevInfo = false;
}

int32_t SoftbusCache::GetLocalDeviceInfo(DmDeviceInfo &nodeInfo)
{
    std::lock_guard<std::mutex> mutexLock(localDevInfoMutex_);
    if (g_getLocalDevInfo) {
        nodeInfo = localDeviceInfo_;
        LOGI("SoftbusCache::GetLocalDeviceInfo from dm cache.");
        return DM_OK;
    }
    NodeBasicInfo nodeBasicInfo;
    int32_t ret = GetLocalNodeDeviceInfo(DM_PKG_NAME, &nodeBasicInfo);
    if (ret != DM_OK) {
        LOGE("[SOFTBUS]GetLocalNodeDeviceInfo failed, ret: %{public}d.", ret);
        return ret;
    }
    ConvertNodeBasicInfoToDmDevice(nodeBasicInfo, localDeviceInfo_);
    nodeInfo = localDeviceInfo_;
    SaveDeviceInfo(localDeviceInfo_);
    SaveDeviceSecurityLevel(localDeviceInfo_.networkId);
    g_getLocalDevInfo = true;
    LOGI("SoftbusCache::GetLocalDeviceInfo from softbus.");
    return DM_OK;
}

void SoftbusCache::UpDataLocalDevInfo()
{
    LOGI("SoftbusCache::UpDataLocalDevInfo");
    NodeBasicInfo nodeBasicInfo;
    int32_t ret = GetLocalNodeDeviceInfo(DM_PKG_NAME, &nodeBasicInfo);
    if (ret != DM_OK) {
        LOGE("[SOFTBUS]GetLocalNodeDeviceInfo failed, ret: %{public}d.", ret);
        return;
    }
    std::lock_guard<std::mutex> mutexLock(localDevInfoMutex_);
    ConvertNodeBasicInfoToDmDevice(nodeBasicInfo, localDeviceInfo_);
    ChangeDeviceInfo(localDeviceInfo_);
}

int32_t SoftbusCache::GetUdidByNetworkId(const char *networkId, std::string &udid)
{
    uint8_t mUdid[UDID_BUF_LEN] = {0};
    int32_t ret = GetNodeKeyInfo(DM_PKG_NAME, networkId, NodeDeviceInfoKey::NODE_KEY_UDID, mUdid, sizeof(mUdid));
    if (ret != DM_OK) {
        LOGE("[SOFTBUS]GetNodeKeyInfo failed, ret: %{public}d.", ret);
        return ret;
    }
    udid = reinterpret_cast<char *>(mUdid);
    return ret;
}

int32_t SoftbusCache::GetUuidByNetworkId(const char *networkId, std::string &uuid)
{
    uint8_t mUuid[UUID_BUF_LEN] = {0};
    int32_t ret = GetNodeKeyInfo(DM_PKG_NAME, networkId, NodeDeviceInfoKey::NODE_KEY_UUID, mUuid, sizeof(mUuid));
    if (ret != DM_OK) {
        LOGE("[SOFTBUS]GetNodeKeyInfo failed, ret: %{public}d.", ret);
        return ret;
    }
    uuid = reinterpret_cast<char *>(mUuid);
    return ret;
}

void SoftbusCache::SaveDeviceInfo(DmDeviceInfo deviceInfo)
{
    LOGI("SoftbusCache::SaveDeviceInfo");
    std::string udid = "";
    std::string uuid = "";
    GetUdidByNetworkId(deviceInfo.networkId, udid);
    GetUuidByNetworkId(deviceInfo.networkId, uuid);
    char udidHash[DM_MAX_DEVICE_ID_LEN] = {0};
    if (Crypto::GetUdidHash(udid, reinterpret_cast<uint8_t *>(udidHash)) != DM_OK) {
        LOGE("get udidhash by udid: %{public}s failed.", GetAnonyString(udid).c_str());
        return;
    }
    if (memcpy_s(deviceInfo.deviceId, sizeof(deviceInfo.deviceId), udidHash,
        std::min(sizeof(deviceInfo.deviceId), sizeof(udidHash))) != DM_OK) {
        LOGE("SaveDeviceInfo copy deviceId failed.");
        return;
    }
    std::lock_guard<std::mutex> mutexLock(deviceInfosMutex_);
    deviceInfo_[udid] = std::pair<std::string, DmDeviceInfo>(uuid, deviceInfo);
    LOGI("SaveDeviceInfo success udid %{public}s, networkId %{public}s",
        GetAnonyString(udid).c_str(), GetAnonyString(std::string(deviceInfo.networkId)).c_str());
}

void SoftbusCache::DeleteDeviceInfo(const DmDeviceInfo &nodeInfo)
{
    LOGI("SoftbusCache::DeleteDeviceInfo networkId %{public}s",
        GetAnonyString(std::string(nodeInfo.networkId)).c_str());
    std::lock_guard<std::mutex> mutexLock(deviceInfosMutex_);
    for (const auto &item : deviceInfo_) {
        if (std::string(item.second.second.networkId) == std::string(nodeInfo.networkId)) {
            LOGI("DeleteDeviceInfo success udid %{public}s", GetAnonyString(item.first).c_str());
            deviceInfo_.erase(item.first);
            break;
        }
    }
}

void SoftbusCache::ChangeDeviceInfo(const DmDeviceInfo deviceInfo)
{
    LOGI("SoftbusCache::ChangeDeviceInfo");
    std::string udid = "";
    GetUdidByNetworkId(deviceInfo.networkId, udid);
    std::lock_guard<std::mutex> mutexLock(deviceInfosMutex_);
    if (deviceInfo_.find(udid) != deviceInfo_.end()) {
        if (memcpy_s(deviceInfo_[udid].second.deviceName, sizeof(deviceInfo_[udid].second.deviceName),
            deviceInfo.deviceName, sizeof(deviceInfo.deviceName)) != DM_OK) {
            LOGE("ChangeDeviceInfo deviceInfo copy deviceName failed");
        }
        if (memcpy_s(deviceInfo_[udid].second.networkId, sizeof(deviceInfo_[udid].second.networkId),
            deviceInfo.networkId, sizeof(deviceInfo.networkId)) != DM_OK) {
            LOGE("ChangeDeviceInfo deviceInfo copy networkId failed");
        }
        deviceInfo_[udid].second.deviceTypeId = deviceInfo.deviceTypeId;
        std::string uuid = "";
        GetUuidByNetworkId(deviceInfo.networkId, uuid);
        deviceInfo_[udid].first = uuid;
    }
    LOGI("ChangeDeviceInfo sucess udid %{public}s, networkId %{public}s.",
        GetAnonyString(udid).c_str(), GetAnonyString(std::string(deviceInfo.networkId)).c_str());
}

int32_t SoftbusCache::GetDeviceInfoFromCache(std::vector<DmDeviceInfo> &deviceInfoList)
{
    std::lock_guard<std::mutex> mutexLock(deviceInfosMutex_);
    for (const auto &item : deviceInfo_) {
        if (std::string(item.second.second.networkId) == std::string(localDeviceInfo_.networkId)) {
            continue;
        }
        deviceInfoList.push_back(item.second.second);
    }
    return DM_OK;
}

void SoftbusCache::UpdateDeviceInfoCache()
{
    LOGI("SoftbusCache::UpdateDeviceInfoCache");
    int32_t deviceCount = 0;
    NodeBasicInfo *nodeInfo = nullptr;
    int32_t ret = GetAllNodeDeviceInfo(DM_PKG_NAME, &nodeInfo, &deviceCount);
    if (ret != DM_OK) {
        LOGE("[SOFTBUS]GetAllNodeDeviceInfo failed, ret: %{public}d.", ret);
        return;
    }
    SaveLocalDeviceInfo();
    for (int32_t i = 0; i < deviceCount; ++i) {
        NodeBasicInfo *nodeBasicInfo = nodeInfo + i;
        DmDeviceInfo deviceInfo;
        ConvertNodeBasicInfoToDmDevice(*nodeBasicInfo, deviceInfo);
        SaveDeviceInfo(deviceInfo);
    }
    FreeNodeInfo(nodeInfo);
    LOGI("UpdateDeviceInfoCache success, deviceCount: %{public}d.", deviceCount);
    return;
}

int32_t SoftbusCache::GetUdidFromCache(const char *networkId, std::string &udid)
{
    std::lock_guard<std::mutex> mutexLock(deviceInfosMutex_);
    for (const auto &item : deviceInfo_) {
        if (std::string(item.second.second.networkId) == std::string(networkId)) {
            udid = item.first;
            LOGI("Get udid from cache success, networkId %{public}s, udid %{public}s.",
                GetAnonyString(std::string(networkId)).c_str(), GetAnonyString(udid).c_str());
            return DM_OK;
        }
    }
    int32_t ret = GetUdidByNetworkId(networkId, udid);
    if (ret == DM_OK) {
        LOGI("Get udid from bus success, networkId %{public}s, udid %{public}s.",
            GetAnonyString(std::string(networkId)).c_str(), GetAnonyString(udid).c_str());
        return DM_OK;
    }
    return ret;
}

int32_t SoftbusCache::GetUuidFromCache(const char *networkId, std::string &uuid)
{
    std::lock_guard<std::mutex> mutexLock(deviceInfosMutex_);
    for (const auto &item : deviceInfo_) {
        if (std::string(item.second.second.networkId) == std::string(networkId)) {
            uuid = item.second.first;
            LOGI("Get uuid from cache success, networkId %{public}s, uuid %{public}s.",
                GetAnonyString(std::string(networkId)).c_str(), GetAnonyString(uuid).c_str());
            return DM_OK;
        }
    }
    int32_t ret = GetUuidByNetworkId(networkId, uuid);
    if (ret == DM_OK) {
        LOGI("Get uuid from bus success, networkId %{public}s, uuid %{public}s.",
            GetAnonyString(std::string(networkId)).c_str(), GetAnonyString(uuid).c_str());
        return DM_OK;
    }
    return ret;
}

int32_t SoftbusCache::ConvertNodeBasicInfoToDmDevice(const NodeBasicInfo &nodeInfo, DmDeviceInfo &devInfo)
{
    (void)memset_s(&devInfo, sizeof(DmDeviceInfo), 0, sizeof(DmDeviceInfo));
    if (memcpy_s(devInfo.networkId, sizeof(devInfo.networkId), nodeInfo.networkId,
        std::min(sizeof(devInfo.networkId), sizeof(nodeInfo.networkId))) != DM_OK) {
        LOGE("ConvertNodeBasicInfoToDmDevice copy networkId data failed.");
    }

    if (memcpy_s(devInfo.deviceName, sizeof(devInfo.deviceName), nodeInfo.deviceName,
        std::min(sizeof(devInfo.deviceName), sizeof(nodeInfo.deviceName))) != DM_OK) {
        LOGE("ConvertNodeBasicInfoToDmDevice copy deviceName data failed.");
    }
    devInfo.deviceTypeId = nodeInfo.deviceTypeId;
    nlohmann::json extraJson;
    extraJson[PARAM_KEY_OS_TYPE] = nodeInfo.osType;
    extraJson[PARAM_KEY_OS_VERSION] = ConvertCharArray2String(nodeInfo.osVersion, OS_VERSION_BUF_LEN);
    devInfo.extraData = to_string(extraJson);
    return DM_OK;
}

void SoftbusCache::SaveDeviceSecurityLevel(const char *networkId)
{
    LOGI("SoftbusCache::SaveDeviceSecurityLevel networkId %{public}s.", GetAnonyString(std::string(networkId)).c_str());
    std::lock_guard<std::mutex> mutexLock(deviceSecurityLevelMutex_);
    if (deviceSecurityLevel_.find(std::string(networkId)) != deviceSecurityLevel_.end()) {
        return;
    }
    int32_t tempSecurityLevel = -1;
    if (GetNodeKeyInfo(DM_PKG_NAME, networkId, NodeDeviceInfoKey::NODE_KEY_DEVICE_SECURITY_LEVEL,
        reinterpret_cast<uint8_t *>(&tempSecurityLevel), LNN_COMMON_LEN) != DM_OK) {
        LOGE("[SOFTBUS]GetNodeKeyInfo networkType failed.");
        return;
    }
    deviceSecurityLevel_[std::string(networkId)] = tempSecurityLevel;
}

void SoftbusCache::DeleteDeviceSecurityLevel(const char *networkId)
{
    LOGI("SoftbusCache::DeleteDeviceSecurityLevel networkId %{public}s.",
        GetAnonyString(std::string(networkId)).c_str());
    std::lock_guard<std::mutex> mutexLock(deviceSecurityLevelMutex_);
    if (deviceSecurityLevel_.find(std::string(networkId)) != deviceSecurityLevel_.end()) {
        deviceSecurityLevel_.erase(std::string(networkId));
    }
}

int32_t SoftbusCache::GetSecurityDeviceLevel(const char *networkId, int32_t &securityLevel)
{
    std::lock_guard<std::mutex> mutexLock(deviceSecurityLevelMutex_);
    for (const auto &item : deviceSecurityLevel_) {
        if (item.first == std::string(networkId)) {
            securityLevel = item.second;
            LOGI("Get dev level from cache success, networkId is %{public}s.",
                GetAnonyString(std::string(networkId)).c_str());
            return DM_OK;
        }
    }
    int32_t ret = GetDevLevelFromBus(networkId, securityLevel);
    if (ret == DM_OK) {
        LOGI("Get dev level from softbus success.");
        return DM_OK;
    }
    return ret;
}

int32_t SoftbusCache::GetDevLevelFromBus(const char *networkId, int32_t &securityLevel)
{
    int32_t tempSecurityLevel = -1;
    if (GetNodeKeyInfo(DM_PKG_NAME, networkId, NodeDeviceInfoKey::NODE_KEY_DEVICE_SECURITY_LEVEL,
        reinterpret_cast<uint8_t *>(&tempSecurityLevel), LNN_COMMON_LEN) != DM_OK) {
        LOGE("[SOFTBUS]GetNodeKeyInfo networkType failed.");
        return ERR_DM_FAILED;
    }
    securityLevel = tempSecurityLevel;
    deviceSecurityLevel_[std::string(networkId)] = tempSecurityLevel;
    LOGI("Get dev level from softbus success, networkId is %{public}s.",
        GetAnonyString(std::string(networkId)).c_str());
    return DM_OK;
}

int32_t SoftbusCache::GetDevInfoByNetworkId(const std::string &networkId, DmDeviceInfo &nodeInfo)
{
    {
        std::lock_guard<std::mutex> mutexLock(deviceInfosMutex_);
        for (const auto &item : deviceInfo_) {
            if (std::string(item.second.second.networkId) == networkId) {
                nodeInfo = item.second.second;
                LOGI("GetDevInfoByNetworkId success networkId %{public}s, udid %{public}s.",
                    GetAnonyString(networkId).c_str(), GetAnonyString(item.first).c_str());
                return DM_OK;
            }
        }
    }
    int32_t ret = GetDevInfoFromBus(networkId, nodeInfo);
    if (ret != DM_OK) {
        LOGE("GetDevInfoFromBus failed.");
        return ret;
    }
    SaveDeviceInfo(nodeInfo);
    return DM_OK;
}

int32_t SoftbusCache::GetDevInfoFromBus(const std::string &networkId, DmDeviceInfo &devInfo)
{
    int32_t nodeInfoCount = 0;
    NodeBasicInfo *nodeInfo = nullptr;
    int32_t ret = GetAllNodeDeviceInfo(DM_PKG_NAME, &nodeInfo, &nodeInfoCount);
    if (ret != DM_OK) {
        LOGE("[SOFTBUS]GetAllNodeDeviceInfo failed, ret: %{public}d.", ret);
        return ret;
    }
    for (int32_t i = 0; i < nodeInfoCount; ++i) {
        NodeBasicInfo *nodeBasicInfo = nodeInfo + i;
        if (networkId == std::string(nodeBasicInfo->networkId)) {
            ConvertNodeBasicInfoToDmDevice(*nodeBasicInfo, devInfo);
            break;
        }
    }
    FreeNodeInfo(nodeInfo);
    LOGI("GetDeviceInfo complete, deviceName : %{public}s, deviceTypeId : %{public}d.",
        GetAnonyString(devInfo.deviceName).c_str(), devInfo.deviceTypeId);
    return ret;
}

int32_t SoftbusCache::GetNetworkIdFromCache(const std::string &udid, std::string &networkId)
{
    LOGI("udid %{public}s.", GetAnonyString(udid).c_str());
    {
        std::lock_guard<std::mutex> mutexLock(deviceInfosMutex_);
        if (deviceInfo_.find(udid) != deviceInfo_.end()) {
            networkId = deviceInfo_[udid].second.networkId;
            LOGI("GetNetworkIdFromCache success networkId %{public}s, udid %{public}s.",
                GetAnonyString(networkId).c_str(), GetAnonyString(udid).c_str());
            return DM_OK;
        }
    }
    return ERR_DM_FAILED;
}
} // namespace DistributedHardware
} // namespace OHOS