//
// Created by 张雪明 <zhangxueming@uniontech.com> on 2024/11/19.
//

#include "ImmuBackupModule.h"
#include "utils/Utils.h"
#include "utils/global.h"
#include "utils/Device.h"
#include "common/CommonFunc.h"
#include <QString>
#include <QObject>
#include <QFile>
#include <QDesktopServices>

ImmuBackupModule::ImmuBackupModule(FrameProxyInterface *frame, ComDeepinDaemonUosrecoveryInterface *interface,
                           QObject *parent)
        : QObject(parent),
          ModuleInterface(frame, interface)
{

}

ImmuBackupModule::~ImmuBackupModule()
{

}

void ImmuBackupModule::initialize()
{
    if (m_backupWidget == nullptr) {
        m_backupWidget = new ImmuBackupWidget();
        m_backupWidget->setLVM(m_isLVMOnly);
        m_backupWidget->setEncrypted(m_isEncrypted);
        m_backupWidget->setUserDataSyncType(m_userDataSyncType);
        m_backupWidget->setImmutable(m_isImmutable);
    }
    QSize iconSize(149,149);
    QString ghostProgressIconPath = ":/resources/icons/v25/ghost_progress.png";
    m_ghostProgressIcon = Utils::hidpiPixmap(ghostProgressIconPath, iconSize);

    connect(m_backupWidget, &ImmuBackupWidget::notifySystemBackup, [=] {
        this->checkAdminAuthority(AuthorityType::ImmuSystemBackup);
    });
    connect(m_backupWidget, &ImmuBackupWidget::notifySystemClone, [=] {
        this->checkAdminAuthority(AuthorityType::GhostBackup);
    });
    connect(m_backupWidget, &ImmuBackupWidget::notifyDataBackup, [=] {
        this->checkCommonAuthority(AuthorityType::DataBackup);
    });
    connect(m_recoveryInterface, &ComDeepinDaemonUosrecoveryInterface::ReportProgress,
            this, &ImmuBackupModule::updateProgress);
    connect(m_recoveryInterface, &ComDeepinDaemonUosrecoveryInterface::Error,
            this, &ImmuBackupModule::onError);
    connect(m_recoveryInterface, &ComDeepinDaemonUosrecoveryInterface::ReportSpace,
            this, &ImmuBackupModule::onSpaceChanged);
    connect(m_recoveryInterface, &ComDeepinDaemonUosrecoveryInterface::ReportCheckSpace,
            this, &ImmuBackupModule::onReportCheckSpace);
    connect(m_recoveryInterface, &ComDeepinDaemonUosrecoveryInterface::Success,
            this, &ImmuBackupModule::onSuccess);
}

QString ImmuBackupModule::name() const
{
    return "BackupModule";
}

void ImmuBackupModule::active()
{
    m_frameProxy->popAllWidget();
    m_frameProxy->setCurrentWidget(this, m_backupWidget);
}

QString ImmuBackupModule::icons() const
{
    return QString(":/resources/icons/backup_module.png");
}

QString ImmuBackupModule::text() const
{
    return QString(tr("Backup"));
}

void ImmuBackupModule::checkAdminAuthority(AuthorityType type)
{
    if (nullptr == m_adminAuthUtils) {
        m_adminAuthUtils = new AuthorityUtils();
        connect(m_adminAuthUtils, &AuthorityUtils::notifyAdminAuthority, this, &ImmuBackupModule::doAdminAuthorityTask);
    }

    m_adminAuthUtils->authorization(type);
}

void ImmuBackupModule::doAdminAuthorityTask(bool result, int type)
{
    if (result) {
        AuthorityType authType = static_cast<AuthorityType>(type);
        if (AuthorityType::ImmuSystemBackup == authType) {
            return this->onSystemBackup();
        } else if (AuthorityType::GhostBackup == authType) {
            return this->onGhostBackup();
        }
    }
}

void ImmuBackupModule::checkCommonAuthority(AuthorityType type)
{
    if (nullptr == m_commonAuthUtils) {
        m_commonAuthUtils = new AuthorityUtils();
        connect(m_commonAuthUtils, &AuthorityUtils::notifyCommonUserAuthority, this, &ImmuBackupModule::doCommonAuthorityTask);
    }

    m_commonAuthUtils->checkCommonUserAuthentication(type);
}

void ImmuBackupModule::doCommonAuthorityTask(bool result, int type)
{
    if (result) {
        AuthorityType authType = static_cast<AuthorityType>(type);
        if (AuthorityType::DataBackup == authType) {
            return this->onDataBackup();
        }
    }
}

void ImmuBackupModule::onShowProgress(OperateType opType, const QString &mainTitle, const QString &subTitle, const QString &warning)
{
    if (m_progressWidget == nullptr) {
        m_progressWidget = new ProgressWidgetV2(mainTitle, subTitle, warning);
    } else {
        m_progressWidget->setMainTitle(mainTitle);
        m_progressWidget->setSubTitle(subTitle);
        m_progressWidget->setWarning(warning);
    }
    switch (opType) {
        case OperateType::GhostBackup: {
            m_progressWidget->setIconPixmap(m_ghostProgressIcon);
            break;
        }
        default:
            m_progressWidget->setIconPixmap(m_ghostProgressIcon);
            break;
    }
    m_progressWidget->setValue(0);
    m_frameProxy->setCurrentWidget(this, m_progressWidget);

    m_frameProxy->setMenuDisabled(true);
    m_frameProxy->setQuitMenuDisabled(true);
    m_frameProxy->setWindowFuncClose(false);
    m_frameProxy->enableBackWard(false);
    m_frameProxy->enableModule(false);
}

