/*
 * Copyright (c) 2026 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 "device_manager_impl_3rd.h"

#include <memory>
#include <mutex>
#include <cstring>
#include <securec.h>

#include "device_manager_notify_3rd.h"
#include "dm_auth_info_3rd.h"
#include "dm_error_type_3rd.h"
#include "ipc_utils_3rd.h"
#include "iremote_object.h"
#include "if_system_ability_manager.h"
#include "iservice_registry.h"
#include "system_ability_definition.h"

namespace OHOS {
namespace DistributedHardware {
namespace {
const int32_t USLEEP_TIME_US_100000 = 100000; // 100ms
const int32_t SERVICE_INIT_MAX_NUM = 20;
constexpr int32_t DM_MIN_PINCODE_SIZE = 6;
constexpr int32_t DM_MAX_PINCODE_SIZE = 1024;
constexpr uint32_t MAX_SESSION_KEY_LENGTH = 512;
constexpr int32_t MAX_TRUST_3RD_DEVICE_SIZE = 500;
}

DeviceManagerImpl3rd &DeviceManagerImpl3rd::GetInstance()
{
    static DeviceManagerImpl3rd instance;
    return instance;
}

int32_t DeviceManagerImpl3rd::RegisterAuthCallback(const std::string &businessName,
    std::shared_ptr<DmAuthCallback> dmAuthCallback)
{
    if (businessName.empty() || dmAuthCallback == nullptr) {
        LOGE("Invalid parameter, businessName is empty or dmAuthCallback is nullptr.");
        return ERR_DM_INPUT_PARA_INVALID;
    }
    DeviceManagerNotify3rd::GetInstance().RegisterAuthCallback(businessName, dmAuthCallback);
    LOGI("completed");
    return DM_OK;
}

int32_t DeviceManagerImpl3rd::UnRegisterAuthCallback(const std::string &businessName)
{
    if (businessName.empty()) {
        LOGE("Invalid parameter, businessName is empty .");
        return ERR_DM_INPUT_PARA_INVALID;
    }
    DeviceManagerNotify3rd::GetInstance().UnRegisterAuthCallback(businessName);
    LOGI("completed");
    return DM_OK;
}

void DeviceManagerImpl3rd::SubscribeDMSAChangeListener()
{
    auto systemAbilityManager = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    if (systemAbilityManager == nullptr) {
        LOGE("get system ability manager failed.");
        return;
    }
    saListenerCallback_ = sptr<SystemAbilityListener3rd>(new (std::nothrow) SystemAbilityListener3rd());
    if (saListenerCallback_ == nullptr) {
        LOGE("saListenerCallback_ is nullptr.");
        return;
    }
    if (!isSubscribeDMSAChangeListener_.load()) {
        LOGI("try subscribe source sa change listener, sa id: %{public}d", DISTRIBUTED_HARDWARE_DEVICEMANAGER_SA_ID);
        int32_t ret = systemAbilityManager->SubscribeSystemAbility(DISTRIBUTED_HARDWARE_DEVICEMANAGER_SA_ID,
            saListenerCallback_);
        if (ret != DM_OK) {
            LOGE("subscribe source sa change failed: %{public}d", ret);
            saListenerCallback_ = nullptr;
            return;
        }
        isSubscribeDMSAChangeListener_.store(true);
    }
}

int32_t DeviceManagerImpl3rd::ClientInit()
{
    LOGI("Start");
    if (dm3rdremoteObject_ != nullptr) {
        LOGI("DeviceManagerService Already Init");
        return DM_OK;
    }

    auto samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    if (samgr == nullptr) {
        LOGE("Get SystemAbilityManager Failed");
        return ERR_DM_INIT_FAILED;
    }

    auto object = samgr->CheckSystemAbility(DISTRIBUTED_HARDWARE_DEVICEMANAGER_SA_ID);
    if (object == nullptr) {
        LOGE("Get DeviceManager SystemAbility Failed");
        DmServiceLoad3rd::GetInstance().LoadDMService();
        return ERR_DM_INIT_FAILED;
    }

    dm3rdremoteObject_ = object;
    LOGI("Completed");
    return DM_OK;
}

int32_t DeviceManagerImpl3rd::SendRequest(uint32_t code, MessageParcel &data, MessageParcel &reply)
{
    CHECK_NULL_RETURN(dm3rdremoteObject_, ERR_DM_POINT_NULL);
    MessageOption option;
    return dm3rdremoteObject_->SendRequest(code, data, reply, option);
}

int32_t DeviceManagerImpl3rd::InitDeviceManager(const std::string &businessName,
    std::shared_ptr<DmInit3rdCallback> dmInit3rdCallback)
{
    if (businessName.empty()) {
        LOGE("Invalid parameter, businessName is empty.");
        return ERR_DM_INPUT_PARA_INVALID;
    }
    LOGI("Start, businessName: %{public}s", businessName.c_str());
    SubscribeDMSAChangeListener();
    int32_t errcode = DM_OK;
    int32_t retryNum = 0;
    while (retryNum < SERVICE_INIT_MAX_NUM) {
        errcode = ClientInit();
        if (errcode == DM_OK) {
            break;
        }
        usleep(USLEEP_TIME_US_100000);
        retryNum++;
        if (retryNum == SERVICE_INIT_MAX_NUM) {
            LOGE("InitDeviceManager error, wait for device manager service starting timeout.");
            return ERR_DM_NOT_INIT;
        }
    }
    sptr<IpcClientStub3rd> listener = sptr<IpcClientStub3rd>(new (std::nothrow) IpcClientStub3rd());
    CHECK_NULL_RETURN(listener, ERR_DM_POINT_NULL);

    MessageParcel data;
    WRITE_INTERFACE_TOKEN(data, ERR_DM_IPC_WRITE_FAILED);
    WRITE_HELPER_RET(data, String, businessName, ERR_DM_IPC_WRITE_FAILED);
    if (!data.WriteRemoteObject(listener)) {
        LOGE("write listener failed");
        return ERR_DM_IPC_WRITE_FAILED;
    }
    MessageParcel reply;
    int32_t sendRet = SendRequest(INIT_DEVICE_MANAGER, data, reply);
    if (sendRet != DM_OK) {
        LOGE("SendRequest failed, ret: %{public}d", sendRet);
        return ERR_DM_IPC_SEND_REQUEST_FAILED;
    }
    READ_HELPER_RET(reply, Int32, errcode, ERR_DM_IPC_READ_FAILED);
    if (errcode != DM_OK) {
        LOGE("InitDeviceManager failed, result: %{public}d", errcode);
        return errcode;
    }
    DeviceManagerNotify3rd::GetInstance().RegisterDeathRecipientCallback(businessName, dmInit3rdCallback);

    LOGI("Completed");
    return DM_OK;
}

int32_t DeviceManagerImpl3rd::ImportPinCode3rd(const std::string &businessName, const std::string &pinCode)
{
    if (businessName.empty() || pinCode.empty()) {
        LOGE("Invalid parameter, businessName or pinCode is empty.");
        return ERR_DM_INPUT_PARA_INVALID;
    }
    LOGI("Start, businessName: %{public}s", businessName.c_str());

    MessageParcel data;
    WRITE_INTERFACE_TOKEN(data, ERR_DM_IPC_WRITE_FAILED);
    WRITE_HELPER_RET(data, String, businessName, ERR_DM_IPC_WRITE_FAILED);
    WRITE_HELPER_RET(data, String, pinCode, ERR_DM_IPC_WRITE_FAILED);

    MessageParcel reply;
    int32_t ret = SendRequest(IMPORT_PINCODE_3RD, data, reply);
    if (ret != DM_OK) {
        LOGE("SendRequest failed, ret: %{public}d", ret);
        return ERR_DM_IPC_SEND_REQUEST_FAILED;
    }
    READ_HELPER_RET(reply, Int32, ret, ERR_DM_IPC_READ_FAILED);
    if (ret != DM_OK) {
        LOGE("ImportPinCode3rd failed, result: %{public}d", ret);
        return ret;
    }
    LOGI("Completed");
    return DM_OK;
}

int32_t DeviceManagerImpl3rd::GeneratePinCode(uint32_t pinLength, std::string &pincode)
{
    if (pinLength < DM_MIN_PINCODE_SIZE || pinLength > DM_MAX_PINCODE_SIZE) {
        LOGE("Invalid pinLength: %{public}u", pinLength);
        return ERR_DM_INPUT_PARA_INVALID;
    }

    LOGI("Start, pinLength: %{public}u", pinLength);

    MessageParcel data;
    WRITE_INTERFACE_TOKEN(data, ERR_DM_IPC_WRITE_FAILED);
    WRITE_HELPER_RET(data, Uint32, pinLength, ERR_DM_IPC_WRITE_FAILED);

    MessageParcel reply;
    int32_t ret = SendRequest(GENERATE_PINCODE_3RD, data, reply);
    if (ret != DM_OK) {
        LOGE("SendRequest failed, ret: %{public}d", ret);
        return ERR_DM_IPC_SEND_REQUEST_FAILED;
    }
    READ_HELPER_RET(reply, Int32, ret, ERR_DM_IPC_READ_FAILED);
    if (ret != DM_OK) {
        LOGE("GeneratePinCode failed, result: %{public}d", ret);
        return ret;
    }
    READ_HELPER_RET(reply, String, pincode, ERR_DM_IPC_READ_FAILED);

    LOGI("Completed, pincode length: %{public}zu", pincode.length());
    return DM_OK;
}

bool DeviceManagerImpl3rd::IsInvalidPeerTargetId(const PeerTargetId3rd &targetId)
{
    return targetId.deviceId.empty() && targetId.brMac.empty() && targetId.bleMac.empty() && targetId.wifiIp.empty();
}

bool EncodePeerTargetId(const PeerTargetId3rd &targetId, MessageParcel &parcel)
{
    bool bRet = true;
    bRet = (bRet && parcel.WriteString(targetId.deviceId));
    bRet = (bRet && parcel.WriteString(targetId.brMac));
    bRet = (bRet && parcel.WriteString(targetId.bleMac));
    bRet = (bRet && parcel.WriteString(targetId.wifiIp));
    bRet = (bRet && parcel.WriteUint16(targetId.wifiPort));
    return bRet;
}

int32_t DeviceManagerImpl3rd::AuthPincode(const PeerTargetId3rd &targetId,
    std::map<std::string, std::string> &authParam)
{
    if (IsInvalidPeerTargetId(targetId)) {
        LOGE("DeviceManagerImpl3rd::AuthPincode failed: input targetId is empty.");
        return ERR_DM_INPUT_PARA_INVALID;
    }
    LOGI("Start, deviceId: %{public}s", GetAnonyString(targetId.deviceId).c_str());

    MessageParcel data;
    WRITE_INTERFACE_TOKEN(data, ERR_DM_IPC_WRITE_FAILED);
    EncodePeerTargetId(targetId, data);

    std::string authParamStr = ConvertMapToJsonString(authParam);
    WRITE_HELPER_RET(data, String, authParamStr, ERR_DM_IPC_WRITE_FAILED);

    MessageParcel reply;
    int32_t ret = SendRequest(AUTH_PINCODE_3RD, data, reply);
    if (ret != DM_OK) {
        LOGE("SendRequest failed, ret: %{public}d", ret);
        return ERR_DM_IPC_SEND_REQUEST_FAILED;
    }
    READ_HELPER_RET(reply, Int32, ret, ERR_DM_IPC_READ_FAILED);
    if (ret != DM_OK) {
        LOGE("AuthPincode failed, ret: %{public}d", ret);
        return ret;
    }

    LOGI("Completed");
    return DM_OK;
}

DeviceManagerImpl3rd::SystemAbilityListener3rd::SystemAbilityListener3rd()
{
    LOGI("SystemAbilityListener3rd create");
}
 
DeviceManagerImpl3rd::SystemAbilityListener3rd::~SystemAbilityListener3rd()
{
    LOGI("SystemAbilityListener3rd destroy");
}

void DeviceManagerImpl3rd::SystemAbilityListener3rd::OnAddSystemAbility(int32_t systemAbilityId,
    const std::string &deviceId)
{
    LOGI("sa %{public}d is added.", systemAbilityId);
}

void DeviceManagerImpl3rd::SystemAbilityListener3rd::OnRemoveSystemAbility(int32_t systemAbilityId,
    const std::string &deviceId)
{
    LOGI("sa %{public}d is removed.", systemAbilityId);
    if (systemAbilityId == DISTRIBUTED_HARDWARE_DEVICEMANAGER_SA_ID) {
        DeviceManagerNotify3rd::GetInstance().OnRemoteDied();
    }
}

int32_t DeviceManagerImpl3rd::AuthDevice3rd(const PeerTargetId3rd &targetId, std::map<std::string,
    std::string> &authParam)
{
    if (IsInvalidPeerTargetId(targetId)) {
        LOGE("AuthDevice3rd failed: invalid targetId.");
        return ERR_DM_INPUT_PARA_INVALID;
    }

    std::string authParamStr = ConvertMapToJsonString(authParam);
    if (!authParam.empty() && authParamStr.empty()) {
        LOGE("AuthDevice3rd failed: authParam convert error.");
        return ERR_DM_INPUT_PARA_INVALID;
    }

    MessageParcel data;
    WRITE_INTERFACE_TOKEN(data, ERR_DM_IPC_WRITE_FAILED);
    if (!data.WriteString(targetId.deviceId) ||
        !data.WriteString(targetId.brMac) ||
        !data.WriteString(targetId.bleMac) ||
        !data.WriteString(targetId.wifiIp) ||
        !data.WriteUint16(targetId.wifiPort) ||
        !data.WriteString(authParamStr)) {
        LOGE("AuthDevice3rd write request failed.");
        return ERR_DM_IPC_WRITE_FAILED;
    }
    MessageParcel reply;
    int32_t ret = SendRequest(AUTH_DEVICE_3RD, data, reply);
    if (ret != DM_OK) {
        LOGE("AuthDevice3rd send request failed.");
        return ERR_DM_IPC_SEND_REQUEST_FAILED;
    }
    READ_HELPER_RET(reply, Int32, ret, ERR_DM_IPC_READ_FAILED);
    if (ret != DM_OK) {
        LOGE("AuthDevice3rd failed, result: %{public}d", ret);
        return ret;
    }
    LOGI("Completed");
    return DM_OK;
}

int32_t DeviceManagerImpl3rd::QueryTrustRelation(const std::string &businessName,
    std::vector<TrustDeviceInfo3rd> &trustedDeviceList)
{
    if (businessName.empty()) {
        LOGE("failed: businessName is empty.");
        return ERR_DM_INPUT_PARA_INVALID;
    }
    auto &dm3rdImpl = DeviceManagerImpl3rd::GetInstance();
    CHECK_NULL_RETURN(dm3rdImpl.dm3rdremoteObject_, ERR_DM_POINT_NULL);

    MessageParcel data;
    WRITE_INTERFACE_TOKEN(data, ERR_DM_IPC_WRITE_FAILED);
    if (!data.WriteString(businessName)) {
        LOGE("write businessName failed.");
        return ERR_DM_IPC_WRITE_FAILED;
    }

    MessageParcel reply;
    int32_t sendRet = SendRequest(QUERY_TRUST_RELATION_3RD, data, reply);
    if (sendRet != DM_OK) {
        LOGE("send request failed.");
        return ERR_DM_IPC_SEND_REQUEST_FAILED;
    }
    trustedDeviceList.clear();
    int32_t ret = reply.ReadInt32();
    if (ret != DM_OK) {
        return ret;
    }

    int32_t listSize = reply.ReadInt32();
    if (listSize < 0 || listSize > MAX_TRUST_3RD_DEVICE_SIZE) {
        LOGE("invalid list size.");
        return ERR_DM_IPC_READ_FAILED;
    }
    for (int32_t i = 0; i < listSize; ++i) {
        TrustDeviceInfo3rd deviceInfo;
        deviceInfo.trustDeviceId = reply.ReadString();
        deviceInfo.sessionKeyId = reply.ReadInt32();
        deviceInfo.createTime = reply.ReadInt64();
        deviceInfo.userId = reply.ReadInt32();
        deviceInfo.extra = reply.ReadString();
        deviceInfo.bindLevel = reply.ReadInt32();
        deviceInfo.bindType = reply.ReadInt32();
        std::string keyStr = reply.ReadString();
        BuildSessionKey(keyStr, deviceInfo);
        trustedDeviceList.push_back(deviceInfo);
    }
    return DM_OK;
}

int32_t DeviceManagerImpl3rd::BuildSessionKey(const std::string &keyStr, TrustDeviceInfo3rd &deviceInfo)
{
    size_t keyLen = keyStr.size();
    if (keyLen > MAX_SESSION_KEY_LENGTH) {
        LOGE("SessionKey too long, len: %{public}zu", keyLen);
        return ERR_DM_SAVE_SESSION_KEY_FAILED;
    }
    deviceInfo.sessionKey.key = (uint8_t*)calloc(keyLen, sizeof(uint8_t));
    if (deviceInfo.sessionKey.key == nullptr) {
        LOGE("calloc sessionKey failed");
        return ERR_DM_FAILED;
    }
    if (memcpy_s(deviceInfo.sessionKey.key, keyLen, keyStr.c_str(), keyLen) != DM_OK) {
        LOGE("copy key data failed.");
        if (deviceInfo.sessionKey.key != nullptr) {
            (void)memset_s(deviceInfo.sessionKey.key, keyLen, 0, keyLen);
            free(deviceInfo.sessionKey.key);
            deviceInfo.sessionKey.key = nullptr;
            deviceInfo.sessionKey.keyLen = 0;
        }
        return ERR_DM_FAILED;
    }
    deviceInfo.sessionKey.keyLen = keyLen;
    return DM_OK;
}

int32_t DeviceManagerImpl3rd::DeleteTrustRelation(const std::string &businessName, const std::string &peerDeviceId,
    std::map<std::string, std::string> &unbindParam)
{
    if (businessName.empty() || peerDeviceId.empty()) {
        LOGE("DeleteTrustRelation failed: businessName or peerDeviceId is empty.");
        return ERR_DM_INPUT_PARA_INVALID;
    }
    std::string unbindParamStr = ConvertMapToJsonString(unbindParam);
    if (!unbindParam.empty() && unbindParamStr.empty()) {
        LOGE("DeleteTrustRelation failed: unbindParam convert error.");
        return ERR_DM_INPUT_PARA_INVALID;
    }

    MessageParcel data;
    WRITE_INTERFACE_TOKEN(data, ERR_DM_IPC_WRITE_FAILED);
    if (!data.WriteString(businessName) || !data.WriteString(peerDeviceId) || !data.WriteString(unbindParamStr)) {
        LOGE("DeleteTrustRelation write request failed.");
        return ERR_DM_IPC_WRITE_FAILED;
    }

    MessageParcel reply;
    int32_t sendRet = SendRequest(DELETE_TRUST_RELATION_3RD, data, reply);
    if (sendRet != DM_OK) {
        LOGE("DeleteTrustRelation send request failed.");
        return ERR_DM_IPC_SEND_REQUEST_FAILED;
    }
    return reply.ReadInt32();
}

int32_t DeviceManagerImpl3rd::AuthCredential(const PeerTargetId3rd &targetId, const std::string &authParam)
{
    if (IsInvalidPeerTargetId(targetId)) {
        LOGE("AuthCredential failed: invalid targetId.");
        return ERR_DM_INPUT_PARA_INVALID;
    }

    if (authParam.empty()) {
        LOGE("AuthCredential failed: authParam is empty.");
        return ERR_DM_INPUT_PARA_INVALID;
    }

    MessageParcel data;
    WRITE_INTERFACE_TOKEN(data, ERR_DM_IPC_WRITE_FAILED);
    if (!data.WriteString(targetId.deviceId) ||
        !data.WriteString(targetId.brMac) ||
        !data.WriteString(targetId.bleMac) ||
        !data.WriteString(targetId.wifiIp) ||
        !data.WriteUint16(targetId.wifiPort) ||
        !data.WriteString(authParam)) {
        LOGE("AuthCredential write request failed.");
        return ERR_DM_IPC_WRITE_FAILED;
    }
    MessageParcel reply;
    int32_t ret = SendRequest(AUTH_CREDENTIAL_3RD, data, reply);
    if (ret != DM_OK) {
        LOGE("AuthCredential send request failed: %{public}d", ret);
        return ERR_DM_IPC_SEND_REQUEST_FAILED;
    }
    READ_HELPER_RET(reply, Int32, ret, ERR_DM_IPC_READ_FAILED);
    if (ret != DM_OK) {
        LOGE("AuthCredential failed, result: %{public}d", ret);
        return ret;
    }
    LOGI("Completed");
    return DM_OK;
}
} // namespace DistributedHardware
} // namespace OHOS