#include "RsyncUserData.h"
#include "utils/Device.h"
#include "utils/Utils.h"
#include <QDir>
#include <QFile>
#include <QDebug>
#include <QUuid>
#include <QException>


static const QString USER_DATA_BACKUP_PATH = "/backup/userdata/snapshots/";
static const QString BACKUP_INFO_JSON = "/backup_info.json";

RsyncUserData::RsyncUserData()
{

}

RsyncUserData::~RsyncUserData()
{

}

bool RsyncUserData::supported(FSTabInfoList &fsTabInfoList)
{
    bool support = false;
    DeviceList deviceList;
    for (auto &info : fsTabInfoList) {
        if (info->isComment || info->isEmptyLine) {
            continue;
        }

        QString device = info->device;
        if (device.startsWith("UUID=")) {
            if (info->mountPoint != "/boot" &&
                info->mountPoint != "/boot/efi" &&
                info->mountPoint != "/recovery" &&
                info->mountPoint != "/tmp" &&
                info->mountPoint != "none" &&
                !info->options.contains("nofail")) {
                support = Device::isFsTypeSupportedDataBackup(info->type);
                if (!support) {
                    qCritical()<<"RsyncUserData not support mountPoint = "<<info->mountPoint<<", fsType = "<<info->type;
                    return false;
                }
            }

            const int colSize = 2;
#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
            QStringList col = device.split("=", Qt::SkipEmptyParts);
#else
            QStringList col = device.split("=", QString::SkipEmptyParts);
#endif
            if (colSize == col.size()) {
                device = col[1].trimmed();
            } else {
                qWarning()<<"RsyncSystem::supported, invalid uuid, device = "<<device;
            }
        }

        if (info->device.startsWith("UUID=") &&
            info->mountPoint != "/boot" &&
            info->mountPoint != "/boot/efi" &&
            info->mountPoint != "/recovery" &&
            info->mountPoint != "none" &&
            info->type != "swap") {

            DevicePtr pDevice(new Device(device));
            pDevice->calculateDiskSpace();
            deviceList.append(pDevice);
        }
    }

    std::sort(deviceList.begin(), deviceList.end(), deviceCompare);
    if (!deviceList.isEmpty()) {
        m_defaultBackupDeviceUUID = deviceList.first()->getDeviceInfo()->uuid;
    }

    return support;
}

ErrorCode RsyncUserData::parseUserDataBackupReq(const UserDataBackupRequest &request)
{
    if (request.rootUUID.isEmpty()) {
    //    qCritical() << Q_FUNC_INFO << "error: rootUUID isEmpty";
        return ErrorCode::RootUuidIsEmpty;
    }

    if (request.destUUID.isEmpty()) {
    //    qCritical() << Q_FUNC_INFO << "error: destUUID isEmpty";
        return ErrorCode::DestUuidIsEmpty;
    }

    m_request = request;
    m_backupInfo.username = request.username;
    m_backupInfo.operateID = QUuid::createUuid().toString();
    m_backupInfo.startTime = QDateTime::currentSecsSinceEpoch();
    m_backupInfo.remark = request.remark;
    m_backupInfo.operateType = static_cast<int>(OperateType::UserDataBackup);
    m_backupInfo.recoveryType = static_cast<int>(RecoveryType::Rsync);
    m_backupInfo.rootUUID = request.rootUUID;
    m_backupInfo.backupDevUUID = request.destUUID;
    m_backupInfo.size = 0;

    // if (!Device::getMountPointByUUID(request.rootUUID, m_fromDir)) {
    // //    qCritical() << "getMountPointByUUID failed, rootUUID = "<< request.rootUUID;
    //     return PartitionNotMount;
    // }
    // m_fromDir = QString("%1/home/%2/").arg(m_fromDir).arg(request.username);
    m_fromDir = "/";

    if (!Device::getMountPointByUUID(request.destUUID, m_toDir)) {
        qCritical() << "getMountPointByUUID failed, destUUID = "<< request.destUUID;
        return PartitionNotMount;
    }

    return OK;
}