void ImmuBackupModule::updateProgress(const QString &progress)
{
    QJsonObject jsonObject = Utils::QStringToJson(progress);
    int operateType = jsonObject.value("operateType").toInt();
    if ((operateType != static_cast<int>(OperateType::SystemBackup)) &&
        (operateType != static_cast<int>(OperateType::ImmutableSystemBackup)) &&
        (operateType != static_cast<int>(OperateType::UserDataBackup)) &&
        (operateType != static_cast<int>(OperateType::GhostBackup))) {
        return;
    }

    QString curOperateID = jsonObject.value("operateID").toString();
    if ((operateType == static_cast<int>(OperateType::SystemBackup) && m_curSysOpID != curOperateID) ||
        (operateType == static_cast<int>(OperateType::ImmutableSystemBackup) && m_curSysOpID != curOperateID) ||
        (operateType == static_cast<int>(OperateType::GhostBackup) && m_curSysOpID != curOperateID) ||
        (operateType == static_cast<int>(OperateType::UserDataBackup) && m_curUserOpID != curOperateID)) {
        return;
    }

    if (m_progressWidget != nullptr) {
        auto curProgress = jsonObject.value("progress").toInt();
        auto remainSecond = jsonObject.value("remainSecond").toInt();
        m_progressWidget->setRemainTime(remainSecond);
        m_progressWidget->setValue(curProgress);
        if (curProgress >= 100) {
            if (operateType == static_cast<int>(OperateType::GhostBackup)) {
                onGhostShowResult(true, operateType);
            } else {
                onShowResult(true, operateType);
            }
        }
    }
}

void ImmuBackupModule::onShowResult(bool success, int operateType, const QString &errorMsg, const QString &customResultText)
{
    if (operateType != static_cast<int>(OperateType::SystemBackup) &&
        operateType != static_cast<int>(OperateType::ImmutableSystemBackup) &&
        operateType != static_cast<int>(OperateType::UserDataBackup)) {
        return;
    }

    QString resultText = "";
    if (!customResultText.isEmpty()) {
        resultText = customResultText;
    } else {
        resultText = success ? tr("Backup successful!") : tr("Sorry, backup failed!");
    }
    QString btnText = tr("OK", "button");

    m_frameProxy->setMenuDisabled(false);
    m_frameProxy->setQuitMenuDisabled(false);
    m_frameProxy->setWindowFuncClose(true);

    if (m_progressWidget != nullptr) {
        m_frameProxy->popWidget();
    }

    if (m_resultWidget == nullptr) {
        m_resultWidget = new ResultWidgetV2(operateType, success, resultText, errorMsg, btnText, false);
        connect(m_resultWidget, &ResultWidgetV2::done, this, &ImmuBackupModule::onBackHome);
    } else {
        m_resultWidget->set(operateType, success, resultText, errorMsg, btnText, false);
    }

    m_frameProxy->setCurrentWidget(this, m_resultWidget);
    m_frameProxy->enableBackWard(true);
    m_frameProxy->enableModule(true);
}

void ImmuBackupModule::onShowResult(bool success, const Response &rsp)
{
    QString err = tr("Operation failed,error code: %1").arg(rsp.errCodeStr);
    QString resultText = success ? tr("Backup successful!") : tr("Sorry, backup failed!");
    if (rsp.operateType == OperateType::ImmutableSystemBackup) {
        resultText = err;
    }
    int operateType = static_cast<int>(rsp.operateType);
    QString btnText = tr("OK", "button");

    m_frameProxy->setMenuDisabled(false);
    m_frameProxy->setQuitMenuDisabled(false);
    m_frameProxy->setWindowFuncClose(true);

    if (m_progressWidget != nullptr) {
        m_frameProxy->popWidget();
    }

    if (m_resultWidget == nullptr) {
        m_resultWidget = new ResultWidgetV2(operateType, success, resultText, rsp.errMsg, btnText, false);
        connect(m_resultWidget, &ResultWidgetV2::done, this, &ImmuBackupModule::onBackHome);
    } else {
        m_resultWidget->set(operateType, success, resultText, rsp.errMsg, btnText, false);
    }

    m_frameProxy->setCurrentWidget(this, m_resultWidget);
    m_frameProxy->enableBackWard(true);
    m_frameProxy->enableModule(true);
}

void ImmuBackupModule::onGhostShowResult(bool success, int operateType, const QString &errorMsg)
{
    if (operateType != static_cast<int>(OperateType::GhostBackup)) {
        return;
    }

    QString resultText = success ? tr("Creation successful!") : tr("Sorry, creation failed!");
    QString btnText = tr("OK", "button");

    m_frameProxy->setMenuDisabled(false);
    m_frameProxy->setQuitMenuDisabled(false);
    m_frameProxy->setWindowFuncClose(true);

    if (m_progressWidget != nullptr) {
        m_frameProxy->popWidget();
    }

    if (m_resultWidget == nullptr) {
        m_resultWidget = new ResultWidgetV2(operateType, success, resultText, errorMsg, btnText, false);
        connect(m_resultWidget, &ResultWidgetV2::done, this, &ImmuBackupModule::onBackHome);
        connect(m_resultWidget, &ResultWidgetV2::viewBtnClicked, this, [=](){
            // 显示ghost镜像路径
            QString url = "file://" + m_ghostWidget->getDestDirText();
            if (!QDesktopServices::openUrl(QUrl(url))) {
                qWarning()<<"openUrl failed, url: "<<url;
            }
        });
    } else {
        m_resultWidget->set(operateType, success, resultText, errorMsg, btnText, false);
    }

    m_resultWidget->showViewButton(success, tr("View Image File"), "");
    QString msg = tr("Please create a folder name 'clone' on the USB driver, and move the uimg file into the folder. When installing the system, please select 'Install uimg', import the uimg file for installation, the clone data will be installed on the other device.");
    m_resultWidget->showIntroductionMsg(success, msg, "");

    m_frameProxy->setCurrentWidget(this, m_resultWidget);
    m_frameProxy->enableBackWard(true);
    m_frameProxy->enableModule(true);
}

