/*
 * Copyright (c) Huawei Device Co., Ltd. 2026-2026. All rights reserved.
 * 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 "object_editor_client.h"

#include <filesystem>
#include <fstream>
#include <random>
#include <chrono>
#include <sys/time.h>
#include "application_context.h"
#include <hitrace_meter.h>
#include "iservice_registry.h"
#include "system_utils.h"
#include "iobject_editor_extension.h"
#include "object_editor_package.h"
#include "object_editor_common.h"
#include "hisysevent.h"

namespace OHOS {
namespace ObjectEditor {
namespace {
constexpr int32_t UUID_ARRAY_LEN = 16;
constexpr int32_t UUID_START_POS_1 = 8;
constexpr int32_t UUID_START_POS_2 = 12;
constexpr int32_t UUID_START_POS_3 = 16;
constexpr int32_t UUID_START_POS_4 = 20;
constexpr int32_t UUID_HEAD_LEN = 8;
constexpr int32_t UUID_OTHER_LEN = 4;
constexpr int32_t INDEX_15 = 15;
constexpr int32_t INDEX_14 = 14;
constexpr int32_t INDEX_13 = 13;
constexpr int32_t INDEX_12 = 12;
constexpr int32_t INDEX_10 = 10;
constexpr int32_t INDEX_9 = 9;
constexpr int32_t INDEX_8 = 8;
constexpr int32_t INDEX_7 = 7;
constexpr int32_t INDEX_6 = 6;
constexpr int32_t INDEX_5 = 5;
constexpr int32_t INDEX_4 = 4;
constexpr int32_t INDEX_3 = 3;
constexpr int32_t INDEX_2 = 2;
constexpr int32_t INDEX_1 = 1;
constexpr int32_t INDEX_0 = 0;
constexpr int32_t RIGHT_SHIFT_8 = 8;
constexpr int32_t RIGHT_SHIFT_16 = 16;
constexpr int32_t RIGHT_SHIFT_24 = 24;
constexpr int32_t RIGHT_SHIFT_32 = 32;
constexpr int32_t RIGHT_SHIFT_40 = 40;
constexpr int32_t RIGHT_SHIFT_48 = 48;
constexpr int32_t RIGHT_SHIFT_56 = 56;
constexpr int32_t METADATA_BUFFER_SIZE = 2048;
}
namespace fs = std::filesystem;
// LCOV_EXCL_START
IMPLEMENT_SINGLE_INSTANCE(ObjectEditorClient);

void ObjectEditorClient::SubscribeSystemAbility()
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    if (saStatusListener_ != nullptr) {
        OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "No duplicate subscribe");
        return;
    }
    saStatusListener_ = sptr<ObjectEditorAbilityListener>(new ObjectEditorAbilityListener());
    if (saStatusListener_ == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "create failed");
        return;
    }
    auto sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    if (sam == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "sam is null");
        return;
    }
    int32_t result = sam->SubscribeSystemAbility(static_cast<int32_t>(OBJECT_EDITOR_SERVICE_SA_ID), saStatusListener_);
    if (result != ERR_OK) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "subscribe failed:%{public}d", result);
    }
}

void ObjectEditorClient::UnsubscribeSystemAbility()
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    if (saStatusListener_ == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "listener is null");
        return;
    }
    auto sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    if (sam == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "sam is null");
        return;
    }
    int32_t result = sam->UnSubscribeSystemAbility(
        static_cast<int32_t>(OBJECT_EDITOR_SERVICE_SA_ID), saStatusListener_);
    if (result == ERR_OK) {
        saStatusListener_ = nullptr;
    } else {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "unsubscribe failed:%{public}d", result);
    }
}

void ObjectEditorAbilityListener::OnAddSystemAbility(int32_t systemAbilityId, const std::string &deviceId)
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    std::lock_guard<std::mutex> lock(mutex_);
    if (systemAbilityId == OBJECT_EDITOR_SERVICE_SA_ID) {
        OBJECT_EDITOR_LOGI(ObjectEditorDomain::CLIENT, "sa is added");
    }
}

void ObjectEditorAbilityListener::OnRemoveSystemAbility(int32_t systemAbilityId, const std::string &deviceId)
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    std::lock_guard<std::mutex> lock(mutex_);
    if (systemAbilityId == OBJECT_EDITOR_SERVICE_SA_ID) {
        ObjectEditorClient::GetInstance().SARegCleanUp();
        OBJECT_EDITOR_LOGI(ObjectEditorDomain::CLIENT, "sa is removed");
    }
}

sptr<IObjectEditorManager> ObjectEditorClient::GetObjectEditorProxy(const sptr<IRemoteObject> &object)
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    if (oeSAProxy_ != nullptr) {
        return oeSAProxy_;
    }
    deathRecipient_ = sptr<IRemoteObject::DeathRecipient>(new ObjectEditorSADeathRecipient());
    if (deathRecipient_ == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "deathRecipient is null");
        return nullptr;
    }
    if (object == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "object is null");
        return nullptr;
    }
    if (object->IsProxyObject() && !object->AddDeathRecipient(deathRecipient_)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "AddDeathRecipient failed");
        return nullptr;
    }
    OBJECT_EDITOR_LOGI(ObjectEditorDomain::CLIENT, "get sa proxy succeed");
    oeSAProxy_ = iface_cast<IObjectEditorManager>(object);
    return oeSAProxy_;
}

sptr<IObjectEditorManager> ObjectEditorClient::GetIObjectEditorManager()
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    std::lock_guard<std::mutex> lock(proxyMutex_);
    SubscribeSystemAbility();
    if (oeSAProxy_ != nullptr) {
        return oeSAProxy_;
    }
    auto samgrProxy = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    if (samgrProxy == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "get samgrProxy fail");
        return oeSAProxy_;
    }
    sptr<IRemoteObject> remoteObject = samgrProxy->CheckSystemAbility(OBJECT_EDITOR_SERVICE_SA_ID);
    if (remoteObject != nullptr) {
        return GetObjectEditorProxy(remoteObject);
    }
    sptr<ObjectEditorLoadCallback> loadCallback = new (std::nothrow) ObjectEditorLoadCallback();
    if (loadCallback == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "loadCallback is null");
        return nullptr;
    }
    InitLoadState();
    int32_t result = samgrProxy->LoadSystemAbility(OBJECT_EDITOR_SERVICE_SA_ID, loadCallback);
    if (result != ERR_NONE) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "failed to sa error:%{public}d", result);
        return nullptr;
    }
    if (!WaitLoadStateChange()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "wait sa failed");
        return nullptr;
    }
    OBJECT_EDITOR_LOGI(ObjectEditorDomain::CLIENT, "load sa success");
    return oeSAProxy_;
}

void ObjectEditorClient::InitLoadState()
{
    std::unique_lock<std::mutex> lock(loadMutex_);
    loadState_ = false;
}

bool ObjectEditorClient::WaitLoadStateChange()
{
    std::unique_lock<std::mutex> lock(loadMutex_);
    auto wait = loadCond_.wait_for(lock, std::chrono::milliseconds(LOAD_TIMEOUT_MS), [this] {
        return loadState_ == true;
    });
    if (!wait) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "timeout");
        return false;
    }
    return true;
}

void ObjectEditorLoadCallback::OnLoadSystemAbilitySuccess(
    int32_t systemAbilityId, const sptr<IRemoteObject> &remoteObject)
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    if (systemAbilityId != OBJECT_EDITOR_SERVICE_SA_ID) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "sa id invalid");
        return;
    }
    if (remoteObject == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "remoteObject is null");
        ObjectEditorClient::GetInstance().LoadSystemAbilityFail();
        return;
    }
    OBJECT_EDITOR_LOGI(ObjectEditorDomain::CLIENT, "load sa success");
    ObjectEditorClient::GetInstance().LoadSystemAbilitySuccess(remoteObject);
}

void ObjectEditorLoadCallback::OnLoadSystemAbilityFail(int32_t systemAbilityId)
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    if (systemAbilityId != OBJECT_EDITOR_SERVICE_SA_ID) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "sa id invalid");
        return;
    }
    ObjectEditorClient::GetInstance().LoadSystemAbilityFail();
}

void ObjectEditorClient::LoadSystemAbilitySuccess(const sptr<IRemoteObject> &remoteObject)
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    std::unique_lock<std::mutex> lock(loadMutex_);
    loadState_ = true;
    GetObjectEditorProxy(remoteObject);
    loadCond_.notify_one();
}

void ObjectEditorClient::LoadSystemAbilityFail()
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    std::unique_lock<std::mutex> lock(loadMutex_);
    loadState_ = false;
    loadCond_.notify_one();
}

void ObjectEditorClient::OnRemoteDied(const wptr<IRemoteObject> &remote)
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    if (remote == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "remote is null");
        return;
    }
    std::lock_guard<std::mutex> lock(proxyMutex_);
    if (oeSAProxy_ == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "oeSAProxy_ is null");
        return;
    }
    sptr<IRemoteObject> serviceRemote = oeSAProxy_->AsObject();
    if ((serviceRemote != nullptr) && (serviceRemote == remote.promote())) {
        serviceRemote->RemoveDeathRecipient(deathRecipient_);
        oeSAProxy_ = nullptr;
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "onRemoteDied");
    }
}

ErrCode ObjectEditorClient::StartObjectEditorExtension(
    std::unique_ptr<ObjectEditorDocument> &document,
    const sptr<IObjectEditorClientCallback> &objectEditorClientCallback,
    sptr<IObjectEditorService> &oeExtensionRemoteObject,
    bool &isPackageExtension)
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    if (document == nullptr || objectEditorClientCallback == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "document or callback is null");
        return ObjectEditorClientErrCode::CLIENT_INVALID_PARAMETER;
    }
    if (document->GetOperateType() == OperateType::UNKNOWN) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "unknown operate");
        return ObjectEditorClientErrCode::CLIENT_UNKNOWN_OPERATE;
    }
    HITRACE_METER_FMT(HITRACE_TAG_OHOS, "client::StartObjectEditorExtension");
    if (document->GetDocumentId().empty()) {
        OBJECT_EDITOR_LOGI(ObjectEditorDomain::CLIENT, "init document id");
        document->SetDocumentId(GenRandomUuid());
    }
    auto ret = StartObjectEditorExtensionInner(document, objectEditorClientCallback,
        oeExtensionRemoteObject, isPackageExtension);
    if (ret != ObjectEditorClientErrCode::CLIENT_OK) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "clean temp file");
        CleanupTempFiles(document);
    }
    return ret;
}

ErrCode ObjectEditorClient::StartObjectEditorExtensionInner(
    std::unique_ptr<ObjectEditorDocument> &document,
    const sptr<IObjectEditorClientCallback> &objectEditorClientCallback,
    sptr<IObjectEditorService> &oeExtensionRemoteObject,
    bool &isPackageExtension)
{
    ErrCode ret = PrepareFiles(document);
    if (ret != ObjectEditorClientErrCode::CLIENT_OK) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "prepare files failed, ret: %{public}d", ret);
        return ret;
    }
    sptr<IObjectEditorManager> objectEditorManagerProxy = GetIObjectEditorManager();
    if (objectEditorManagerProxy == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "get proxy fail");
        return ERR_INVALID_VALUE;
    }
    sptr<IRemoteObject> remoteObject = nullptr;
    ret = objectEditorManagerProxy->StartObjectEditorExtension(document,
        objectEditorClientCallback, remoteObject, isPackageExtension);
    if (ret != ERR_OK || (!isPackageExtension && remoteObject == nullptr)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "failed:%{public}d", ret);
        return ret;
    }
    if (isPackageExtension) {
        ret = HandlePackage(document, objectEditorClientCallback, oeExtensionRemoteObject);
        if (ret != ERR_OK) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "HandlePackage failed:%{public}d", ret);
            return ret;
        }
    } else {
        oeExtensionRemoteObject = iface_cast<IObjectEditorExtension>(remoteObject);
        if (oeExtensionRemoteObject == nullptr) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "oeExtensionRemoteObject is null");
            return ERR_INVALID_OPERATION;
        }
        oeExtensionRemoteObject->SetRemoteObject(remoteObject);
    }
    return ERR_OK;
}

ErrCode ObjectEditorClient::HandlePackage(
    const std::unique_ptr<ObjectEditorDocument> &document,
    const sptr<IObjectEditorClientCallback> &objectEditorClientCallback,
    sptr<IObjectEditorService> &oeExtensionRemoteObject)
{
    auto start = std::chrono::steady_clock::now();
    OBJECT_EDITOR_LOGI(ObjectEditorDomain::CLIENT, "in");
    if ((document->GetOperateType() == OperateType::CREATE_BY_FILE &&
         !document->GetNativeFileUri().has_value()) ||
        !document->GetTmpFileUri().has_value()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "document param is invalid");
        return ERR_INVALID_OPERATION;
    }
    std::unique_ptr<ObjectEditorDocument> newDocument = nullptr;
    if (document->GetOperateType() == OperateType::CREATE_BY_FILE) {
        newDocument = ObjectEditorDocument::CreateByFile(document->GetOriFilePath());
        if (newDocument == nullptr) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "create document by file failed");
            return CE_ERR_NULL_POINTER;
        }
        newDocument->SetOEid(PACKAGE_OEID);
        newDocument->SetNativeFileUri(document->GetNativeFileUri().value());
    } else if (document->GetOperateType() == OperateType::EDIT) {
        newDocument = ObjectEditorDocument::CreateByOEid(PACKAGE_OEID);
        if (newDocument == nullptr) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "create document by oeid failed");
            return CE_ERR_NULL_POINTER;
        }
        newDocument->SetOperateType(OperateType::EDIT);
    }
    if (newDocument == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "newDocument is nullptr");
        return CE_ERR_NULL_POINTER;
    }
    newDocument->SetLinking(document->GetLinking());
    newDocument->SetDocumentId(document->GetDocumentId());
    newDocument->SetTmpFileUri(document->GetTmpFileUri().value());
    newDocument->SetSnapshotUri(document->GetSnapshotUri());
    sptr<ObjectEditorPackage> packageProxy = sptr<ObjectEditorPackage>::MakeSptr();
    if (packageProxy == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "create package proxy failed");
        return CLIENT_MEMORY_ALLOCATION_FAILED;
    }
    packageProxy->Initial(std::move(newDocument), objectEditorClientCallback);
    oeExtensionRemoteObject = packageProxy;
    auto end = std::chrono::steady_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
    if (document->GetOperateType() == OperateType::CREATE_BY_FILE) {
        HiSysEventWrite(OBJECT_EDITOR, "CREATE_DOCUMENT", OHOS::HiviewDFX::HiSysEvent::EventType::STATISTIC,
                        "OEID", PACKAGE_OEID, "CREATEMODE", "CREATE_BY_FILE",
                        "FILEEXT", SystemUtils::GetFileSuffix(document->GetOriFileUri().value()),
                        "ISLINKING", document->GetLinking(), "ISPACKAGE", true, "DURATION", duration);
    }
    return ERR_OK;
}

std::string ObjectEditorClient::GenRandomUuid()
{
    struct timeval tv;
    struct timezone tz;
    struct tm randomTime;
    uint32_t randomNum = 0;
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, RAND_MAX);
    std::array<uint8_t, UUID_ARRAY_LEN> uuid_ = {0x00};
    randomNum = static_cast<unsigned int>(dis(gen));
    gettimeofday(&tv, &tz);
    localtime_r(&tv.tv_sec, &randomTime);
    uuid_[INDEX_15] = static_cast<uint8_t>(static_cast<uint64_t>(tv.tv_usec) & 0x00000000000000FF);
    uuid_[INDEX_14] = static_cast<uint8_t>((static_cast<uint64_t>(tv.tv_usec) & 0x000000000000FF00) >> RIGHT_SHIFT_8);
    uuid_[INDEX_13] = static_cast<uint8_t>((static_cast<uint64_t>(tv.tv_usec) & 0x0000000000FF0000) >> RIGHT_SHIFT_16);
    uuid_[INDEX_12] = static_cast<uint8_t>((static_cast<uint64_t>(tv.tv_usec) & 0x00000000FF000000) >> RIGHT_SHIFT_24);
    uuid_[INDEX_10] = static_cast<uint8_t>((static_cast<uint64_t>(tv.tv_usec) & 0x000000FF00000000) >> RIGHT_SHIFT_32);
    uuid_[INDEX_9] = static_cast<uint8_t>((static_cast<uint64_t>(tv.tv_usec) & 0x0000FF0000000000) >> RIGHT_SHIFT_40);
    uuid_[INDEX_8] = static_cast<uint8_t>((static_cast<uint64_t>(tv.tv_usec) & 0x00FF000000000000) >> RIGHT_SHIFT_48);
    uuid_[INDEX_7] = static_cast<uint8_t>((static_cast<uint64_t>(tv.tv_usec) & 0xFF00000000000000) >> RIGHT_SHIFT_56);
    uuid_[INDEX_6] = static_cast<uint8_t>((unsigned int)randomTime.tm_sec +
        static_cast<unsigned int>(randomNum) & 0xFF);
    uuid_[INDEX_5] = static_cast<uint8_t>(((unsigned int)randomTime.tm_min + (randomNum >> RIGHT_SHIFT_8)) & 0xFF);
    uuid_[INDEX_4] = static_cast<uint8_t>(((unsigned int)randomTime.tm_hour + (randomNum >> RIGHT_SHIFT_16)) & 0xFF);
    uuid_[INDEX_3] = static_cast<uint8_t>(((unsigned int)randomTime.tm_mday + (randomNum >> RIGHT_SHIFT_24)) & 0xFF);
    uuid_[INDEX_2] = static_cast<uint8_t>((unsigned int)randomTime.tm_mon & 0xFF);
    uuid_[INDEX_1] = static_cast<uint8_t>((unsigned int)randomTime.tm_year & 0xFF);
    uuid_[INDEX_0] = static_cast<uint8_t>(((unsigned int)randomTime.tm_year & 0xFF00) >> RIGHT_SHIFT_8);
    std::string tmp = "";
    std::string ret = "";
    static const char *hex = "0123456789ABCDEF";
    static const int NIBBLE_SHIFT = 4;
    static const int NIBBLE_MASK = 0xF;
    for (auto it = uuid_.begin(); it != uuid_.end(); it++) {
        tmp.push_back(hex[(*it >> NIBBLE_SHIFT) & NIBBLE_MASK]); // Right shift by 4 bits
        tmp.push_back(hex[*it & NIBBLE_MASK]); // Take the lower 4 bits
    }
    ret = tmp.substr(0, UUID_HEAD_LEN) + "-" +
        tmp.substr(UUID_START_POS_1, UUID_OTHER_LEN) + "-" +
        tmp.substr(UUID_START_POS_2, UUID_OTHER_LEN) + "-" +
        tmp.substr(UUID_START_POS_3, UUID_OTHER_LEN) + "-" +
        tmp.substr(UUID_START_POS_4);
    return ret;
}

namespace {
ErrCode FlushDocument(const std::unique_ptr<ObjectEditorDocument> &document)
{
    if (document == nullptr || !document->Flush()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "document flush failed");
        return ObjectEditorClientErrCode::CLIENT_PREPARE_FILES_ERROR;
    }
    return ObjectEditorClientErrCode::CLIENT_OK;
}
} // namespace

std::string ObjectEditorClient::GetTempDir(const std::unique_ptr<ObjectEditorDocument> &document)
{
    std::string sandboxPath = "";
    if (document == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "document is null");
        return sandboxPath;
    }
    std::shared_ptr<OHOS::AbilityRuntime::ApplicationContext> context =
        AbilityRuntime::Context::GetApplicationContext();
    if (context == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "context is null");
        return sandboxPath;
    }
    std::string fileDirPath = context->GetTempDir();
    if (fileDirPath.empty()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "get sandboxPath failed");
        return sandboxPath;
    }
    sandboxPath = fileDirPath + '/' + document->GetDocumentId();
    std::string canonicalFileName;
    if (!SystemUtils::ValidateAndNormalizePath(sandboxPath, canonicalFileName)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "Failed to validate and normalize path");
        return "";
    }
    OBJECT_EDITOR_LOGI(ObjectEditorDomain::CLIENT, "sandboxPath: %{private}s", canonicalFileName.c_str());
    return canonicalFileName;
}

ErrCode ObjectEditorClient::PrepareFiles(const std::unique_ptr<ObjectEditorDocument> &document)
{
    std::string sandboxPath = GetTempDir(document);
    if (sandboxPath.empty()) {
        return ObjectEditorClientErrCode::CLIENT_GET_PATH_ERROR;
    }
    fs::path targetDirPath(sandboxPath);
    std::error_code ec;
    bool result = fs::create_directories(targetDirPath, ec);
    if (!result || ec) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "create sandboxPath failed, ec: %{public}d", ec.value());
        return ObjectEditorClientErrCode::CLIENT_UNKNOWN_OPERATE;
    }
    if (document->GetOperateType() == OperateType::CREATE_BY_FILE) {
        OBJECT_EDITOR_LOGI(ObjectEditorDomain::CLIENT, "handle copy file");
        std::string source = document->GetOriFilePath();
        if (source.empty()) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "source is empty");
            return ObjectEditorClientErrCode::CLIENT_GET_PATH_ERROR;
        }
        if (document->GetLinking()) {
            document->SetNativeFileUri(SystemUtils::GetUriFromPath(source));
        } else {
            fs::path sourcePath(source);
            fs::path destPath = targetDirPath / sourcePath.filename().string();
            std::uintmax_t fileSize = fs::file_size(sourcePath, ec);
            if (ec) {
                OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "get file_size failed, ec: %{public}d", ec.value());
                return ObjectEditorClientErrCode::CLIENT_COPY_FILE_FAILED;
            }
            auto spaceInfo = fs::space(destPath.parent_path(), ec);
            if (ec) {
                OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "get space failed, ec: %{public}d", ec.value());
                return ObjectEditorClientErrCode::CLIENT_COPY_FILE_FAILED;
            }
            std::uintmax_t freeSpace = spaceInfo.available;
            if (freeSpace < fileSize + METADATA_BUFFER_SIZE) {
                OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT,
                    "freeSpace: %{public}" PRIu64 " fileSize: %{public}" PRIu64,
                    static_cast<uint64_t>(freeSpace), static_cast<uint64_t>(fileSize));
                return ObjectEditorClientErrCode::CLIENT_COPY_FILE_FAILED;
            }
            result = fs::copy_file(sourcePath, destPath, fs::copy_options::overwrite_existing, ec);
            if (!result || ec) {
                OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "copy source to sandboxPath failed, ec: %{public}d",
                    ec.value());
                return ObjectEditorClientErrCode::CLIENT_UNKNOWN_OPERATE;
            }
            document->SetNativeFileUri(SystemUtils::GetUriFromPath(destPath.string()));
        }
    }
    std::string canonicalFilePath;
    if (!SystemUtils::ValidateAndNormalizePath(sandboxPath, canonicalFilePath)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "Failed to validate and normalize path");
        return ObjectEditorClientErrCode::CLIENT_PREPARE_FILES_ERROR;
    }
    sandboxPath = canonicalFilePath;
    std::string snapshotFilePath = sandboxPath + "/snapshot.png";
    OBJECT_EDITOR_LOGI(ObjectEditorDomain::CLIENT, "snapshotFilePath is %{private}s", snapshotFilePath.c_str());
    std::ofstream snapshotFile(snapshotFilePath, std::ios::binary);
    if (!snapshotFile.is_open()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "open snapshotFilePath failed");
        return ObjectEditorClientErrCode::CLIENT_PREPARE_FILES_ERROR;
    }
    snapshotFile.close();
    document->SetSnapshotUri(SystemUtils::GetUriFromPath(snapshotFilePath));
    if (document->GetLinking()) {
        OBJECT_EDITOR_LOGI(ObjectEditorDomain::CLIENT, "ole.bin not need generate");
        return ObjectEditorClientErrCode::CLIENT_OK;
    }
    document->SetTmpFileUri(SystemUtils::GetUriFromPath(sandboxPath + "/ole.bin"));
    return FlushDocument(document);
}

ErrCode ObjectEditorClient::CleanupTempFiles(const std::unique_ptr<ObjectEditorDocument> &document)
{
    std::string sandboxPath = GetTempDir(document);
    if (sandboxPath.empty()) {
        return ObjectEditorClientErrCode::CLIENT_GET_PATH_ERROR;
    }
    std::error_code ec;
    if (!fs::exists(sandboxPath, ec) || !fs::is_directory(sandboxPath, ec)) {
        OBJECT_EDITOR_LOGI(ObjectEditorDomain::CLIENT, "sandboxPath not exist or not directory");
        return ObjectEditorClientErrCode::CLIENT_OK;
    }
    std::string snapshotPath = sandboxPath + "/snapshot.png";
    if (fs::exists(snapshotPath, ec)) {
        fs::remove(snapshotPath, ec);
        OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "remove snapshot result: %{public}d", ec.value());
    }
    std::string oleBinPath = sandboxPath + "/ole.bin";
    if (fs::exists(oleBinPath, ec)) {
        fs::remove(oleBinPath, ec);
        OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "remove oleBin result: %{public}d", ec.value());
    }
    if (document->GetOperateType() == OperateType::CREATE_BY_FILE && !document->GetLinking()) {
        std::string source = document->GetOriFilePath();
        if (source.empty()) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "source is empty");
            return ObjectEditorClientErrCode::CLIENT_GET_PATH_ERROR;
        }
        fs::path sourcePath(source);
        std::string destPath = sandboxPath + "/" + sourcePath.filename().string();
        if (fs::exists(destPath, ec)) {
            fs::remove(destPath, ec);
            OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "remove destPath result: %{public}d", ec.value());
        }
    }
    if (fs::exists(sandboxPath, ec) && fs::is_empty(sandboxPath, ec)) {
        fs::remove(sandboxPath, ec);
        OBJECT_EDITOR_LOGI(ObjectEditorDomain::CLIENT, "remove sandboxPath:%{private}s result: %{public}d",
            sandboxPath.c_str(), ec.value());
    }
    return ObjectEditorClientErrCode::CLIENT_OK;
}

ErrCode ObjectEditorClient::StopObjectEditorExtension(
    std::unique_ptr<ObjectEditorDocument> &document,
    const sptr<IObjectEditorService> &oeExtensionRemoteObject,
    const bool &isPackageExtension)
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    if (document == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "document is null");
        return ERR_INVALID_VALUE;
    }
    std::string documentId = document->GetDocumentId();
    if (documentId.empty()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "documentId is not initialized");
        return ERR_INVALID_VALUE;
    }
    HITRACE_METER_FMT(HITRACE_TAG_OHOS, "client::StopObjectEditorExtension");
    CleanupTempFiles(document);
    if (isPackageExtension) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "is package");
        return ObjectEditorClientErrCode::CLIENT_OK;
    }
    if (oeExtensionRemoteObject == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "extension is null");
        return ERR_INVALID_VALUE;
    }
    sptr<IObjectEditorManager> objectEditorManagerProxy = GetIObjectEditorManager();
    if (objectEditorManagerProxy == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "get proxy fail");
        return ERR_INVALID_VALUE;
    }
    sptr<IRemoteObject> remoteObject = oeExtensionRemoteObject->GetRemoteObject();
    ErrCode ret = objectEditorManagerProxy->StopObjectEditorExtension(documentId, remoteObject, isPackageExtension);
    if (ret != ERR_OK) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "proxy fail:%{public}d", ret);
        return ret;
    }
    return ERR_OK;
}

ErrCode ObjectEditorClient::QueryExtensionStopReason(const sptr<IRemoteObject> &oeExtensionRemoteObject,
    ExtensionStopReason &stopReason)
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    sptr<IObjectEditorManager> objectEditorManagerProxy = GetIObjectEditorManager();
    if (objectEditorManagerProxy == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "get proxy fail");
        return ERR_INVALID_VALUE;
    }
    ErrCode ret = objectEditorManagerProxy->QueryExtensionStopReason(oeExtensionRemoteObject, stopReason);
    if (ret != ERR_OK) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "failed:%{public}d", ret);
        return ret;
    }
    return ERR_OK;
}

ErrCode ObjectEditorClient::GetIcon(const std::string &oeid, std::string &resFilePath)
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    sptr<IObjectEditorManager> objectEditorManagerProxy = GetIObjectEditorManager();
    if (objectEditorManagerProxy == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "get proxy fail");
        return ERR_INVALID_VALUE;
    }
    ErrCode ret = objectEditorManagerProxy->GetIconByOEid(oeid, resFilePath);
    if (ret != ERR_OK) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "GetIconByOEid failed:%{public}d", ret);
        return ret;
    }
    return ERR_OK;
}

ErrCode ObjectEditorClient::GetFormatName(const std::string &oeid,
    const std::string &locale, std::string &formatName)
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    sptr<IObjectEditorManager> objectEditorManagerProxy = GetIObjectEditorManager();
    if (objectEditorManagerProxy == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "get proxy fail");
        return ERR_INVALID_VALUE;
    }
    ErrCode ret = objectEditorManagerProxy->GetFormatName(oeid, locale, formatName);
    if (ret != ERR_OK) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "GetFormatName failed:%{public}d", ret);
        return ret;
    }
    return ERR_OK;
}

ErrCode ObjectEditorClient::GetObjectEditorFormatByOEidAndLocale(
    const std::string &oeid,
    const std::string &locale,
    std::unique_ptr<ObjectEditorFormat> &format)
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    HITRACE_METER_FMT(HITRACE_TAG_OHOS, "client::GetObjectEditorFormatByOEidAndLocale");
    sptr<IObjectEditorManager> objectEditorManagerProxy = GetIObjectEditorManager();
    if (objectEditorManagerProxy == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "get proxy fail");
        return ERR_INVALID_VALUE;
    }
    ErrCode ret = objectEditorManagerProxy->GetObjectEditorFormatByOEidAndLocale(oeid, locale, format);
    if (ret != ERR_OK) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "failed:%{public}d", ret);
        return ret;
    }
    return ERR_OK;
}

ErrCode ObjectEditorClient::GetObjectEditorFormatsByLocale(const std::string &locale,
    std::vector<std::unique_ptr<ObjectEditorFormat>> &formats)
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::CLIENT, "in");
    HITRACE_METER_FMT(HITRACE_TAG_OHOS, "client::GetObjectEditorFormatsByLocale");
    sptr<IObjectEditorManager> objectEditorManagerProxy = GetIObjectEditorManager();
    if (objectEditorManagerProxy == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "get proxy fail");
        return ERR_INVALID_VALUE;
    }
    ErrCode ret = objectEditorManagerProxy->GetObjectEditorFormatsByLocale(locale, formats);
    if (ret != ERR_OK) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "failed:%{public}d", ret);
        return ret;
    }
    return ERR_OK;
}

void ObjectEditorClient::SARegCleanUp()
{
    OBJECT_EDITOR_LOGI(ObjectEditorDomain::CLIENT, "in");
    std::lock_guard<std::mutex> lock(proxyMutex_);
    UnsubscribeSystemAbility();
    if (oeSAProxy_ == nullptr) {
        return;
    }
    sptr<IRemoteObject> remoteObject = oeSAProxy_->AsObject();
    if (remoteObject != nullptr && deathRecipient_ != nullptr) {
        remoteObject->RemoveDeathRecipient(deathRecipient_);
    }
    oeSAProxy_ = nullptr;
}

ObjectEditorClient::~ObjectEditorClient()
{
    OBJECT_EDITOR_LOGI(ObjectEditorDomain::CLIENT, "destructor");
    SARegCleanUp();
}
// LCOV_EXCL_STOP
} // namespace ObjectEditor
} // namespace OHOS