ErrorCode RsyncUserData::checkUserDataBackupSpace(const UserDataBackupRequest &request)
{
    ErrorCode errCode = this->parseUserDataBackupReq(request);
    if (errCode != OK) {
        return errCode;
    }

    QString destMountPoint;
    Device::getMountPointByUUID(request.destUUID, destMountPoint);
    if (Device::isDeviceMountedReadOnly(destMountPoint)) {
        return ErrorCode::NoWriteable;
    }

    // 每次备份都创建一个备份任务，任务执行完毕后自动删除
    QString opID = request.operateID;
    m_pTryBackupTaskMap[opID] = new RsyncDryRunTask(false);
    m_pTryBackupTask = m_pTryBackupTaskMap[opID];
    connect(m_pTryBackupTask, &RsyncDryRunTask::spaceChanged, [=](const QJsonObject &jsonObject) {
        m_backupInfo.status = Running;
        QJsonObject spaceJson = jsonObject;
        spaceJson.insert("operateType", static_cast<int>(OperateType::CheckUserDataBackupSpace));
        spaceJson.insert("operateID", request.operateID);
        QString spaceInfo = Utils::JsonToQString(spaceJson);
     //   qInfo()<<Q_FUNC_INFO<<"spaceChanged, spaceInfo = "<<spaceInfo;
        Q_EMIT spaceChanged(spaceInfo);
    });

    connect(m_pTryBackupTask, &RsyncDryRunTask::error, [=](const QJsonObject &jsonObject) {
        m_backupInfo.status = Failed;
        QJsonObject errJson = jsonObject;
        errJson.insert("operateType", static_cast<int>(OperateType::CheckUserDataBackupSpace));
        errJson.insert("operateID", request.operateID);
        Q_EMIT error(Utils::JsonToQString(errJson));
    });

    connect(m_pTryBackupTask, &QThread::finished, [=] {
        if (nullptr != m_pTryBackupTaskMap[opID]) {
            m_pTryBackupTaskMap[opID]->deleteLater();
            m_pTryBackupTaskMap.remove(opID);
            // qWarning() << "m_pTryBackupTask deleteLater finish"<<", opID = "<<opID;
        }
    });

    m_backupInfo.startTime = QDateTime::currentMSecsSinceEpoch(); // 试运行时采用精度更高的，避免快速操作误删真正的备份文件
    QString curTime = QDateTime::fromMSecsSinceEpoch(m_backupInfo.startTime).toString("yyyy-MM-dd_hh-mm-ss-zzz");
    m_backupInfo.backupPath = USER_DATA_BACKUP_PATH + QString("%1/localhost").arg(curTime);
    m_destDir = QDir(m_toDir + m_backupInfo.backupPath).absolutePath();
    m_pTryBackupTask->setOptions({"-aAXHir", "--verbose", "--delete", "--force", "--sparse", "--stats",
                                  "--info=progress2", "--ignore-missing-args", "--no-inc-recursive"});
    m_pTryBackupTask->setSourcePath(m_fromDir);
    m_pTryBackupTask->setDestinationPath(m_destDir);
    m_pTryBackupTask->setCheckPath(m_destDir);
    m_pTryBackupTask->setExcludes(request.exclude);
    m_pTryBackupTask->setFileForm(request.filesFrom);
    m_pTryBackupTask->enableDryRun(true);
    m_pTryBackupTask->setDestUUID(request.destUUID);

    //qInfo()<< "m_destDir=" << m_destDir;
    QDir dir(m_destDir);
    if (!dir.exists()) {
        dir.mkpath(m_destDir);
    }

    QString tmpDir = QDir(m_toDir + USER_DATA_BACKUP_PATH + curTime).absolutePath();
    Process::spawnCmd("setfacl", {"-m", "o:rwx", tmpDir});
    // qWarning()<<"tmpDir = "<<tmpDir;

    if (!m_pTryBackupTask->buildArgumentsForBackup()) {
        qCritical()<<"checkUserDataBackupSpace buildArgumentsForBackup failed";
        QJsonObject errJson;
        errJson.insert("operateType", static_cast<int>(OperateType::CheckUserDataBackupSpace));
        errJson.insert("operateID", request.operateID);
        errJson.insert("progress", 0);
        errJson.insert("remainSecond", 0);
        errJson.insert("errMsg", "build arguments failed");
        Q_EMIT error(Utils::JsonToQString(errJson));
        return ErrorCode::ParamError;
    }
    m_pTryBackupTask->start();
    return OK;
}