void ImmuBackupModule::onSystemBackup()
{
    if (m_systemBackupWidget == nullptr) {
        m_systemBackupWidget = new ImmuSystemBackupWidget();
//        if (m_systemSyncType == static_cast<int>(RecoveryType::OSTree) ||
//            m_systemSyncType == static_cast<int>(RecoveryType::ImmutableMode)) {
//            m_systemBackupWidget->setDestBtnEnabled(false);
//        }

        connect(m_systemBackupWidget, &ImmuSystemBackupWidget::cancel, this, &ImmuBackupModule::onBack);
        connect(m_systemBackupWidget, &ImmuSystemBackupWidget::start, this,
                &ImmuBackupModule::onStartSystemBackup);
        connect(m_systemBackupWidget, &ImmuSystemBackupWidget::notifySetDestPartition, [=] {
            m_frameProxy->showSettingDialog();
        });
        connect(m_systemBackupWidget, &ImmuSystemBackupWidget::notifyBackupManage, [=] {
            m_frameProxy->showBackupFileManagerDialog();
        });
    }

    auto rootUUIDReply = m_recoveryInterface->GetRootUUID();
    rootUUIDReply.waitForFinished();
    if (!rootUUIDReply.isValid()) {
        qCritical() << rootUUIDReply.error();
        onShowResult(false, static_cast<int>(OperateType::SystemBackup),
                     tr("DBus error, please try again"));
        return;
    }

    auto destUUIDReply = m_recoveryInterface->GetBackupDeviceUUID(m_frameProxy->rootUUID());
    destUUIDReply.waitForFinished();
    if (!destUUIDReply.isValid()) {
        qCritical() << destUUIDReply.error();
        onShowResult(false, static_cast<int>(OperateType::SystemBackup),
                     tr("DBus error, please try again"));
        return;
    }

    m_destUUID = destUUIDReply.value();
    if (!m_destUUID.isEmpty()) {
        //qInfo() << "destUUID=" << m_destUUID;
        auto partitionReply = m_recoveryInterface->GetPartition(m_destUUID);
        partitionReply.waitForFinished();
        if (!partitionReply.isValid()) {
            qCritical() << "GetPartition failed, err: "<<partitionReply.error();
            return;
        }
        QString partitionInfo = partitionReply.value();
        m_systemBackupWidget->setDestPartitionText(Utils::QStringToJson(partitionInfo));
    }
    m_systemBackupWidget->setNotes("");

    m_frameProxy->setCurrentWidget(this, m_systemBackupWidget);
}

void ImmuBackupModule::onGhostBackup()
{
    if (nullptr == m_ghostWidget) {
        m_ghostWidget = new ImmuGhostWidget();

        connect(m_ghostWidget, &ImmuGhostWidget::cancel, this, &ImmuBackupModule::onBack);
        connect(m_ghostWidget, &ImmuGhostWidget::checkSpaceSignal, this, &ImmuBackupModule::onGhostCheckSpace, Qt::QueuedConnection);
        connect(m_ghostWidget, &ImmuGhostWidget::start, this, &ImmuBackupModule::onStartGhostBackup);
    }

    m_ghostWidget->setResultInfo("");
    m_ghostWidget->setStoreItemInfo(0, 0);
    m_ghostWidget->setStartEnable(false);
    m_ghostWidget->setDefaultStorePathTips();
    m_ghostWidget->setGhostItemTips(tr("Select a directory to save the .uimg image file"));
    m_ghostWidget->resetLayout(LayoutType::tipsLayout);
    m_ghostWidget->setGhostItemTipsLabelIcon(2);

    m_frameProxy->setCurrentWidget(this, m_ghostWidget);
}

void ImmuBackupModule::onSuccess(const QString &msg)
{
    QJsonObject jsonObject = Utils::QStringToJson(msg);
    int operateType = jsonObject.value("operateType").toInt(-1);
    OperateType opTye = static_cast<OperateType>(operateType);
    if ((opTye != OperateType::SystemBackupV20) && (opTye != OperateType::ManualBackupV20) &&
        (opTye != OperateType::CancelBackupV20) && (opTye != OperateType::ImmutableSystemBackup) &&
        (opTye != OperateType::GhostBackup)) {
        return;
    }

    QString curOperateID = jsonObject.value("operateID").toString();
    if (m_curSysOpID != curOperateID) {
        return;
    }

    if (opTye == OperateType::ImmutableSystemBackup) {
        onShowResult(true, static_cast<int>(OperateType::ImmutableSystemBackup), "");
    } else if (opTye == OperateType::GhostBackup) {
        onGhostShowResult(true, static_cast<int>(OperateType::GhostBackup), "");
    }

    m_frameProxy->enableBackWard(true);
    m_frameProxy->enableModule(true);
    m_frameProxy->setWindowFuncClose(true);
}

void ImmuBackupModule::setSelinuxEnable(bool enable)
{
    m_isSelinuxEnabled = enable;
}

void ImmuBackupModule::setLVM(bool isLVM)
{
    m_isLVMOnly = isLVM;
}

void ImmuBackupModule::setEncrypted(bool isEncrypted)
{
    m_isEncrypted = isEncrypted;
}

