/*
 * 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 <algorithm>
#include <array>
#include <cinttypes>
#include <cstddef>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <functional>
#include <limits>
#include <optional>
#include <random>
#include <sstream>
#include <utility>
#include <vector>

#include "application_context.h"

#include "dirtree.h"
#include "object_editor_document.h"

namespace OHOS {
namespace ObjectEditor {
namespace {
    constexpr std::size_t MAX_DEPTH = 256;
    constexpr std::size_t MAX_VISITS = 10000;
}
// LCOV_EXCL_START
namespace fs = std::filesystem;

ObjectEditorDocument::~ObjectEditorDocument()
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "destructor oeid: %{public}s, documentId: %{private}s",
        oeid_.c_str(), documentId_.c_str());
}

ObjectEditorDocument::ObjectEditorDocument(std::unique_ptr<Storage> storage,
    std::string tmpFilePath) noexcept
    : storage_(std::move(storage)),
      tmpFileUri_(SystemUtils::GetUriFromPath(tmpFilePath))
{
    if (!storage_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "storage is nullptr");
        return;
    }
    DirEntry *root = storage_->GetRootEntry();
    if (root) {
        oeid_ = FormatClsidToOEid(root->Clsid());
    }
}

std::unique_ptr<ObjectEditorDocument> ObjectEditorDocument::CreateByOEid(const std::string &oeid)
{
    OBJECT_EDITOR_LOGI(ObjectEditorDomain::DOCUMENT, "oeid: %{public}s", oeid.c_str());
    auto storage = std::make_unique<Storage>(oeid);
    if (!storage || storage->Result() != Storage::Ok) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "storage creation failed");
        return nullptr;
    }
    auto doc = std::make_unique<ObjectEditorDocument>(std::move(storage), std::string{});
    doc->oeid_ = oeid;
    doc->operateType_ = OperateType::CREATE_BY_OEID;
    return doc;
}

std::unique_ptr<ObjectEditorDocument> ObjectEditorDocument::CreateByFile(
    const std::string &path, [[maybe_unused]] bool isLinking)
{
    OBJECT_EDITOR_LOGI(ObjectEditorDomain::DOCUMENT, "path: %{private}s, isLinking: %{public}d",
        path.c_str(), isLinking);
    static constexpr char kDefaultOEid[] = "00000000000000000000000000000000";
    auto documentPtr = CreateByOEid(kDefaultOEid);
    if (!documentPtr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "CreateByOEid failed");
        return nullptr;
    }
    documentPtr->oriFileUri_ = SystemUtils::GetUriFromPath(path);
    documentPtr->operateType_ = OperateType::CREATE_BY_FILE;
    documentPtr->SetLinking(isLinking);
    return documentPtr;
}

std::unique_ptr<ObjectEditorDocument> ObjectEditorDocument::LoadFromFile(const std::string &path)
{
    OBJECT_EDITOR_LOGI(ObjectEditorDomain::DOCUMENT, "path: %{private}s", path.c_str());
    auto storage = std::make_unique<Storage>(path.c_str());
    if (!storage || storage->Result() != Storage::Ok) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "storage creation failed");
        return nullptr;
    }

    auto doc = std::make_unique<ObjectEditorDocument>(std::move(storage), std::string{});
    doc->operateType_ = OperateType::EDIT;
    doc->userTmpFilePath_ = path;
    return doc;
}

std::string ObjectEditorDocument::GetOEid() const
{
    return oeid_;
}

void ObjectEditorDocument::SetOEid(const std::string &oeid)
{
    oeid_ = oeid;
}

bool ObjectEditorDocument::FlushOEid()
{
    std::lock_guard<std::recursive_mutex> lock(docMutex_);
    if (!storage_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "storage is null");
        return false;
    }
    DirEntry *root = storage_->GetRootEntry();
    if (!root) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "root entry is null");
        return false;
    }

    const auto clsidOpt = ParseOEidToClsid(oeid_);
    if (!clsidOpt) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "parse oeid failed");
        return false;
    }

    root->SetClsid(*clsidOpt, std::size(*clsidOpt));
    return storage_->Flush();
}

std::string ObjectEditorDocument::GetOEidInternal() const
{
    if (!storage_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "storage is null");
        return std::string{};
    }
    DirEntry *root = storage_->GetRootEntry();
    if (!root) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "root entry is null");
        return std::string{};
    }
    return std::string(reinterpret_cast<const char*>(root->Clsid().data()), root->Clsid().size());
}

Storage *ObjectEditorDocument::GetRootStorage() noexcept
{
    std::lock_guard<std::recursive_mutex> lock(docMutex_);
    return storage_.get();
}

const Storage *ObjectEditorDocument::GetRootStorage() const noexcept
{
    std::lock_guard<std::recursive_mutex> lock(docMutex_);
    return storage_.get();
}

std::optional<std::string> ObjectEditorDocument::GetOriFileUri() const noexcept
{
    if (oriFileUri_.empty()) {
        return std::nullopt;
    }
    return oriFileUri_;
}

void ObjectEditorDocument::SetOriFileUri(const std::string &oriFileUri) noexcept
{
    oriFileUri_ = oriFileUri;
}

std::optional<std::string> ObjectEditorDocument::GetTmpFileUri() const noexcept
{
    if (tmpFileUri_.empty()) {
        return std::nullopt;
    }
    return tmpFileUri_;
}

void ObjectEditorDocument::SetTmpFileUri(const std::string &tmpFileUri) noexcept
{
    tmpFileUri_ = tmpFileUri;
}

std::optional<std::string> ObjectEditorDocument::GetNativeFileUri() const noexcept
{
    if (nativeFileUri_.empty()) {
        return std::nullopt;
    }
    return nativeFileUri_;
}

void ObjectEditorDocument::SetNativeFileUri(const std::string &nativeFileUri) noexcept
{
    nativeFileUri_ = nativeFileUri;
}

void ObjectEditorDocument::RestoreStorage()
{
    std::lock_guard<std::recursive_mutex> lock(docMutex_);
    storage_ = std::make_unique<Storage>(GetTmpFilePath().c_str());
}

bool ObjectEditorDocument::Flush()
{
    std::lock_guard<std::recursive_mutex> lock(docMutex_);
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "oeid: %{public}s, operateType: %{public}d",
        oeid_.c_str(), static_cast<int32_t>(operateType_));
    if (!storage_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "storage is null");
        return false;
    }
    if (ShouldRebuild()) {
        OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "Need rebuild before flush");
        return RebuildAndFlush();
    }
    const bool hasUserTmp = !userTmpFilePath_.empty();
    std::string tmpFilePath = GetTmpFilePath();
    const bool hasTmpFilePath = !tmpFilePath.empty();
    if (hasUserTmp && !hasTmpFilePath) {
        OBJECT_EDITOR_LOGI(ObjectEditorDomain::DOCUMENT, "Direct flush without temp file");
        return storage_->Flush();
    }

    if (hasUserTmp && hasTmpFilePath) {
        OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "Copy user temp file to temp file");
        if (storage_->IsDirty()) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "storage is dirty");
            return false;
        }
        std::filesystem::path sourcePath(userTmpFilePath_);
        std::filesystem::path targetPath(GetTmpFilePath());
        std::error_code copyEc;
        std::filesystem::copy_file(sourcePath, targetPath, std::filesystem::copy_options::overwrite_existing, copyEc);
        if (copyEc) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "copy file failed, error: %{public}s",
                copyEc.message().c_str());
            return false;
        }
        RestoreStorage();
        userTmpFilePath_.clear();
        return true;
    }
    if (hasTmpFilePath) {
        OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "Temp file path exists");
        if (std::filesystem::exists(std::filesystem::path(tmpFilePath))) {
            OBJECT_EDITOR_LOGI(ObjectEditorDomain::DOCUMENT, "Direct flush to existing file");
            return storage_->Flush();
        }
        return storage_->SaveToFile(tmpFilePath.c_str());
    }
    const auto base = std::filesystem::current_path();
    std::random_device rd;
    std::mt19937_64 generator(rd());
    std::uniform_int_distribution<uint64_t> distribution;

    static constexpr char kHexAlphabet[] = "0123456789abcdef";
    constexpr uint32_t ATTEMPT_TIMES = 64;
    constexpr uint32_t FILENAME_SUFFIX_SIZE = 16;
    for (uint32_t attempt = 0; attempt < ATTEMPT_TIMES; ++attempt) {
        auto value = distribution(generator);
        std::array<char, FILENAME_SUFFIX_SIZE + 1> suffix{};
        for (uint32_t index = 0; index < FILENAME_SUFFIX_SIZE; ++index) {
            suffix[FILENAME_SUFFIX_SIZE - 1 - index] = kHexAlphabet[value & 0x0Fu];
            value >>= 4U;
        }

        std::string filename = "oe-storage-";
        filename.append(suffix.data(), FILENAME_SUFFIX_SIZE);
        filename.append(".bin");
        std::filesystem::path candidate = base / filename;
        if (std::filesystem::exists(candidate)) {
            OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "candidate exists, try next");
            continue;
        }

        if (!storage_->SaveToFile(candidate.string().c_str())) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "save to file failed, candidate: %{private}s",
                candidate.string().c_str());
            return false;
        }
        tmpFileUri_ = SystemUtils::GetUriFromPath(candidate.string());
        OBJECT_EDITOR_LOGI(ObjectEditorDomain::DOCUMENT, "Generate and save temp file success, path: %{private}s",
            candidate.string().c_str());
        return true;
    }
    OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Failed to generate unique temp file name");
    return false;
}

namespace {
bool AtomicReplaceFile(const std::string &tempPath, const std::string &targetPath)
{
    std::error_code ec;
    return fs::copy_file(tempPath, targetPath, fs::copy_options::overwrite_existing, ec);
}
} // namespace

std::string GenerateTempPath(const std::string &targetPath, const std::string documentId)
{
    std::string sandboxPath = "";
    std::shared_ptr<OHOS::AbilityRuntime::ApplicationContext> context =
        AbilityRuntime::Context::GetApplicationContext();
    if (context == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "get context failed");
        return sandboxPath;
    }
    std::string fileDirPath = context->GetTempDir();
    if (fileDirPath.empty()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "get sandboxPath failed");
        return sandboxPath;
    }
    sandboxPath = fileDirPath + '/' + documentId;
    fs::path targetDirPath(sandboxPath);
    std::error_code ec;
    fs::create_directories(targetDirPath, ec);
    if (ec) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::CLIENT, "create directories failed, ec: %{public}d", ec.value());
        return "";
    }
    thread_local std::random_device rd;
    thread_local std::mt19937 gen(rd());
    constexpr uint32_t PATH_LEN = 16;
    thread_local std::uniform_int_distribution<> dis(0, PATH_LEN - 1);
    static constexpr char kHex[] = "0123456789abcdef";

    std::ostringstream ss;
    ss << sandboxPath << "/ole.bin.rebuilding.";
    for (uint32_t i = 0; i < PATH_LEN; ++i) {
        ss << kHex[dis(gen)];
    }
    return ss.str();
}

void ObjectEditorDocument::TraverseDirectory(const std::string &path, std::size_t depth,
    uint64_t &total, std::size_t &visitCount) const
{
    if (!storage_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "storage is null");
        return;
    }
    if (total == std::numeric_limits<uint64_t>::max()) {
        OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "total is max, stop traverse");
        return;
    }
    if (depth > MAX_DEPTH) {
        OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "depth is max, stop traverse");
        total = std::numeric_limits<uint64_t>::max();
        return;
    }
    if (visitCount++ >= MAX_VISITS) {
        OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "visit count is max, stop traverse");
        total = std::numeric_limits<uint64_t>::max();
        return;
    }

    std::string savedPath;
    storage_->Path(savedPath);
    std::vector<const DirEntry *> entries;
    if (!storage_->EnterDirectory(path)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "enter directory failed, path: %{private}s", path.c_str());
        return;
    }
    storage_->ListEntries(entries);
    if (!savedPath.empty()) {
        (void)storage_->EnterDirectory(savedPath);
    }

    for (const DirEntry *e : entries) {
        if (e == nullptr) {
            continue;
        }

        std::string fullPath = (path == "/") ? ("/" + e->Name()) : (path + "/" + e->Name());
        if (e->IsFile()) {
            const uint64_t size = e->Size();
            if (total > std::numeric_limits<uint64_t>::max() - size) {
                total = std::numeric_limits<uint64_t>::max();
                return;
            }
            total += size;
        } else if (e->IsDir()) {
            TraverseDirectory(fullPath, depth + 1, total, visitCount);
        }
    }
}

uint64_t ObjectEditorDocument::ComputeLiveDataSize() const
{
    uint64_t total = 0;
    std::size_t visitCount = 0;
    TraverseDirectory("/", 0, total, visitCount);
    return total;
}

bool ObjectEditorDocument::ShouldRebuild() const
{
    if (!storage_) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "storage is null");
        return false;
    }

    std::string targetPath = GetTmpFilePath();
    if (targetPath.empty()) {
        OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "tmp file path is empty, no rebuild");
        return false;
    }

    std::error_code ec;
    const auto fileSizeRaw = std::filesystem::file_size(targetPath, ec);
    if (ec || fileSizeRaw == 0) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "get file size failed, error: %{public}s",
            ec.message().c_str());
        return false;
    }
    if (fileSizeRaw > std::numeric_limits<uint64_t>::max()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "file size is max, stop traverse");
        return false;
    }

    const uint64_t fileSize = static_cast<uint64_t>(fileSizeRaw);
    const uint64_t liveDataSize = ComputeLiveDataSize();
    if (liveDataSize >= fileSize) {
        OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "live data size is greater than file size, no rebuild");
        return false;
    }

    const uint64_t wasteBytes = fileSize - liveDataSize;
    constexpr uint64_t MIN_WASTE_BYTES = 8ULL * 1024 * 1024;
    constexpr uint64_t MIN_FILE_SIZE = 64ULL * 1024;
    constexpr uint32_t NUM_BASE = 10;
    constexpr uint32_t WASTE_RATIO = 3;
    constexpr uint64_t kMaxSafeFileSize = std::numeric_limits<uint64_t>::max() / NUM_BASE;

    bool ratioCheck = false;
    if (fileSize > kMaxSafeFileSize) {
        ratioCheck = wasteBytes >= (fileSize / NUM_BASE) * WASTE_RATIO;
    } else {
        ratioCheck = wasteBytes * NUM_BASE >= fileSize * WASTE_RATIO;
    }

    return fileSize > MIN_FILE_SIZE && wasteBytes >= MIN_WASTE_BYTES && ratioCheck;
}

bool ObjectEditorDocument::CopyStreamData(Storage *src, Storage *dst, const std::string &path, uint64_t size)
{
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "path: %{private}s, size: %{public}" PRIu64,
        path.c_str(), size);
    if (!src || !dst) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "src is null or dst is null");
        return false;
    }
    Stream *dstStream = dst->GetStream(path, true);
    if (!dstStream) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "dst stream is null, path: %{private}s", path.c_str());
        return false;
    }
    if (size == 0) {
        OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "size is 0, skip copy");
        return true;
    }
    Stream *srcStream = src->GetStream(path, false);
    if (!srcStream) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "src stream is null, path: %{private}s", path.c_str());
        return false;
    }
    if (size > std::numeric_limits<size_t>::max()) {
        return false;
    }
    constexpr uint64_t kChunkSize = 1ULL * 1024 * 1024;
    const std::size_t bufferSize = static_cast<std::size_t>(std::min<uint64_t>(kChunkSize, size));
    std::vector<std::uint8_t> buffer(bufferSize);
    srcStream->Seek(0);
    dstStream->Seek(0);
    uint64_t remaining = size;
    OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "Copy loop start, bufferSize: %{public}zu", bufferSize);
    while (remaining > 0) {
        const std::size_t toRead = static_cast<std::size_t>(
            std::min<uint64_t>(remaining, static_cast<uint64_t>(buffer.size())));

        auto bytesRead = srcStream->Read(buffer.data(), static_cast<std::streamsize>(toRead));
        if (bytesRead <= 0 || bytesRead > static_cast<std::streamsize>(toRead)) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
                "Read out of range, expected: %{public}u, actual: %{public}" PRIu64 ", path: %{private}s",
                static_cast<uint32_t>(toRead), static_cast<uint64_t>(bytesRead), path.c_str());
            return false;
        }
        dstStream->Write(buffer.data(), static_cast<std::uint32_t>(bytesRead));
        if (dstStream->Fail()) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "dst stream fail, path: %{private}s", path.c_str());
            return false;
        }

        remaining -= static_cast<uint64_t>(bytesRead);
    }
    return true;
}

bool ObjectEditorDocument::CopyAllStreamRecursivelyImpl(Storage *src, Storage *dst,
    const std::string& path, std::size_t depth, std::size_t &visitCount)
{
    if (!src || !dst) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "src is null or dst is null");
        return false;
    }
    if (depth > MAX_DEPTH) {
        OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "depth is max, stop copy");
        return false;
    }
    if (visitCount++ >= MAX_VISITS) {
        OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "visit count is max, stop copy");
        return false;
    }
    std::string savedPath;
    src->Path(savedPath);
    std::vector<const DirEntry *> entries;
    if (!src->EnterDirectory(path)) {
        if (!savedPath.empty()) {
            (void)src->EnterDirectory(savedPath);
        }
        return false;
    }
    src->ListEntries(entries);
    if (!savedPath.empty()) {
        (void)src->EnterDirectory(savedPath);
    }
    for (const DirEntry *entry : entries) {
        if (!entry) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "entry is null");
            return false;
        }
        if (!entry->IsFile() && !entry->IsDir()) {
            OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "entry is not file or dir, skip");
            return false;
        }
        std::string fullPath = path;
        if (path != "/") {
            fullPath += "/";
        }
        fullPath += entry->Name();

        if (entry->IsDir()) {
            DirEntry *newDir = dst->GetStorage(fullPath, true);
            if (!newDir) {
                return false;
            }
            newDir->SetClsid(entry->Clsid(), std::size(entry->Clsid()));
            if (!CopyAllStreamRecursivelyImpl(src, dst, fullPath, depth + 1, visitCount))
                return false;
            continue;
        }

        if (!CopyStreamData(src, dst, fullPath, entry->Size()))
            return false;
    }
    return true;
}

bool ObjectEditorDocument::CopyAllStreamsRecursively(Storage *src, Storage *dst, const std::string &basePath)
{
    if (!src || !dst) {
        return false;
    }
    std::size_t visitCount = 0;
    return CopyAllStreamRecursivelyImpl(src, dst, basePath, 0, visitCount);
}

std::string GenerateSafeTempPath(const std::string &targetPath, const std::string &documentId)
{
    constexpr uint32_t ATTEMPT_COUNT = 64;
    std::string tempPath;
    for (uint32_t attempt = 0; attempt < ATTEMPT_COUNT; ++attempt) {
        tempPath = GenerateTempPath(targetPath, documentId);
        std::error_code existsEc;
        if (!std::filesystem::exists(tempPath, existsEc) && !existsEc) {
            return tempPath;
        }
        if (existsEc || attempt == ATTEMPT_COUNT - 1) {
            return "";
        }
    }
    return "";
}

struct TempGuard {
    std::string path;
    bool shouldClean = true;
    ~TempGuard()
    {
        if (shouldClean && !path.empty()) {
            std::error_code ec;
            std::filesystem::remove(path, ec);
        }
    }
};

bool ObjectEditorDocument::RebuildAndFlush()
{
    std::string targetPath = GetTmpFilePath();
    if (targetPath.empty()) {
        OBJECT_EDITOR_LOGD(ObjectEditorDomain::DOCUMENT, "No target path, direct flush");
        return storage_ && storage_->Flush();
    }

    std::string tempPath = GenerateSafeTempPath(targetPath, documentId_);
    if (tempPath.empty()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "GenerateSafeTempPath failed");
        return false;
    }
    TempGuard guard{tempPath, true};
    auto newDoc = ObjectEditorDocument::CreateByOEid(GetOEid());
    if (!newDoc) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "CreateByOEid failed, oeid: %{public}s", GetOEid().c_str());
        return false;
    }
    Storage *newStorage = newDoc->GetRootStorage();
    if (!newStorage) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "GetRootStorage failed");
        return false;
    }
    if (!newStorage->SaveToFile(tempPath.c_str())) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "SaveToFile failed, tempPath: %{private}s", tempPath.c_str());
        return false;
    }
    if (!CopyAllStreamsRecursively(storage_.get(), newStorage, "/")) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "CopyAllStreamsRecursively failed");
        return false;
    }
    if (!newStorage->Flush()) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "New storage flush failed");
        return false;
    }

    {
        auto verify = std::make_unique<ObjectEditor::Storage>(tempPath.c_str());
        if (!verify || verify->Result() != ObjectEditor::Storage::Ok) {
            OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Verify failed, tempPath: %{private}s", tempPath.c_str());
            return false;
        }
    }

    newDoc.reset();
    auto oldStorage = std::move(storage_);
    if (!AtomicReplaceFile(tempPath, targetPath)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT,
            "AtomicReplaceFile failed, tempPath: %{private}s, targetPath: %{private}s",
            tempPath.c_str(), targetPath.c_str());
        storage_ = std::move(oldStorage);
        return false;
    }
    guard.shouldClean = false;
    auto reloaded = std::make_unique<ObjectEditor::Storage>(targetPath.c_str());
    if (!reloaded || reloaded->Result() != ObjectEditor::Storage::Ok) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Reload failed, targetPath:%{private}s", targetPath.c_str());
        storage_.reset();
        return false;
    }
    storage_ = std::move(reloaded);
    OBJECT_EDITOR_LOGI(ObjectEditorDomain::DOCUMENT, "success, targetPath: %{private}s", targetPath.c_str());
    return true;
}

bool ObjectEditorDocument::Marshalling(Parcel &parcel) const
{
    if (!parcel.WriteString(oeid_)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Write oeid failed");
        return false;
    }
    if (!parcel.WriteBool(isLinking_)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Write isLinking failed");
        return false;
    }
    if (!parcel.WriteString(tmpFileUri_)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Write tmpFileUri failed");
        return false;
    }
    if (!parcel.WriteString(oriFileUri_)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Write oriFileUri failed");
        return false;
    }
    if (!parcel.WriteString(snapshotUri_)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Write snapshotUri failed");
        return false;
    }
    if (!parcel.WriteString(nativeFileUri_)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Write nativeFileUri failed");
        return false;
    }
    if (!parcel.WriteInt32(static_cast<int32_t>(operateType_))) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Write operateType failed");
        return false;
    }
    if (!parcel.WriteString(documentId_)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Write documentId failed");
        return false;
    }
    return true;
}

ObjectEditorDocument *ObjectEditorDocument::Unmarshalling(Parcel &parcel)
{
    ObjectEditorDocument *doc = new (std::nothrow) ObjectEditorDocument();
    if (doc == nullptr) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "Create ObjectEditorDocument failed");
        return nullptr;
    }
    doc->oeid_ = parcel.ReadString();
    doc->isLinking_ = false;
    if (!parcel.ReadBool(doc->isLinking_)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "read isLinking_ failed");
        delete doc;
        return nullptr;
    }
    doc->tmpFileUri_ = parcel.ReadString();
    doc->oriFileUri_ = parcel.ReadString();
    doc->snapshotUri_ = parcel.ReadString();
    doc->nativeFileUri_ = parcel.ReadString();
    int32_t operateTypeValue = 0;
    if (!parcel.ReadInt32(operateTypeValue)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "read operateType failed");
        delete doc;
        return nullptr;
    }
    if (operateTypeValue < 0 || operateTypeValue > static_cast<int32_t>(OperateType::EDIT)) {
        OBJECT_EDITOR_LOGE(ObjectEditorDomain::DOCUMENT, "invalid operateTypeValue: %{public}d", operateTypeValue);
        delete doc;
        return nullptr;
    }
    doc->operateType_ = static_cast<OperateType>(operateTypeValue);
    doc->documentId_ = parcel.ReadString();
    return doc;
}

std::string ObjectEditorDocument::GetDocumentId() const
{
    return documentId_;
}

void ObjectEditorDocument::SetDocumentId(const std::string &documentId)
{
    documentId_ = documentId;
}
// LCOV_EXCL_STOP
} // namespace ObjectEditor
} // namespace OHOS