ErrorCode RsyncUserData::userDataBackup(const UserDataBackupRequest &request)
{
    ErrorCode ret = OK;
    if (request.rootUUID.isEmpty()) {
    //    qCritical() << "error: rootUUID isEmpty";
        return ErrorCode::RootUuidIsEmpty;
    }

    if (request.destUUID.isEmpty()) {
    //    qCritical() << "error: destUUID isEmpty";
        return ErrorCode::DestUuidIsEmpty;
    }

    m_request = request;
    ret = fillBackupInfo();
    if (ret != OK) {
    //    qCritical() << "fillBackupInfo failed";
        return ret;
    }

    QString fromDir;
    // if (!Device::getMountPointByUUID(request.rootUUID, fromDir)) {
    //     qCritical() << "getMountPointByUUID failed, rootUUID = "<< request.rootUUID;
    //     return PartitionNotMount;
    // }
    // fromDir = QString("%1/home/%2/").arg(fromDir).arg(request.username);
    fromDir = "/";

    QString toDir;
    if (!Device::getMountPointByUUID(request.destUUID, toDir)) {
        qCritical() << "getMountPointByUUID failed, destUUID = "<< request.destUUID;
        return PartitionNotMount;
    }

    //每次备份都创建一个备份任务，任务执行完毕后自动删除
    QString opID = request.operateID;
    m_pUserDataBackTaskMap[opID] = new RsyncTask;
    m_pUserDataBackTask = m_pUserDataBackTaskMap[opID];
    m_pUserDataBackTask->setOperateType(OperateType::UserDataBackup);
    connect(m_pUserDataBackTask, &RsyncTask::progressChanged, [=](const QJsonObject &jsonObject) {
        m_backupInfo.status = Running;
        QJsonObject progress = jsonObject;
        progress.insert("operateType", static_cast<int>(OperateType::UserDataBackup));
        progress.insert("operateID", request.operateID);
        Q_EMIT progressChanged(Utils::JsonToQString(progress));
    });

    connect(m_pUserDataBackTask, &AsyncTask::success, [=] {
        m_backupInfo.status = Success;
        if (Utils::isImmutableSystem()) {
            m_backupInfo.systemEnvType = 1;
        }
        writeBackupInfo();
        Process::spawnCmd("rm", {"-fr", m_last});
        QString out;
        QString errMsg;
        Process::spawnCmd("ln", {"-s", m_destDir, m_last}, out, errMsg);
        qInfo() << "errMsg = "<< errMsg <<", out = "<<out;

        ResultInfo retInfo(0, "success", "");
        this->reportEventLog(retInfo, OperateType::UserDataBackup, RecoveryType::Rsync);
        QJsonObject jsonObject;
        jsonObject.insert("operateType", static_cast<int>(OperateType::UserDataBackup));
        jsonObject.insert("errCode", 0);
        jsonObject.insert("operateID", request.operateID);
        Q_EMIT success(Utils::JsonToQString(jsonObject));
        // qInfo() << "AsyncTask::success, destDir = " << m_destDir;

    });

    connect(m_pUserDataBackTask, &RsyncTask::error, [=](const QJsonObject &jsonObject) {
        m_backupInfo.status = Failed;
        QJsonObject errJson = jsonObject;
        errJson.insert("operateType", static_cast<int>(OperateType::UserDataBackup));
        errJson.insert("operateID", request.operateID);
        Process::spawnCmd("rm", {"-fr", m_destDir});

        int errCode = jsonObject.value("errCode").toInt(-1);
        QString errMsg = jsonObject.value("errMsg").toString();
        ResultInfo retInfo(errCode, "failed", errMsg);
        this->reportEventLog(retInfo, OperateType::UserDataBackup, RecoveryType::Rsync);
        qInfo() << "userDataBackup RsyncTask::error";
        Q_EMIT error(Utils::JsonToQString(errJson));
    });

    connect(m_pUserDataBackTask, &QThread::finished, [=] {
        if (nullptr != m_pUserDataBackTaskMap[opID]) {
            m_pUserDataBackTaskMap[opID]->deleteLater();
            m_pUserDataBackTaskMap.remove(opID);
            // qWarning() << "m_pUserDataBackTask deleteLater finish"<<", opID = "<<opID;
        }
    });

    QString curTime = QDateTime::fromSecsSinceEpoch(m_backupInfo.startTime).toString("yyyy-MM-dd_hh-mm-ss");
    m_backupInfo.backupPath = USER_DATA_BACKUP_PATH + QString("%1/localhost").arg(curTime);
    m_destDir = QDir(toDir + m_backupInfo.backupPath).absolutePath();
    m_pUserDataBackTask->setOptions({"-aAXHir", "--verbose", "--delete", "--force", "--sparse", "--stats",
                                     "--info=progress2", "--ignore-missing-args", "--no-inc-recursive"});
    m_pUserDataBackTask->setSourcePath(fromDir);
    m_pUserDataBackTask->setDestinationPath(m_destDir);
    m_pUserDataBackTask->setCheckPath(m_destDir);
    m_pUserDataBackTask->setExcludes(request.exclude);
    m_pUserDataBackTask->setFileForm(request.filesFrom);
    m_pUserDataBackTask->enableDryRun(false);

    QString linkDest = QDir(toDir + USER_DATA_BACKUP_PATH + "last/").absolutePath();
    m_last = linkDest;
    m_pUserDataBackTask->setLinkDest(linkDest);
  //  qInfo()<< "m_destDir=" << m_destDir;
    QDir dir(m_destDir);
    if (!dir.exists()) {
    //    qInfo()<< "mkpath destDir="<< m_destDir;
        dir.mkpath(m_destDir);
    }
    m_backupInfo.status = Failed;
    writeBackupInfo();

    if (!m_pUserDataBackTask->buildArgumentsForBackup()) {
        qCritical()<<"userDataBackup buildArgumentsForBackup failed";
        QJsonObject errJson;
        errJson.insert("operateType", static_cast<int>(OperateType::UserDataBackup));
        errJson.insert("operateID", request.operateID);
        errJson.insert("progress", 0);
        errJson.insert("remainSecond", 0);
        errJson.insert("errMsg", "build arguments failed");
        Q_EMIT error(Utils::JsonToQString(errJson));
        return ErrorCode::ParamError;
    }
    m_pUserDataBackTask->start();
    return ret;
}