void ImmuBackupModule::setDevice(bool isDevice)
{
    m_isDevice = isDevice;
}

void ImmuBackupModule::setSupportV20BackupRestore(bool isSupport)
{
    m_isSupportV20BackupRestore = isSupport;
}

void ImmuBackupModule::setFileMgrEncrypted(bool encrypt)
{
    m_isFileMgrEncrypted = encrypt;
}

void ImmuBackupModule::setMajorVersion(int version)
{
    m_osMajorVer = version;
}

void ImmuBackupModule::setMinorVersion(int version)
{
    m_osMinorVer = version;
}

void ImmuBackupModule::setImmutable(bool isImmutable)
{
    m_isImmutable = isImmutable;
}

void ImmuBackupModule::setSystemBackupSpaceTips(const QString &tips)
{
   // if (m_systemBackupWidget != nullptr) {
   //     m_systemBackupWidget->setTips(tips);
   // }
}

void ImmuBackupModule::onStartSystemBackup(const QString &remark)
{
    if (Utils::isEnableImmutableSystemWritable()) {
        onShowResult(false, static_cast<int>(OperateType::ImmutableSystemBackup), "",
                    tr("System backup failed. This operation cannot be performed in the current restoration environment.")); 
        return;
    }

    if (m_recoveryInterface->IsRunning()) {
        QString operateID;
        QString userName;
        int opType;
        int progress;
        m_recoveryInterface->GetContext(operateID, userName, opType, progress);
        QString opTypeDes = Common::GetOperateTypeDes(opType);
        QString tips = tr("Task conflict, user %1's task %2 has not been completed").arg(userName).arg(opTypeDes);
        m_systemBackupWidget->setTips(tips);
        m_systemBackupWidget->setTipsStyleSheet("QLabel {"
                                                "color: #FF5736;"
                                                "}");
        qWarning()<<"onStartSystemBackup, another task is running, operateID = "<<operateID
                  <<", opType = "<<opType<<", progress = "<<progress;
        return;
    }

    m_curSysBackupReq.clear();
    m_frameProxy->setMenuDisabled(true);
    m_frameProxy->setQuitMenuDisabled(true);
    m_frameProxy->setWindowFuncClose(false);

    SystemBackupRequest request;
    request.userName = Utils::getUserName();
    request.rootUUID = m_frameProxy->rootUUID();
    request.destUUID = m_destUUID;
    request.remark = remark;
    request.operateID = QUuid::createUuid().toString();
    request.snapshotName = Utils::getOSVersion();
    m_curSysOpID = request.operateID;
    QJsonObject jsonObject = request.marshal();

    m_systemBackupWidget->setTips("");
    if (Utils::isImmutableSystem()) {
        auto checkSpaceReply = m_recoveryInterface->CheckIncSystemBackupSpace(request);
        checkSpaceReply.waitForFinished();
        if (!checkSpaceReply.isValid()) {
            m_curSysOpID.clear();
            qCritical() << Q_FUNC_INFO << " checkSpaceReply isvalid!";
            onShowResult(false, static_cast<int>(OperateType::ImmutableSystemBackup),
                         tr("DBus error, please try again"));
            return;
        }
#if 0
        auto reply = m_recoveryInterface->SystemBackup(request);
        reply.waitForFinished();
        if (!reply.isValid()) {
            m_curSysOpID.clear();
            qCritical() << Q_FUNC_INFO << " SystemBackup isvalid!";
            onShowResult(false, static_cast<int>(OperateType::ImmutableSystemBackup),
                         tr("DBus error, please try again"));
            return;
        }

        int replayValue = reply.value();
        if (replayValue != ErrorCode::OK) {
            qInfo()<<"SystemBackup replayValue = "<<replayValue;
            // TODO: 可以根据具体的错误码显示更详细的错误提示信息，先简单显示
            onShowResult(false, static_cast<int>(OperateType::ImmutableSystemBackup),
                         tr("Sorry, backup failed!"));
            return;
        }

        onShowProgress(OperateType::ImmutableSystemBackup,
                       tr("Backing up..."),
                       tr("Time remaining:") + " ",
                       tr("To avoid data loss, please do not use your computer during the process."));
        return;
#endif
    } else {
        auto checkSpaceReply = m_recoveryInterface->CheckIncSystemBackupSpace(request);
        checkSpaceReply.waitForFinished();
        if (!checkSpaceReply.isValid()) {
            m_curSysOpID.clear();
            qCritical() << Q_FUNC_INFO << " checkSpaceReply isvalid!";
            onShowResult(false, static_cast<int>(OperateType::SystemBackup),
                         tr("DBus error, please try again"));
            return;
        }
    }

    m_curSysBackupReq = Utils::JsonToQString(jsonObject);
    m_curSystemBackupRequest = request;
}

void ImmuBackupModule::onDataBackup()
{
    if (m_isSelinuxEnabled && Common::isSecurityEnhanced()) {
        QString securityMsg = tr("The system has enabled security management and the backup and restore function cannot be used. Please contact the administrator.");
        QString btnText = tr("OK", "button");
        if (m_resultWidget == nullptr) {
            m_resultWidget = new ResultWidgetV2(static_cast<int>(OperateType::UserDataBackup), true, securityMsg, "", btnText, true);
            connect(m_resultWidget, &ResultWidgetV2::done, this, &ImmuBackupModule::onBackHome);
        } else {
            m_resultWidget->set(static_cast<int>(OperateType::UserDataBackup), true, securityMsg, "", btnText, true);
        }

        m_frameProxy->setCurrentWidget(this, m_resultWidget);
        m_frameProxy->enableBackWard(true);
        m_frameProxy->enableModule(true);
        m_frameProxy->setWindowFuncClose(true);
        return;
    }

    if (m_dataBackupWidget == nullptr) {
        m_dataBackupWidget = new ImmuDataBackupWidget();
        connect(m_dataBackupWidget, &ImmuDataBackupWidget::cancel, this, &ImmuBackupModule::onBack);
        connect(m_dataBackupWidget, &ImmuDataBackupWidget::start,
                this, &ImmuBackupModule::onDataBackupCheckSpace);
    } else {
        m_dataBackupWidget->resetWidget();
    }

    auto partitionReply = m_recoveryInterface->ListPartition(true);
    partitionReply.waitForFinished();
    if (!partitionReply.isValid()) {
        qCritical() << "ListPartition err, "<< partitionReply.error();
        onShowResult(false, static_cast<int>(OperateType::UserDataBackup),
                     tr("DBus error, please try again"));
        return;
    }

    m_dataBackupWidget->setDestDevice(Utils::QStringToJson(partitionReply.value()));
    m_frameProxy->setCurrentWidget(this, m_dataBackupWidget);
}

QStringList ImmuBackupModule::getUsrDataSpecialExcludes()
{
    QStringList excludes = {
            ".config/browser/Default/virtual"
    };

    return excludes;
}

void ImmuBackupModule::onStartDataBackup(const QString &remark)
{
    UserDataBackupRequest request;
    request.username = Utils::getUserName();
    request.remark = remark;
    request.destUUID = m_destUUID;
    request.operateID = QUuid::createUuid().toString();
    m_curUserOpID = request.operateID;
    if (!m_destUUID.isEmpty()) {
        m_frameProxy->setMenuDisabled(true);
        m_frameProxy->setQuitMenuDisabled(true);
        m_frameProxy->setWindowFuncClose(false);

        request.rootUUID = m_frameProxy->rootUUID();
        request.exclude = m_excludes;
        //    QJsonObject jsonObject = request.marshal();
        auto requestReply = m_recoveryInterface->UserDataBackup(request);
        requestReply.waitForFinished();
        if (!requestReply.isValid()) {
            m_curUserOpID.clear();
            qCritical() << requestReply.error();
            onShowResult(false, static_cast<int>(OperateType::UserDataBackup), tr("DBus error, please try again"));
            return;
        }
        if (requestReply.value() != OK) {
            m_curUserOpID.clear();
            onShowResult(false, static_cast<int>(OperateType::UserDataBackup));
            return;
        }
        onShowProgress(OperateType::UserDataBackup,
                       tr("Backing up..."),
                       tr("Time remaining:") + " ",
                       tr("To avoid data loss, please do not use your computer during the process."));
    }
}

void ImmuBackupModule::onBack()
{
    if (m_frameProxy) {
        m_frameProxy->back();
    }
}

void ImmuBackupModule::onBackHome()
{
    if (nullptr != m_frameProxy) {
        m_frameProxy->backHome();
    }
}

void ImmuBackupModule::updateDestPartition(const QJsonObject &jsonObject)
{
#if 0
    if (m_systemBackupWidget != nullptr) {
        m_systemBackupWidget->setDestPartitionText(jsonObject);
    }

    if (jsonObject.contains("uuid")) {
        m_destUUID = jsonObject.value("uuid").toString();
    }
#endif
}


void ImmuBackupModule::onError(const QString &errMsg)
{
    QJsonObject jsonObject = Utils::QStringToJson(errMsg);
    Response rsp;
    rsp.unmarshal(jsonObject);
    QString errInfo = "Unknown";
    if (jsonObject.contains("errMsg")) {
        errInfo = jsonObject.value("errMsg").toString();
    }

    int opType = -1;
    if (jsonObject.contains("operateType")) {
        opType = jsonObject.value("operateType").toInt(-1);
    }
    QString curOperateID = jsonObject.value("operateID").toString();

    OperateType operateType = static_cast<OperateType>(opType);
    if ((operateType == OperateType::CheckUserDataBackupSpace && m_curUserOpID != curOperateID) ||
        (operateType == OperateType::UserDataBackup && m_curUserOpID != curOperateID) ||
        (operateType == OperateType::GhostBackup && m_curSysOpID != curOperateID) ||
        (operateType == OperateType::ImmutableSystemBackup && m_curSysOpID != curOperateID)) {
        return;
    }

    if (!(operateType == OperateType::CheckUserDataBackupSpace ||
        operateType == OperateType::UserDataBackup ||
        operateType == OperateType::GhostBackup ||
        operateType == OperateType::ImmutableSystemBackup)) {
        return;
    }
    qCritical()<<"BackupModule::onError, errMsg = "<<errMsg.toLocal8Bit().data();
    m_curUserOpID.clear();
    m_curSysOpID.clear();
    if (operateType == OperateType::GhostBackup) {
        onGhostShowResult(false, opType, errInfo);
    } else if (operateType == OperateType::ImmutableSystemBackup) {
        onShowResult(false, rsp);
    } else {
        onShowResult(false, opType, errInfo);
    }
}

void ImmuBackupModule::setSystemSyncType(int type)
{
    m_systemSyncType = type;
}

void ImmuBackupModule::setUserDataSyncType(int type)
{
    m_userDataSyncType = type;
}