ErrorCode RsyncUserData::userDataRestore(const UserDataRestoreRequest &request)
{
    if (request.backupInfo.rootUUID.isEmpty()) {
    //    qCritical() << "error: rootUUID isEmpty";
        return ErrorCode::RootUuidIsEmpty;
    }

    if (request.backupInfo.backupDevUUID.isEmpty()) {
    //    qCritical() << "error: backupDevUUID isEmpty";
        return ErrorCode::DestUuidIsEmpty;
    }

    QString fromDir;
    if (!Device::getMountPointByUUID(request.backupInfo.backupDevUUID, fromDir)) {
    //    qCritical() << "getMountPointByUUID failed, backupDevUUID = "<< request.backupInfo.backupDevUUID;
        return PartitionNotMount;
    }
    fromDir = QString("%1/%2/").arg(fromDir).arg(request.backupInfo.backupPath);

    QString toDir;
    // if (!Device::getMountPointByUUID(request.backupInfo.rootUUID, toDir)) {
    // //    qCritical() << "getMountPointByUUID failed, rootUUID = "<< request.backupInfo.rootUUID;
    //     return PartitionNotMount;
    // }
    // toDir = QString("%1/home/%2/").arg(toDir).arg(request.backupInfo.username);
    toDir = "/";

    //每次备份都创建一个备份任务，任务执行完毕后自动删除
    QString opID = request.operateID;
    m_pUserDataRestoreTaskMap[opID] = new RsyncTask;
    m_pUserDataRestoreTask = m_pUserDataRestoreTaskMap[opID];
    m_pUserDataRestoreTask->setOperateType(OperateType::UserDataRestore);
    connect(m_pUserDataRestoreTask, &RsyncTask::progressChanged, [=](const QJsonObject &jsonObject) {
        QJsonObject progress = jsonObject;
        progress.insert("operateType", static_cast<int>(OperateType::UserDataRestore));
        progress.insert("operateID", request.operateID);
        Q_EMIT progressChanged(Utils::JsonToQString(progress));
    });

    connect(m_pUserDataRestoreTask, &RsyncTask::error, [=](const QJsonObject &jsonObject) {
        QJsonObject errJson = jsonObject;
        errJson.insert("operateType", static_cast<int>(OperateType::UserDataRestore));
        errJson.insert("operateID", request.operateID);

        int errCode = jsonObject.value("errCode").toInt(-1);
        QString errMsg = jsonObject.value("errMsg").toString();
        ResultInfo retInfo(errCode, "failed", errMsg);
        this->reportEventLog(retInfo, OperateType::UserDataRestore, RecoveryType::Rsync);
        Q_EMIT error(Utils::JsonToQString(errJson));
    });

    connect(m_pUserDataRestoreTask, &AsyncTask::success, [=] {
        QJsonObject jsonObject;
        jsonObject.insert("operateType", static_cast<int>(OperateType::UserDataRestore));
        jsonObject.insert("errCode", 0);
        jsonObject.insert("operateID", request.operateID);
        Q_EMIT success(Utils::JsonToQString(jsonObject));

        ResultInfo retInfo(0, "success", "");
        this->reportEventLog(retInfo, OperateType::UserDataRestore, RecoveryType::Rsync);
    });


    connect(m_pUserDataRestoreTask, &QThread::finished, [=] {
    //    qInfo() << "task finish";
        if (nullptr != m_pUserDataRestoreTaskMap[opID]) {
            m_pUserDataRestoreTaskMap[opID]->deleteLater();
            m_pUserDataRestoreTaskMap.remove(opID);
        }
    });

    m_pUserDataRestoreTask->setOptions({"-aAXHir", "--verbose", "--force", "--sparse", "--stats",
                                     "--info=progress2", "--ignore-missing-args", "--no-inc-recursive"});
    m_pUserDataRestoreTask->setSourcePath(fromDir);
    m_pUserDataRestoreTask->setDestinationPath(toDir);
    m_pUserDataRestoreTask->setCheckPath(fromDir);
    m_pUserDataRestoreTask->enableDryRun(false);

    if (!m_pUserDataRestoreTask->buildArgumentsForRestore(fromDir)) {
        QJsonObject errJson;
        errJson.insert("operateType", static_cast<int>(OperateType::UserDataRestore));
        errJson.insert("operateID", request.operateID);
        errJson.insert("progress", 0);
        errJson.insert("remainSecond", 0);
        errJson.insert("errMsg", "build arguments failed");
        Q_EMIT error(Utils::JsonToQString(errJson));
        return ParamError;
    }

    m_pUserDataRestoreTask->start();
    return OK;

}