void ImmuBackupModule::doDataBackup(const QString &totalSizeBytes)
{
    if (nullptr == m_dataBackupWidget) {
        qCritical() << "doDataBackup, m_dataBackupWidget is nullptr";
        return;
    }

    UserDataBackupRequest request;
    request.username = Utils::getUserName();
    request.remark = m_dataBackupWidget->getNote();
    request.destUUID = m_destUUID;
    request.strTotalSizeBytes = totalSizeBytes;
    request.operateID = QUuid::createUuid().toString();
    m_curUserOpID = request.operateID;
    if (!m_destUUID.isEmpty()) {
        m_frameProxy->setMenuDisabled(true);
        m_frameProxy->setQuitMenuDisabled(true);
        m_frameProxy->setWindowFuncClose(false);

        request.rootUUID = m_frameProxy->rootUUID();
        request.exclude = m_excludes;
        request.filesFrom = m_dataBackupWidget->getBackupFiles();
        auto requestReply = m_recoveryInterface->UserDataBackup(request);
        requestReply.waitForFinished();
        if (!requestReply.isValid()) {
            qCritical() << requestReply.error();
            onShowResult(false, static_cast<int>(OperateType::UserDataBackup), tr("DBus error, please try again"));
            return;
        }
        if (requestReply.value() != OK) {
            onShowResult(false, static_cast<int>(OperateType::UserDataBackup));
            return;
        }
        onShowProgress(OperateType::UserDataBackup,
                       tr("Backing up..."),
                       tr("Time remaining:") + " ",
                       tr("To avoid data loss, please do not use your computer during the process."));
    }
}

void ImmuBackupModule::onSpaceChanged(const QString &space)
{
    QJsonObject jsonObject = Utils::QStringToJson(space);
    int operateType = jsonObject.value("operateType").toInt();
    QString curOperateID = jsonObject.value("operateID").toString();
    if (operateType == static_cast<int>(OperateType::CheckUserDataBackupSpace) && (curOperateID != m_curUserOpID)) {
        return;
    }
    qWarning()<<"onSpaceChanged, space: "<<space.toLocal8Bit().data();

    if (operateType == static_cast<int>(OperateType::CheckUserDataBackupSpace)) {
        m_frameProxy->setMenuDisabled(false);
        m_frameProxy->setQuitMenuDisabled(false);
        m_frameProxy->setWindowFuncClose(true);
        bool hasSpace = false;
        if (jsonObject.contains("hasSpace")) {
            hasSpace = jsonObject.value("hasSpace").toBool();
        }

        if (!hasSpace) {
            if (nullptr != m_dataBackupWidget) {
                m_dataBackupWidget->setButtonEnable(true);
                m_dataBackupWidget->stopSpinner();
                m_dataBackupWidget->setTips(tr("Insufficient disk space on the device"));
                m_dataBackupWidget->setTipsStyleSheet("QLabel {"
                                                      "color: #FF5736;"
                                                      "}");
            }
            return;
        }

        QString totalSizeBytes = jsonObject.value("totalSizeBytes").toString();

        return this->doDataBackup(totalSizeBytes);
    } else if (operateType == static_cast<int>(OperateType::CheckGhostBackupSpace)) {
        QString spaceOKTips = tr("Enough available space in selected directory");
        QString insufficientSpaceTips = tr("Insufficient space in the selected directory. Please reselect one.");
        // 处理提示
        int errCode = jsonObject.value("errCode").toInt();
        QString destPath = jsonObject.value("destDir").toString();
        quint64 totalSizeBytes = jsonObject.value("totalSizeBytes").toVariant().toLongLong();
        quint64 usedSizeBytes = jsonObject.value("usedSizeBytes").toVariant().toLongLong();
        m_ghostWidget->resetLayout(LayoutType::tipsLabelLayout);
        m_ghostWidget->setCancelEnable(true);
        if (errCode == OK) {
            m_backupSizeBytes = jsonObject.value("backupSizeBytes").toVariant().toLongLong();
            m_ghostWidget->setStoreItemInfo(usedSizeBytes, totalSizeBytes);
            m_ghostWidget->setResultInfo("");
            m_ghostWidget->setStartEnable(true);
            QDir mediaDir("/media");
            QString realMediaDir = mediaDir.canonicalPath();
            if (destPath.startsWith("/media/") || destPath.startsWith(realMediaDir)) {
                m_ghostWidget->setErrorInfo(tr("Choosing an external disk will reduce the production speed and success rate. Please choose carefully."));
            } else {
                m_ghostWidget->setErrorInfo("");
            }
            m_ghostWidget->setGhostItemTips(spaceOKTips);
            m_ghostWidget->setGhostItemTipsLabelIcon(0);
        } else if (errCode == InsufficientDiskSpace) {
           // m_ghostWidget->setDestDirText("");
            m_ghostWidget->setStoreItemInfo(0, totalSizeBytes);
            m_ghostWidget->setResultInfo("");
            m_ghostWidget->setStartEnable(false);
            m_ghostWidget->setGhostItemTips(insufficientSpaceTips);
            m_ghostWidget->setGhostItemTipsLabelIcon(1);
        } else {
            m_ghostWidget->setResultInfo(jsonObject.value("errMsg").toString());
            m_ghostWidget->setStoreItemInfo(totalSizeBytes, totalSizeBytes);
            m_ghostWidget->setStartEnable(false);
            m_ghostWidget->setGhostItemTips(insufficientSpaceTips);
            m_ghostWidget->setGhostItemTipsLabelIcon(1);
        }

        m_frameProxy->setMenuDisabled(false);
        m_frameProxy->setQuitMenuDisabled(false);
        m_frameProxy->setWindowFuncClose(true);
        m_frameProxy->enableBackWard(true);
        m_frameProxy->enableModule(true);
        return;
    }
}

void ImmuBackupModule::onDataBackupCheckSpace(const QString &msg)
{
    // m_dataBackupWidget->setTips(tr(""));
    QStringList backupFiles = m_dataBackupWidget->getBackupFiles();
    QStringList denyFileList; // = Common::getDenyUserSecurityList("uos-recovery");
    QString bindPath;
    for (QString &denyPath : denyFileList) {
        if (denyPath.startsWith("/home/")) {
            bindPath = m_dataBackupWidget->getDirBindPath("/home");
            int index = bindPath.indexOf("/home");
            bindPath = bindPath.left(index);
        } else {
            bindPath = "";
        }
        for (QString &backupPath : backupFiles) {
            if (backupPath.startsWith(denyPath) || backupPath.startsWith(bindPath + denyPath)) {
                m_dataBackupWidget->setTips(tr("Backup and restore cannot access the data in %1").arg(denyPath));
                m_dataBackupWidget->setTipsStyleSheet("QLabel {"
                                                      "color: #FF5736;"
                                                      "}");
                return;
            }
        }
    }

    m_destUUID = m_dataBackupWidget->getDestDeviceUUID();
    if (m_destUUID.isEmpty()) {
        return;
    }

    if (m_recoveryInterface->IsRunning()) {
        QString operateID;
        QString userName;
        int opType;
        int progress;
        m_recoveryInterface->GetContext(operateID, userName, opType, progress);
        QString opTypeDes = Common::GetOperateTypeDes(opType);
        QString tips = tr("Task conflict, user %1's task %2 has not been completed").arg(userName).arg(opTypeDes);
        m_dataBackupWidget->setTips(tips);
        m_dataBackupWidget->setTipsStyleSheet("QLabel {"
                                              "color: #FF5736;"
                                              "}");
        qWarning()<<"onDataBackupCheckSpace, another task is running, operateID = "<<operateID<<", userName = "<<userName
                  <<", opType = "<<opType<<", progress = "<<progress;
        return;
    }

    UserDataBackupRequest request;
    request.username = Utils::getUserName();
    request.operateID = QUuid::createUuid().toString();
    request.filesFrom = backupFiles;
    //m_excludeFileItems = m_dataBackupWidget->getExclude();
    request.destUUID = m_destUUID;
    request.remark = msg;
    m_curUserOpID = request.operateID;
    if (!m_destUUID.isEmpty()) {
        m_frameProxy->setMenuDisabled(true);
        m_frameProxy->setQuitMenuDisabled(true);
        m_frameProxy->setWindowFuncClose(false);
        m_dataBackupWidget->setButtonEnable(false);

        request.rootUUID = m_frameProxy->rootUUID();
        request.exclude = this->getUsrDataSpecialExcludes(); // 先获取特殊过滤项目，后面追加用户界面选择的
        m_excludes = request.exclude;
        m_dataBackupWidget->setTips(tr("Calculating file size..."));
        m_dataBackupWidget->setTipsStyleSheet("QLabel {"
                                              "color: #000000;"
                                              "}");
        m_dataBackupWidget->startSpinner();
        auto requestReply = m_recoveryInterface->CheckUserDataBackupSpace(request);
        requestReply.waitForFinished();
        if (!requestReply.isValid()) {
            m_curUserOpID.clear();
            m_dataBackupWidget->stopSpinner();
            m_dataBackupWidget->setTips(tr(""));
            m_dataBackupWidget->setButtonEnable(true);
            qCritical() << "CheckUserDataBackupSpace failed, err: " << requestReply.error();
            onShowResult(false, static_cast<int>(OperateType::UserDataBackup), tr("DBus error, please try again"));
            return;
        }

        int retVal = requestReply.value();
        if (static_cast<int>(ErrorCode::NoWriteable) == retVal) {
            m_curUserOpID.clear();
            m_dataBackupWidget->stopSpinner();
            m_dataBackupWidget->setButtonEnable(true);
            m_dataBackupWidget->setTips(tr("Cannot back up data to the read-only device"));
            m_dataBackupWidget->setTipsStyleSheet("QLabel {"
                                                  "color: #FF5736;"
                                                  "}");
            m_frameProxy->setMenuDisabled(false);
            m_frameProxy->setQuitMenuDisabled(false);
            m_frameProxy->setWindowFuncClose(true);
            return;
        }

        if (requestReply.value() != OK) {
            qCritical()<<"CheckUserDataBackupSpace failed, val = "<<requestReply.value();
            m_curUserOpID.clear();
            m_dataBackupWidget->stopSpinner();
            m_dataBackupWidget->setTips(tr(""));
            m_dataBackupWidget->setButtonEnable(true);
            onShowResult(false, static_cast<int>(OperateType::UserDataBackup));
            return;
        }
    }
}

void ImmuBackupModule::onGhostCheckSpace(const QString &selectDir)
{
    if (m_recoveryInterface->IsRunning()) {
        QString operateID;
        QString userName;
        int opType;
        int progress;
        m_recoveryInterface->GetContext(operateID, userName, opType, progress);
        QString opTypeDes = Common::GetOperateTypeDes(opType);
        QString tips = tr("Task conflict, user %1's task %2 has not been completed").arg(userName).arg(opTypeDes);
        m_ghostWidget->setResultInfo(tips, false);
        m_ghostWidget->setDefaultStorePathTips();
        m_ghostWidget->setGhostItemTips(tr("Select a directory to save the .uimg image file"));
        m_ghostWidget->resetLayout(LayoutType::tipsLayout);
        m_ghostWidget->setGhostItemTipsLabelIcon(2);
        qWarning()<<"onGhostCheckSpace, another task is running, operateID = "<<operateID
                  <<", opType = "<<opType<<", progress = "<<progress;
        return;
    }

    // 执行可用空间检查
    auto reply = m_recoveryInterface->CheckGhostBackupDiskSpace(selectDir);
    reply.waitForFinished();
    if (!reply.isValid()) {
        QDBusError dbusErr = reply.error();
        qCritical()<<"Space calculating failed, dbusErr: "<<dbusErr;
        m_ghostWidget->setResultInfo(tr("Space calculating failed"));
        return;
    }

    m_frameProxy->setMenuDisabled(true);
    m_frameProxy->setQuitMenuDisabled(true);
    m_frameProxy->setWindowFuncClose(false);
    m_frameProxy->enableBackWard(false);
    m_frameProxy->enableModule(false);

    return;
}