BackupInfoList RsyncUserData::listUserDataBackup(const QString &username)
{
    BackupInfoList backupList;
    QList<QString> mediaMountPointList;
    if(!Device::getAllMediaMountPoint(mediaMountPointList)) {
        return backupList;
    }

    QSet<QString> allBackupUUIDSet;
    bool isAdmin = Utils::isAdminUser(username);

    for (QString mountPoint : mediaMountPointList) {
        QDir dir(mountPoint + USER_DATA_BACKUP_PATH);
        if (!dir.exists()) {
        //    qWarning()<< Q_FUNC_INFO <<", dir not exists, dir = "<< mountPoint + USER_DATA_BACKUP_PATH;
            continue;
        }

        for (auto &file : dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::NoSymLinks)) {
            QString fileName = file.absolutePath() + "/" + file.fileName() + BACKUP_INFO_JSON;
        //    qInfo() << Q_FUNC_INFO <<", fileName = "<<fileName;
            QFile backupInfoFile(fileName);
            if (backupInfoFile.open(QIODevice::Text | QIODevice::ReadOnly)) {
                QString bkInfo = backupInfoFile.readAll();
                QJsonObject jsonObject = Utils::QStringToJson(bkInfo);
                if (!jsonObject.isEmpty()) {
                    BackupInfo backupInfo;
                    backupInfo.unmarshal(jsonObject);
                    if (!backupInfo.operateID.isEmpty() && allBackupUUIDSet.contains(backupInfo.operateID)) {
                        qWarning()<<"opID: "<<backupInfo.operateID<<" repeat, devUUID: "<<backupInfo.backupDevUUID<<", path: "<<backupInfo.backupPath;
                        continue;
                    }
                    allBackupUUIDSet.insert(backupInfo.operateID);
                    if (isAdmin || !username.isEmpty() && username == backupInfo.username) {
                        if (!backupInfo.username.isEmpty() && Utils::isUserExist(backupInfo.username)) {
                            backupList.append(backupInfo);
                        }
                    }
                } else {
                    qWarning()<<"listUserDataBackup jsonObject is empty!";
                }
            }
        }
    }

    return backupList;
}

void RsyncUserData::writeBackupInfo()
{
    QDir dir(m_destDir);
    if (dir.cdUp()) {
        QString backupInfoFile = dir.absolutePath() + "/backup_info.json";
        QFile file(backupInfoFile);
        try {
            if (file.open(QIODevice::Text | QIODevice::ReadWrite)) {
                QJsonObject jsonObject = m_backupInfo.marshal();
                auto content = Utils::JsonToQString(jsonObject);
                file.write(content.toUtf8());
                file.close();
            }
        } catch (QException &e) {
            qCritical() << e.what();
            file.close();
        }
    }
}

ErrorCode RsyncUserData::fillBackupInfo()
{
    m_backupInfo.username = m_request.username;
    m_backupInfo.operateID = m_request.operateID;
    m_backupInfo.startTime = QDateTime::currentSecsSinceEpoch();
    m_backupInfo.remark = m_request.remark;
    m_backupInfo.operateType = static_cast<int>(OperateType::UserDataBackup);
    m_backupInfo.recoveryType = static_cast<int>(RecoveryType::Rsync);
    m_backupInfo.rootUUID = m_request.rootUUID;
    m_backupInfo.backupDevUUID = m_request.destUUID;
    m_backupInfo.size = m_request.strTotalSizeBytes.toULongLong();
    static QString systemVersion = Utils::getOSVersion();
    m_backupInfo.systemVersion = systemVersion;

    DevicePtr backupDevice(new Device(m_backupInfo.backupDevUUID));
    if (backupDevice.isNull()) {
        qCritical() << QString("device is not exist, uuid:%1").arg(m_backupInfo.backupDevUUID);
        return PartitionNotExist;
    }
    if (backupDevice->getDeviceInfo()->label.isEmpty()) {
        QString diskName;
        DevicePtr pDisk(new Device(backupDevice->getDeviceInfo()->pkname));
        if (!pDisk.isNull()) {
            diskName = pDisk->getDeviceInfo()->mode;
        }
        m_backupInfo.backupDevice = QString("%1(%2)")
                .arg(diskName)
                .arg(Utils::byte2DisplaySize(backupDevice->getDeviceInfo()->sizeBytes));
    } else {
        m_backupInfo.backupDevice = backupDevice->getDeviceInfo()->label;
    }

    m_backupInfo.backupDeviceRemovable = backupDevice->getDeviceInfo()->mountPoint.startsWith("/media");
    return OK;
}

ErrorCode RsyncUserData::removeUserDataBackup(const RemoveUserDataBackupRequest &request)
{
    QString opID = request.operateID;
    m_pRemoveTaskMap[opID] = new RemoveTask;
    m_pRemoveTask = m_pRemoveTaskMap[opID];
    connect(m_pRemoveTask, &RemoveTask::error, [=] (const QJsonObject &jsonObject) {
        QJsonObject errJson = jsonObject;
        errJson.insert("operateType", static_cast<int>(OperateType::removeBackup));
        errJson.insert("operateID", request.operateID);
        Q_EMIT error(Utils::JsonToQString(errJson));
    });

    connect(m_pRemoveTask, &RemoveTask::success, [=] (const QJsonObject &jsonObject) {

        //更新last指向
        QString mountPoint;
        if (!Device::getMountPointByUUID(request.backupInfo.backupDevUUID, mountPoint)) {
        //    qCritical() << "getMountPointByUUID failed, backupDevUUID = "<< request.backupInfo.backupDevUUID;
            QJsonObject errJson = jsonObject;
            errJson.insert("operateType", static_cast<int>(OperateType::removeBackup));
            errJson.insert("operateID", request.operateID);
            Q_EMIT error(Utils::JsonToQString(errJson));
        }

        QDir backupPath(mountPoint + USER_DATA_BACKUP_PATH);
        if (!Process::spawnCmd("rm", QStringList() << "-rf" << backupPath.absolutePath() + "/last")) {
            qCritical() << QString("delete last failed:%1").arg(backupPath.absolutePath() + "/last");
        }

        if (request.backupInfo.status == OperateStatus::Success) {
            auto backupInfos = listUserDataBackup(request.username);
            if (!backupInfos.isEmpty()) {
                std::sort(backupInfos.begin(), backupInfos.end());
                const QString cmd = "ln";
                QStringList args = {"-s",
                                    QDir(mountPoint + backupInfos.last().backupPath).absolutePath(),
                                    backupPath.absolutePath() + "/last"};
                if (!Process::spawnCmd(cmd, args)) {
                    qCritical() << QString("exec command failed:%1 %2").arg(cmd).arg(args.join(" "));
                }
            }
        }

        QJsonObject msg = jsonObject;
        msg.insert("operateType", static_cast<int>(OperateType::removeBackup));
        msg.insert("operateId", request.backupInfo.operateID);
        msg.insert("operateID", request.operateID);
        Q_EMIT success(Utils::JsonToQString(msg));
    });

    connect(m_pRemoveTask, &QThread::finished, [=] {
     //   qInfo() << "task finish";
        if (nullptr != m_pRemoveTaskMap[opID]) {
            m_pRemoveTaskMap[opID]->deleteLater();
            m_pRemoveTaskMap.remove(opID);
        }
    });

    return m_pRemoveTask->remove(request.backupInfo);

}