void ImmuBackupModule::onStartGhostBackup(const QString &selectDir)
{
    QString rootPath;
    QString dev;
    QList<QStorageInfo> storeInfoList = QStorageInfo::mountedVolumes();
    for (auto &storeInfo : storeInfoList) {
        rootPath = storeInfo.rootPath();
        if ("/" == rootPath) {
            if (selectDir == rootPath) {
                dev = storeInfo.device();
                break;
            }
        } else {
            if (selectDir.startsWith(rootPath)) {
                dev = storeInfo.device();
                break;
            }
        }
    }

    QString devUUID;
    if (!dev.isEmpty()) {
        Device::getUUIDByPartitionPath(dev, devUUID);
    }

    SystemCloneRequest req;
    req.username = Utils::getUserName();
    req.operateID = QUuid::createUuid().toString();
    req.operateType = static_cast<int> (OperateType::GhostBackup);
    req.destPath = selectDir;
    req.totalSize = m_backupSizeBytes;
    req.destUUID = devUUID;
    req.relativePath = selectDir.right(selectDir.length() - rootPath.length());
    QString reqStr = Utils::JsonToQString(req.marshal());
    if (m_recoveryInterface->IsRunning()) {
        QString operateID;
        QString userName;
        int opType;
        int progress;
        m_recoveryInterface->GetContext(operateID, userName, opType, progress);
        QString opTypeDes = Common::GetOperateTypeDes(opType);
        QString tips = tr("Task conflict, user %1's task %2 has not been completed").arg(userName).arg(opTypeDes);
        m_ghostWidget->setResultInfo(tips, false);
        qWarning()<<"onStartGhostBackup, another task is running, operateID = "<<operateID
                  <<", opType = "<<opType<<", progress = "<<progress;
        return;
    }

    auto reply = m_recoveryInterface->CreateUImg(req);
    reply.waitForFinished();
    if (!reply.isValid()) {
        QDBusError dbusErr = reply.error();
        qCritical()<<"onStartGhostBackup failed, dbusErr: "<<dbusErr;
        onShowResult(false, static_cast<int>(OperateType::GhostBackup), tr("Failed to create the .uimg image file"));
        return;
    }

    m_curSysOpID = req.operateID;

    // 切换到进度界面
    onShowProgress(OperateType::GhostBackup,
                   tr("Creating the .uimg image file"),
                   tr("Time remaining:") + " ",
                   tr("Your system may be stuck in the process, please wait patiently"));
}

void ImmuBackupModule::onReportCheckSpace(const QString &space)
{
    QJsonObject jsonObject = Utils::QStringToJson(space);
    QString curOperateID = jsonObject.value("operateID").toString();
    if (curOperateID.isEmpty()) {
        return;
    }
    int recoveryType = -1;
    if (jsonObject.contains("recoveryType")) {
        recoveryType = jsonObject.value("recoveryType").toInt(-1);
    }

    int errorCode = -1;
    if (jsonObject.contains("errCode")) {
        errorCode = jsonObject.value("errCode").toInt(-1);
    }

    int operateType = -1;
    if (jsonObject.contains("operateType")) {
        operateType = jsonObject.value("operateType").toInt(-1);
    }

    if ((recoveryType == static_cast<int> (RecoveryType::ImmutableMode)) && (m_curSysOpID == curOperateID)) {
        return this->systemBackup(operateType, errorCode);
    }
}

void ImmuBackupModule::systemBackup(int operateType, int errorCode)
{
    if (operateType == static_cast<int> (OperateType::CheckIncSystemBackupSpace)) {
        if (errorCode != OK) {
            m_frameProxy->setMenuDisabled(false);
            m_frameProxy->setQuitMenuDisabled(false);
            m_frameProxy->setWindowFuncClose(true);
            m_systemBackupWidget->setTips(
                    tr("Insufficient disk space. Please clean up first."));
            qCritical() << Q_FUNC_INFO << " systemBackup failed! errorCode = " << errorCode;
            return;
        }

        auto reply = m_recoveryInterface->SystemBackup(m_curSystemBackupRequest);
        reply.waitForFinished();
        if (!reply.isValid()) {
            m_curSysOpID.clear();
            qCritical() << Q_FUNC_INFO << " SystemBackup isvalid!";
            onShowResult(false, static_cast<int>(OperateType::ImmutableSystemBackup),
                         tr("DBus error, please try again"));
            return;
        }

        int replayValue = reply.value();
        if (replayValue != ErrorCode::OK) {
            qInfo()<<"SystemBackup replayValue = "<<replayValue;
            // TODO: 可以根据具体的错误码显示更详细的错误提示信息，先简单显示
            onShowResult(false, static_cast<int>(OperateType::ImmutableSystemBackup),
                         tr("Sorry, backup failed!"));
            return;
        }

        onShowProgress(OperateType::ImmutableSystemBackup,
                       tr("Backing up..."),
                       tr("Time remaining:") + " ",
                       tr("To avoid data loss, please do not use your computer during the process."));
        return;
    }
}
