#include "RecoveryService.h"
#include "RsyncSystem.h"
#include "RsyncUserData.h"
#include "DimFileHandle.h"
#include "GhostUImg.h"
#include "ImmutableSystem.h"
#include "utils/Device.h"
#include "utils/Process.h"
#include "utils/global.h"
#include "OSTreeSystem.h"
#include "BtrfsSystem.h"
#include "LvmSystem.h"
#include "utils/Utils.h"
#include "BtrfsUserData.h"
#include "LvmUserData.h"
#include "DBInterface.h"
#include <QSettings>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QDebug>
#include <QDir>
#include <QStandardPaths>
#include <QDBusConnectionInterface>
#include "OSTreeWorkerThread.h"
#include "WorkerThread.h"

RecoveryService::RecoveryService(QObject *parent) : QObject(parent) , m_pSystemRecovery(nullptr)
{
    registerALLMetaType();
    m_allDevice.clear();
    m_fsTabInfoList = FSTab::getFSTabFromFile("/etc/fstab");
    DBInterface::getInstance()->initDataBase();
    m_isImmutableSys = Utils::isImmutableSystem();
}

RecoveryService::~RecoveryService()
{

}

bool RecoveryService::checkCaller()
{
    const QString uosRecoveryGuiRegisterdProcName = "uos-recovery-gui";
    QDBusConnection conn = connection();
    QString msgService = message().service();
    uint senderPid = conn.interface()->servicePid(msgService).value();
    if (m_registeredProcNameMap[uosRecoveryGuiRegisterdProcName] == senderPid) {
        return true;
    }

    QString name;
    if (!Utils::getProcessNameByPid(senderPid, name)) {
        sendErrorReply(QDBusError::ErrorType::AccessDenied,QString("Invalid Client!"));
        return false;
    }

    QFile initNsMntFile("/proc/1/ns/mnt");
    QFile senderNsMntFile(QString("/proc/%1/ns/mnt").arg(senderPid));
    QString initNsMntFileLink = initNsMntFile.symLinkTarget().trimmed();
    QString senderNsMntFileLink = senderNsMntFile.symLinkTarget().trimmed();
    QString initNsMnt = initNsMntFileLink.remove(0, QString("/proc/1/ns/mnt").length());
    QString senderNsMnt = senderNsMntFileLink.remove(0, QString("/proc/%1/ns/mnt").arg(senderPid).length());
    if (senderNsMnt.isEmpty() || (initNsMnt != senderNsMnt)) {
        sendErrorReply(QDBusError::ErrorType::AccessDenied, QString("Invalid Client!!"));
        qCritical()<<"checkCaller initNsMnt: "<<initNsMnt<<", senderNsMnt: "<<senderNsMnt;
        return false;
    }

    if (uosRecoveryGuiRegisterdProcName.startsWith(name)) {
        m_registeredProcNameMap[uosRecoveryGuiRegisterdProcName] = senderPid;
        return true;
    }
    sendErrorReply(QDBusError::ErrorType::AccessDenied,QString("Invalid Client!"));
    return false;
}

bool RecoveryService::IsInLive()
{
    return false;
}

static void buildPartitionMapByDevice(const QString &name, const QString &deviceName,
    QMap<QString, DeviceList> &partitionMap)
{
    DevicePtr pDevice(new Device(deviceName));
    pDevice->calculateDiskSpace();
    DeviceList &devList = partitionMap[name];
    devList.append(pDevice);
}

static void deviceList2DiskAndVgPartition(DeviceInfoList &deviceInfoList, QMap<QString, DeviceList> &disk2partition,
    QMap<QString, DeviceList> &vg2partition)
{
    disk2partition.clear();
    vg2partition.clear();
    DeviceInfoList diskList;
    DeviceInfoList cryptDevList;
    DeviceInfoList noCryptDevList;
    DeviceInfoList partitionList;
    DeviceInfoList lvmList;

    for (auto &dev : deviceInfoList) {
        if (dev->type == "disk") {
            diskList.append(dev);
        } else if ((dev->type == "part" && dev->fsType == "luks") || (dev->type == "crypt" && dev->fsType == "lvm2")) {
            cryptDevList.append(dev); // 全盘加密场景
        } else if (dev->type == "part" && dev->fsType == "lvm2") {
            noCryptDevList.append(dev); // 普通LVM
        } else if (dev->type == "part") {
            partitionList.append(dev);
        } else if (dev->type == "lvm") {
            lvmList.append(dev);
        }
    }

    for (auto &diskDev : diskList) {
        QString mnt = diskDev->mountPoint;
        if (!mnt.isEmpty() && (diskDev->removable || mnt.startsWith("/media/"))) {
            // 数据备份支持不带分区但是有文件系统的设备
            buildPartitionMapByDevice(diskDev->name, diskDev->name, disk2partition);
            continue;
        }

        for (auto &cryptDev : cryptDevList) {
            if (!(cryptDev->type == "part" && cryptDev->fsType == "luks" && cryptDev->pkname == diskDev->name)) {
                continue;
            }

            for (auto &lvm2Dev : cryptDevList) {
                if (!(lvm2Dev->type == "crypt" && lvm2Dev->fsType == "lvm2" && lvm2Dev->pkname == cryptDev->kname)) {
                    continue;
                }

                for (auto &lvmMember : lvmList) {
                    if (lvmMember->pkname == lvm2Dev->kname) {
                        QString vgName = lvmMember->name;
                        vgName = vgName.left(vgName.indexOf("-"));
                        buildPartitionMapByDevice(vgName, lvmMember->deviceName, vg2partition);
                    }
                }
            }
        }

        for (auto &noCryptDev : noCryptDevList) {
            if (noCryptDev->pkname != diskDev->name) {
                continue;
            }

            for (auto &lvmMember : lvmList) {
                if (lvmMember->pkname == noCryptDev->kname) {
                    QString vgName = lvmMember->name;
                    vgName = vgName.left(vgName.indexOf("-"));
                    buildPartitionMapByDevice(vgName, lvmMember->deviceName, vg2partition);
                }
            }
        }

        for (auto &partitionDev : partitionList) {
            if (partitionDev->pkname == diskDev->name) {
                buildPartitionMapByDevice(diskDev->name, partitionDev->deviceName, disk2partition);
            }
        }
    }
}

void RecoveryService::partition2DiskArray(const QMap<QString, DeviceList> &partitionMap, QJsonArray &diskArray,
    bool removable, bool isLvm)
{
    for (auto iter = partitionMap.begin(); iter != partitionMap.end(); ++iter) {
        QJsonArray partitionArray;
        for (auto &p : iter.value()) {
            DeviceInfoPtr devIntoPtr = p->getDeviceInfo();
            if (nullptr == devIntoPtr) {
                continue;
            }
            QString mountPoint = devIntoPtr->mountPoint;
            if (mountPoint.isEmpty()) {
                continue;
            }

            bool externalDevice = (p->getDeviceInfo()->removable || mountPoint.startsWith("/media"));
            if (removable && !externalDevice) {
                continue;
            }
            Partition partition;
            if (isLvm) {
                QString vgName = p->getDeviceInfo()->name;
                vgName = vgName.right(vgName.length() - vgName.indexOf("-") - 1);
                partition.name = vgName;
                partition.deviceType = "lvm";
            } else {
                partition.name = p->getDeviceInfo()->name;
                partition.deviceType = "";
            }
            partition.fsType = p->getDeviceInfo()->fsType;
            partition.label = p->getDeviceInfo()->label;
            partition.uuid = p->getDeviceInfo()->uuid;
            partition.size = p->getDeviceInfo()->sizeBytes;
            partition.used = p->getDeviceInfo()->usedBytes;
            partition.free = p->getDeviceInfo()->availableBytes;
            partition.fsTypeSupported = Device::isFsTypeSupported(partition.fsType);
            partition.fsTypeSupportedDataBackup = Device::isFsTypeSupportedDataBackup(partition.fsType);
            partition.externalDevice = externalDevice;
            partition.mountPoint = p->getDeviceInfo()->mountPoint;
            QJsonObject partitionJson = partition.marshal();
            partitionArray.append(partitionJson);
        }

        QJsonObject diskJson;
        if (isLvm) {
            quint64 vgTotalSize = 0;
            for (auto &iterLvm : iter.value()) {
                DeviceInfoPtr lvmDevPtr = iterLvm->getDeviceInfo();
                if (lvmDevPtr == nullptr) {
                    continue;
                }
                if (!lvmDevPtr->mountPoint.isEmpty()) {
                    vgTotalSize += lvmDevPtr->sizeBytes;
                }
            }

            diskJson.insert("vg", iter.key());
            diskJson.insert("size", QString::number(vgTotalSize));
        } else {
            DevicePtr pDevice(new Device);
            pDevice->getDeviceByName(iter.key());
            DeviceInfoPtr devPtr = pDevice->getDeviceInfo();
            if (devPtr == nullptr) {
                continue;
            }

            diskJson.insert("disk", pDevice->getDeviceInfo()->diskName);
            diskJson.insert("size", QString::number(pDevice->getDeviceInfo()->sizeBytes));
        }
        diskJson.insert("partitions", partitionArray);
        diskArray.append(diskJson);
    }
}

QString RecoveryService::ListPartition(bool removable)
{
    if (!this->checkCaller()) {
        return tr("Invalid Client!");
    }

    DeviceInfoList deviceInfoList =  Device::getDeviceByLsblk();
    QMap<QString, DeviceList> disk2partition;
    QMap<QString, DeviceList> vg2partition;
    deviceList2DiskAndVgPartition(deviceInfoList, disk2partition, vg2partition);

    QJsonObject jsonRoot;
    QJsonArray diskArray;
    partition2DiskArray(vg2partition, diskArray, removable, true);
    partition2DiskArray(disk2partition, diskArray, removable, false);

    jsonRoot.insert("disks", diskArray);
    return Utils::JsonToQString(jsonRoot);;
}

QString RecoveryService::GetPartition(const QString & uuid)
{
    if (!this->checkCaller()) {
        return tr("Invalid Client!");
    }

    QString ret;
    DevicePtr pDevice(new Device(uuid));
    if (!pDevice->getDeviceInfo().isNull()) {
        pDevice->calculateDiskSpace();

        Partition partition;
        partition.name = pDevice->getDeviceInfo()->name;
        partition.fsType = pDevice->getDeviceInfo()->fsType;
        partition.label = pDevice->getDeviceInfo()->label;
        partition.uuid = pDevice->getDeviceInfo()->uuid;
        partition.size = pDevice->getDeviceInfo()->sizeBytes;
        partition.fsSize = pDevice->getDeviceInfo()->fsSizeBytes;
        partition.used = pDevice->getDeviceInfo()->usedBytes;
        partition.free = pDevice->getDeviceInfo()->availableBytes;
        partition.mountPoint = pDevice->getDeviceInfo()->mountPoint;
        QJsonObject jsonObject = partition.marshal();
        ret = Utils::JsonToQString(jsonObject);
    }
    return ret;
}


QString RecoveryService::GetRootUUID()
{
    if (!this->checkCaller()) {
        qCritical()<<"GetRootUUID failed, Invalid Client!";
        return tr("Invalid Client!");
    }

    if (IsInLive()) {
        //运行于live环境时返回空，需要用户指定根分区的uuid
        return QString();
    }

    if (!m_rootUUID.isEmpty()) {
        return m_rootUUID;
    }

    for (auto &info : m_fsTabInfoList) {
        if (info->isEmptyLine || info->isComment) {
            continue;
        }
        if ("/" == info->mountPoint) {
            m_rootUUID = FSTab::getDeviceUUID(info);
            break;
        }
    }

    Device::getDeviceByLsblk();

    return m_rootUUID;
}

ErrorCode RecoveryService::GetMountPointByUUID(const QString & srcUUID, QString &mountPoint)
{
    if (!this->checkCaller()) {
        return ErrorCode::InvalidClient;
    }

    // 优先从缓存中获取
    DevicePtr pDevice(new Device(srcUUID));
    if (!pDevice.isNull()) {
        mountPoint = pDevice->getDeviceInfo()->mountPoint;
        if (mountPoint.isEmpty()) {
            return PartitionNotMount;
        }
        return OK;
    }

    // 如果缓存中没有，再调用命令去确认一下
    if (!Device::getMountPointByUUID(srcUUID, mountPoint)) {
    //    qCritical()<<"GetMountPointByUUID, getMountPointByUUID failed, srcUUID = "<<srcUUID;
        return PartitionNotExist;
    }

    return OK;
}

QString RecoveryService::GetBackupDeviceUUID(const QString &rootUUID)
{
    if (!this->checkCaller()) {
        return tr("Invalid Client!");
    }

    QString backupUuid = "";
    QString rootPath = "/";
    if (IsInLive()) {
        qWarning() << "in live, to do ...";
        return backupUuid;
    }

    if (!IsRootfs(rootUUID)) {
        qCritical()<<"GetBackupDeviceUUID: IsRootfs false, rootUUID = "<<rootUUID;
        return backupUuid;
    }

    if (m_systemSyncType == static_cast<int> (RecoveryType::OSTree) ||
        m_systemSyncType == static_cast<int> (RecoveryType::ImmutableMode)) {
        // OStree 环境 系统备份目录是不支持修改的
        backupUuid = m_pSystemRecovery->getDefaultBackupDeviceUUID(rootUUID);
        return backupUuid;
    }

    QString recoveryConf = rootPath + UOS_RECOVERY_INI;
    QSettings settings(recoveryConf, QSettings::IniFormat);
    settings.beginGroup(BACKUP_GROUP);
    backupUuid = settings.value(BACKUP_DEVICE_UUID_KEY).toString();
    settings.endGroup();

    if (!backupUuid.isEmpty() && !this->checkUuidInFstab(backupUuid, rootPath)) {
        qWarning()<<"GetBackupDeviceUUID invalid uuid = "<<backupUuid<<", rootUUID = "<<rootUUID;
        backupUuid = "";
    }

    return backupUuid;
}

bool RecoveryService::checkUuidInFstab(const QString &uuid, const QString &rootmnt)
{
    QString fileName;
    if ("/" == rootmnt) {
        fileName = "/etc/fstab";
    } else {
        if (rootmnt.endsWith("/")) {
            fileName = rootmnt + "etc/fstab";
        } else {
            fileName = rootmnt + "/etc/fstab";
        }
    }

    static FSTabInfoList fstabList = FSTab::getFSTabFromFile(fileName);
    return FSTab::isUuidInFstab(uuid, fstabList);
}

int RecoveryService::SetBackupDeviceUUID(const QString &deviceUUID)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    QString rootPath = "/";
    if (this->IsInLive()) {
        qWarning() << "in live, to do ...";
        return  -1;
    }

    QString recoveryConf = rootPath + UOS_RECOVERY_INI;
    QSettings settings(recoveryConf, QSettings::IniFormat);
    settings.beginGroup(BACKUP_GROUP);
    auto currDeviceUUID = settings.value(BACKUP_DEVICE_UUID_KEY).toString();
    if (currDeviceUUID != deviceUUID) {
        settings.setValue(BACKUP_DEVICE_UUID_KEY, deviceUUID);
        if (this->checkUuidInFstab(currDeviceUUID, rootPath)) {
            auto historyBackupUUID = settings.value(BACKUP_HISTORY_DEVICE_UUID_KEY).toString();
            if (historyBackupUUID.isEmpty()) {
                historyBackupUUID = currDeviceUUID;
            } else {
                if (!historyBackupUUID.contains(currDeviceUUID)) {
                    historyBackupUUID.append("," + currDeviceUUID);
                }
            }
            settings.setValue(BACKUP_HISTORY_DEVICE_UUID_KEY, historyBackupUUID);
        }
    }
    settings.endGroup();
    return OK;
}

int RecoveryService::AutoInitSystemRecoveryType(const QString &rootUUID)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    ErrorCode errCode = UnKnow;
    if (m_allDevice.isEmpty()) {
        m_allDevice = Device::getDeviceByLsblk();
    }

    if (!this->IsRootfs(rootUUID)) {
        qCritical()<< Q_FUNC_INFO <<", not root fs, rootUUID = "<<rootUUID;
        return static_cast<int> (errCode);
    }

    QString rootPath = "/";
//    errCode = this->GetMountPointByUUID(rootUUID, rootPath);
//    if (OK != errCode) {
//        qCritical() << Q_FUNC_INFO <<", GetMountPointByUUID failed, uuid=" << rootUUID;
//        return static_cast<int> (errCode);
//    }

    if (m_pDimFileRecovery.isNull()) {
        DimFileRecoveryPtr testDimFileRecovery(new DimFileHandle);
        m_pDimFileRecovery = testDimFileRecovery;
        connect(m_pDimFileRecovery.get(), &DimFileRecovery::progressChanged, this, &RecoveryService::onReportProgress);
        connect(m_pDimFileRecovery.get(), &DimFileRecovery::success, this, &RecoveryService::onSuccess);
        connect(m_pDimFileRecovery.get(), &DimFileRecovery::error, this, &RecoveryService::onError);
        connect(m_pDimFileRecovery.get(), &DimFileRecovery::spaceCheckFinished, this, &RecoveryService::onReportSpace);
    }

    if (m_pUImgRecovery.isNull()) {
        UImgRecoveryPtr testUImgRecovery(new GhostUImg);
        m_pUImgRecovery = testUImgRecovery;
        connect(m_pUImgRecovery.get(), &UImgRecovery::progressChanged, this, &RecoveryService::onReportProgress);
        connect(m_pUImgRecovery.get(), &UImgRecovery::success, this, &RecoveryService::onSuccess);
        connect(m_pUImgRecovery.get(), &UImgRecovery::error, this, &RecoveryService::onError);
        connect(m_pUImgRecovery.get(), &UImgRecovery::spaceCheckFinished, this, &RecoveryService::onReportSpace);
    }

    m_recoveryConf = rootPath + UOS_RECOVERY_INI;
    errCode = InitSystemBackupType();
    if (OK != errCode) {
        qCritical() << Q_FUNC_INFO <<", InitSystemBackupType failed, errCode = "<<errCode;
        return static_cast<int> (errCode);
    }

    errCode = InitUserDataBackupType();

    return static_cast<int> (errCode);
}

ErrorCode RecoveryService::InitSystemBackupType()
{
    ErrorCode errCode = UnKnow;
    const int invalidVal = -1;
    if (nullptr != m_pSystemRecovery) {
        QSettings getConf(m_recoveryConf, QSettings::IniFormat);
        getConf.beginGroup(BACKUP_GROUP);
        m_systemSyncType = getConf.value(BACKUP_SYNC_TYPE_KEY, invalidVal).toInt();
        getConf.endGroup();
        if (invalidVal == m_systemSyncType) {
            qCritical()<< Q_FUNC_INFO <<", invalid system sync type!";
            return errCode;
        }
        return OK;
    }

    SystemRecoveryPtr pImmutableSync(new ImmutableSystem());
    SystemRecoveryPtr pOStreeSync(new OSTreeSystem);
    SystemRecoveryPtr pBtrfsSync(new BtrfsSystem);
    SystemRecoveryPtr pLvmSync(new LvmSystem);
    SystemRecoveryPtr pRsync(new RsyncSystem);
    // 按照优先级来判断，提供最佳备份还原的方式，优先级: ImmutableSystem > OStree > Btrfs > LvmSync > rsync
    if (pImmutableSync->supported(m_fsTabInfoList)) {
        m_pSystemRecovery = pImmutableSync;
        m_systemSyncType = static_cast<int> (RecoveryType::ImmutableMode);
        errCode = OK;
    } else if (pOStreeSync->supported(m_fsTabInfoList)) {
        m_pSystemRecovery = pOStreeSync;
        m_systemSyncType = static_cast<int> (RecoveryType::OSTree);
        errCode = OK;
    } else if (pBtrfsSync->supported(m_fsTabInfoList)) {
        m_pSystemRecovery = pBtrfsSync;
        m_systemSyncType = static_cast<int> (RecoveryType::BtrfsSnapshot);
        errCode = OK;
    } else if (pLvmSync->supported(m_fsTabInfoList)) {
        m_pSystemRecovery = pLvmSync;
        m_systemSyncType = static_cast<int> (RecoveryType::LvmSnapshot);
        errCode = OK;
    } else if (pRsync->supported(m_fsTabInfoList)) {
        m_pSystemRecovery = pRsync;
        m_systemSyncType = static_cast<int> (RecoveryType::Rsync);
        errCode = OK;
    } else {
        m_systemSyncType = invalidVal;
        errCode = PartitionNotSupportRecovery;
        qCritical()<< Q_FUNC_INFO <<", not support system sync type yet";
    }

    QSettings setConf(m_recoveryConf, QSettings::IniFormat);
    setConf.beginGroup(BACKUP_GROUP);
    setConf.setValue(BACKUP_SYNC_TYPE_KEY, m_systemSyncType);
    setConf.endGroup();

    if (!m_pSystemRecovery.isNull()) {
        connect(m_pSystemRecovery.get(), &SystemRecovery::progressChanged, this, &RecoveryService::onReportProgress);
        connect(m_pSystemRecovery.get(), &SystemRecovery::success, this, &RecoveryService::onSuccess);
        connect(m_pSystemRecovery.get(), &SystemRecovery::error, this, &RecoveryService::onError);
        connect(m_pSystemRecovery.get(), &SystemRecovery::spaceCheckFinished, this, &RecoveryService::onReportCheckSpace);
    }

    return errCode;
}

void RecoveryService::onReportProgress(const QString &progress)
{
    QJsonObject jsonObject = Utils::QStringToJson(progress);
    int curProgress = jsonObject.value("progress").toInt();
    QString operateID = jsonObject.value("operateID").toString();
    m_opIDProgress[operateID] = curProgress;
    Q_EMIT this->ReportProgress(progress);
}

void RecoveryService::onReportSpace(const QString &space)
{
    BRContext::getInstance()->setRun(false);
    Q_EMIT this->ReportSpace(space);
}

void RecoveryService::onError(const QString &errMsg)
{
    BRContext::getInstance()->setRun(false);
    Q_EMIT this->Error(errMsg);
}

void RecoveryService::onSuccess(const QString &msg)
{
    BRContext::getInstance()->setRun(false);
    Q_EMIT this->Success(msg);
}

void RecoveryService::onReportCheckSpace(const QString &space)
{
    BRContext::getInstance()->setRun(false);
    Q_EMIT this->ReportCheckSpace(space);
}

ErrorCode RecoveryService::InitUserDataBackupType()
{
    ErrorCode errCode = UnKnow;
    const int invalidVal = -1;
    if (nullptr != m_pUserDataRecovery) {
        QSettings getConf(m_recoveryConf, QSettings::IniFormat);
        getConf.beginGroup(BACKUP_GROUP);
        m_userDataSyncType = getConf.value(BACKUP_USER_DATA_SYNC_TYPE_KEY, invalidVal).toInt();
        getConf.endGroup();
        if (invalidVal == m_userDataSyncType) {
            qCritical()<< Q_FUNC_INFO << ", invalid user data sync type";
            return errCode;
        }
        return OK;
    }

    UserDataRecoveryPtr pBtrfsSync(new BtrfsUserData);
    UserDataRecoveryPtr pLvmSync(new LvmUserData);
    UserDataRecoveryPtr pRsync(new RsyncUserData);
    // 按照优先级来判断，提供最佳备份还原的方式，优先级: Btrfs > LvmSync > rsync
    // OStree 不支持数据备份
    if (pBtrfsSync->supported(m_fsTabInfoList)) {
        m_pUserDataRecovery = pBtrfsSync;
        m_userDataSyncType = static_cast<int> (RecoveryType::BtrfsSnapshot);
        errCode = OK;
    } else if (pLvmSync->supported(m_fsTabInfoList)) {
        m_pUserDataRecovery = pLvmSync;
        m_userDataSyncType = static_cast<int> (RecoveryType::LvmSnapshot);
        errCode = OK;
    } else if (pRsync->supported(m_fsTabInfoList)) {
        m_pUserDataRecovery = pRsync;
        m_userDataSyncType = static_cast<int> (RecoveryType::Rsync);
        errCode = OK;
    } else {
        m_userDataSyncType = invalidVal;
        errCode = PartitionNotSupportRecovery;
        qCritical()<< Q_FUNC_INFO << ", not support user data sync type yet";
    }

    QSettings setConf(m_recoveryConf, QSettings::IniFormat);
    setConf.beginGroup(BACKUP_GROUP);
    setConf.setValue(BACKUP_USER_DATA_SYNC_TYPE_KEY, m_userDataSyncType);
    setConf.endGroup();

    if (!m_pUserDataRecovery.isNull()) {
        connect(m_pUserDataRecovery.get(), &UserDataRecovery::progressChanged, this, &RecoveryService::onReportProgress);
        connect(m_pUserDataRecovery.get(), &UserDataRecovery::spaceChanged, this, &RecoveryService::onReportSpace);
        connect(m_pUserDataRecovery.get(), &UserDataRecovery::success, this, &RecoveryService::onSuccess);
        connect(m_pUserDataRecovery.get(), &UserDataRecovery::error, this, &RecoveryService::onError);
    }

    return errCode;
}

int RecoveryService::GetSystemSyncType()
{
    return m_systemSyncType;
}

int RecoveryService::GetUserDataSyncType()
{
    return m_userDataSyncType;
}

int RecoveryService::SystemBackup(const QString &request)
{
    return -1; // not used in V20
}

int RecoveryService::BackupV20(const V20BackupReq &request)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    QJsonObject jsonObject = request.marshal();
    QString reqStr = Utils::JsonToQString(jsonObject);
    // qWarning()<<"BackupV20, req = "<<reqStr.toLocal8Bit().data();

    BRContext::getInstance()->setRun(true);
    int opType = request.operateType;
    Context ctx(request.operateID, request.userName, opType);
    BRContext::getInstance()->setContext(ctx);

    m_recoveryThread = new WorkerThread(static_cast<ActionType> (request.actionType));
    if (nullptr != m_recoveryThread) {
        m_recoveryThread->setBackupRequest(request);
        m_recoveryThread->start();
    }

    connect(m_recoveryThread, &WorkerThread::error, this, [=] (int errCode){
        V20Response rsp(request.operateID, request.userName, request.operateType, errCode, 100, "", false);
        BRContext::getInstance()->setRun(false);
        OperateLog operateLog;
        operateLog.time = QDateTime::currentMSecsSinceEpoch();
        operateLog.username = request.userName;
        operateLog.operateType = OperateType::SystemBackup;
        operateLog.status = OperateStatus::Failed;
        DBInterface::getInstance()->addOperateLog(operateLog);
        QString rspStr = Utils::JsonToQString(rsp.marshal());
        // qWarning()<<"BackupV20, Error rsp = "<<rspStr.toLocal8Bit().data();
        Q_EMIT Error(rspStr);
    });

    connect(m_recoveryThread, &WorkerThread::success, this, [=] (){
        BRContext::getInstance()->setRun(false);
        OperateLog operateLog;
        operateLog.time = QDateTime::currentMSecsSinceEpoch();
        operateLog.username = request.userName;
        operateLog.operateType = OperateType::SystemBackup;
        operateLog.status = OperateStatus::Running;
        DBInterface::getInstance()->addOperateLog(operateLog);

        V20Response rsp(request.operateID, request.userName, request.operateType, 0, 100, "", false);
        QString rspStr = Utils::JsonToQString(rsp.marshal());
        // qWarning()<<"BackupV20, Success rsp = "<<rspStr.toLocal8Bit().data();
        Q_EMIT Success(rspStr);
    });

    connect(m_recoveryThread, &QThread::finished, [=] {
        BRContext::getInstance()->setRun(false);
        m_recoveryThread->deleteLater();
        m_recoveryThread = nullptr;
        qInfo()<<"m_recoveryThread ManualBackupV20 finished";
    });

    return 0;
}

int RecoveryService::Remove(const V20BackupReq &request)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    QJsonObject jsonObject = request.marshal();
    QString reqStr = Utils::JsonToQString(jsonObject);
    // qWarning()<<"Remove, req = "<<reqStr.toLocal8Bit().data();

    OperateType opType = static_cast<OperateType> (request.operateType);
    if (OperateType::RemoveCoverBackupV20 == opType) {
        Process::spawnCmd("rm", {"-rf", QString("%1").arg(request.backupPath)});
    }

    return 0;
}

int RecoveryService::CancelBackupOrRestore20(const V20BackupReq &request)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    QJsonObject jsonObject = request.marshal();
    QString reqStr = Utils::JsonToQString(jsonObject);
    // qWarning()<<"CancelBackupOrRestore20, req = "<<reqStr.toLocal8Bit().data();

    BRContext::getInstance()->setRun(true);
    int opType = static_cast<int>(request.operateType);
    Context ctx(request.operateID, request.userName, opType);
    BRContext::getInstance()->setContext(ctx);

    m_recoveryThread = new WorkerThread(static_cast<ActionType> (request.actionType));
    if (nullptr != m_recoveryThread) {
        m_recoveryThread->start();
    }

    connect(m_recoveryThread, &WorkerThread::error, this, [=] (int errCode){
        BRContext::getInstance()->setRun(false);
        // OperateLog operateLog;
        // operateLog.time = QDateTime::currentMSecsSinceEpoch();
        // operateLog.username = req.userName;
        // operateLog.operateType = req.operateType;
        // operateLog.status = OperateStatus::Failed;
        // DBInterface::getInstance()->addOperateLog(operateLog);
        V20Response rsp(request.operateID, request.userName, request.operateType, errCode, 100, "", false);
        QString rspStr = Utils::JsonToQString(rsp.marshal());
        // qWarning()<<"CancelBackupOrRestore20, Error rsp = "<<rspStr.toLocal8Bit().data();
        Q_EMIT Error(rspStr);
    });

    connect(m_recoveryThread, &WorkerThread::success, this, [=] (){
        BRContext::getInstance()->setRun(false);
        // OperateLog operateLog;
        // operateLog.time = QDateTime::currentMSecsSinceEpoch();
        // operateLog.username = req.userName;
        // operateLog.operateType = req.operateType;
        // operateLog.status = OperateStatus::Running;
        // DBInterface::getInstance()->addOperateLog(operateLog);
        V20Response rsp(request.operateID, request.userName, request.operateType, 0, 100, "", false);
        QString rspStr = Utils::JsonToQString(rsp.marshal());
        // qWarning()<<"CancelBackupOrRestore20, Success rsp = "<<rspStr.toLocal8Bit().data();
        Q_EMIT Success(rspStr);
    });

    connect(m_recoveryThread, &QThread::finished, [=] {
        BRContext::getInstance()->setRun(false);
        m_recoveryThread->deleteLater();
        m_recoveryThread = nullptr;
        qInfo()<<"m_recoveryThread CancelBackupOrRestore20 finished";
    });

    return 0;
}

int RecoveryService::SystemRestoreV20(const V20RestoreReq &request)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    QJsonObject jsonObject = request.marshal();
    QString reqStr = Utils::JsonToQString(jsonObject);
    // qWarning()<<"SystemRestoreV20, req = "<<reqStr.toLocal8Bit().data();

    BRContext::getInstance()->setRun(true);
    Context ctx(request.operateID, request.userName, request.operateType);
    BRContext::getInstance()->setContext(ctx);

    m_recoveryThread = new WorkerThread(static_cast<ActionType> (request.actionType));
    if (nullptr != m_recoveryThread) {
        m_recoveryThread->setSystemRestoreRequest(request);
        m_recoveryThread->start();
    }

    connect(m_recoveryThread, &WorkerThread::error, this, [=] (int errCode){
        BRContext::getInstance()->setRun(false);
        OperateLog operateLog;
        operateLog.time = QDateTime::currentMSecsSinceEpoch();
        operateLog.username = request.userName;
        OperateType opType = static_cast<OperateType> (request.operateType);
        if (OperateType::InitRestoreV20 == opType) {
            operateLog.operateType = OperateType::FactoryRestore;
        } else {
            operateLog.operateType = OperateType::SystemRestore;
        }
        operateLog.status = OperateStatus::Failed;
        DBInterface::getInstance()->addOperateLog(operateLog);
        V20Response rsp(request.operateID, request.userName, request.operateType, errCode, 100, "", false);
        QString rspStr = Utils::JsonToQString(rsp.marshal());
        // qWarning()<<"RecoveryService::SystemRestoreV20, Error rsp = "<<rspStr.toLocal8Bit().data();
        Q_EMIT Error(rspStr);
    });

    connect(m_recoveryThread, &WorkerThread::success, this, [=] (){
        BRContext::getInstance()->setRun(false);
        OperateLog operateLog;
        operateLog.time = QDateTime::currentMSecsSinceEpoch();
        operateLog.username = request.userName;
        OperateType opType = static_cast<OperateType> (request.operateType);
        if (OperateType::InitRestoreV20 == opType) {
            operateLog.operateType = OperateType::FactoryRestore;
        } else {
            operateLog.operateType = OperateType::SystemRestore;
        }
        operateLog.status = OperateStatus::Running;
        DBInterface::getInstance()->addOperateLog(operateLog);
        V20Response rsp(request.operateID, request.userName, request.operateType, 0, 100, "", false);
        QString rspStr = Utils::JsonToQString(rsp.marshal());
        // qWarning()<<"RecoveryService::SystemRestoreV20, Success rsp = "<<rspStr.toLocal8Bit().data();
        Q_EMIT Success(rspStr);
    });

    connect(m_recoveryThread, &QThread::finished, [=] {
        BRContext::getInstance()->setRun(false);
        m_recoveryThread->deleteLater();
        m_recoveryThread = nullptr;
        qWarning()<<"m_recoveryThread SystemRestoreV20 finished";
    });

    return 0;
}

int RecoveryService::SystemBackup(const SystemBackupRequest &request)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    QJsonObject jsonObject = request.marshal();
    QString reqStr = Utils::JsonToQString(jsonObject);
    // qWarning()<<"SystemBackup, req = "<<reqStr.toLocal8Bit().data();

    BRContext::getInstance()->setRun(true);
    int opType = static_cast<int>(OperateType::SystemBackup);
    Context ctx(request.operateID, request.userName, opType);
    BRContext::getInstance()->setContext(ctx);

    ErrorCode errorCode = m_pSystemRecovery->systemBackup(request);
    if (errorCode == OK) {
        OperateLog operateLog;
        operateLog.time = QDateTime::currentMSecsSinceEpoch();
        operateLog.username = request.userName;
        operateLog.operateType = OperateType::SystemBackup;
        operateLog.status = OperateStatus::Running;
        operateLog.remark = request.remark;
        DBInterface::getInstance()->addOperateLog(operateLog);
    } else {
        BRContext::getInstance()->setRun(false);
    }

    return 0;
}

int RecoveryService::SystemRestore(const SystemRestoreRequest &request)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    QJsonObject jsonObject = request.marshal();
    QString reqStr = Utils::JsonToQString(jsonObject);
    // qWarning()<<"SystemRestore, req = "<<reqStr.toLocal8Bit().data();

    BRContext::getInstance()->setRun(true);
    int opType = static_cast<int>(OperateType::SystemRestore);
    Context ctx(request.operateID, request.username, opType);
    BRContext::getInstance()->setContext(ctx);

    ErrorCode errorCode = m_pSystemRecovery->systemRestore(request);
    if (errorCode == OK) {
        OperateLog operateLog;
        operateLog.time = QDateTime::currentMSecsSinceEpoch();
        operateLog.username = request.username;
        operateLog.operateType = OperateType::SystemRestore;
        operateLog.status = OperateStatus::Running;
        operateLog.remark = ""; // 跟产品再次确认后，系统还原备注为空
        DBInterface::getInstance()->addOperateLog(operateLog);
    } else {
        BRContext::getInstance()->setRun(false);
    }
    return errorCode;
}

int RecoveryService::CheckUserDataBackupSpace(const UserDataBackupRequest &request)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    BRContext::getInstance()->setRun(true);
    int opType = static_cast<int>(OperateType::CheckUserDataBackupSpace);
    Context ctx(request.operateID, request.username, opType);
    BRContext::getInstance()->setContext(ctx);
    ErrorCode errCode = m_pUserDataRecovery->checkUserDataBackupSpace(request);
    if (OK != errCode) {
        BRContext::getInstance()->setRun(false);
    }

    return errCode;
}

int RecoveryService::UserDataBackup(const UserDataBackupRequest &request)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    QJsonObject jsonObject = request.marshal();
    QString reqStr = Utils::JsonToQString(jsonObject);
    // qWarning()<<"UserDataBackup, req = "<<reqStr.toLocal8Bit().data();

    BRContext::getInstance()->setRun(true);
    int opType = static_cast<int>(OperateType::UserDataBackup);
    Context ctx(request.operateID, request.username, opType);
    BRContext::getInstance()->setContext(ctx);

    auto errorCode = m_pUserDataRecovery->userDataBackup(request);
    if (errorCode == OK) {
        OperateLog operateLog;
        operateLog.time = QDateTime::currentMSecsSinceEpoch();
        operateLog.username = request.username;
        operateLog.operateType = OperateType::UserDataBackup;
        operateLog.status = OperateStatus::Running;
        operateLog.remark = request.remark;
        DBInterface::getInstance()->addOperateLog(operateLog);
    } else {
        BRContext::getInstance()->setRun(false);
    }
    return errorCode;
}

QString RecoveryService::ListSystemBackup()
{
    if (!this->checkCaller()) {
        return tr("Invalid Client!");
    }

    BackupInfoList backupInfoList;
    if (-1 == m_systemSyncType) {
        if (m_rootUUID.isEmpty()) {
            GetRootUUID();
        }

        int retCode = AutoInitSystemRecoveryType(m_rootUUID);
        if (retCode != static_cast<int> (ErrorCode::OK)) {
            qCritical()<< Q_FUNC_INFO <<", AutoInitSystemRecoveryType faield, retCode = "<<retCode;
            return QString();
        }
    } else if (m_systemSyncType == static_cast<int> (RecoveryType::Rsync)) {
        QSettings settings(m_recoveryConf, QSettings::IniFormat);
        settings.beginGroup(BACKUP_GROUP);
        QString deviceUUID = settings.value(BACKUP_DEVICE_UUID_KEY).toString();
        QString historyDeviceUUID = settings.value(BACKUP_HISTORY_DEVICE_UUID_KEY).toString();
        settings.endGroup();

#if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
        QStringList uuidList = historyDeviceUUID.split(",", Qt::SkipEmptyParts);
#else
        QStringList uuidList = historyDeviceUUID.split(",", QString::SkipEmptyParts);
#endif
        uuidList.append(deviceUUID);
        uuidList.removeDuplicates();
        backupInfoList = m_pSystemRecovery->listSystemBackup(uuidList);
    } else if (m_systemSyncType == static_cast<int> (RecoveryType::OSTree)) {
        QStringList noUUID;
        backupInfoList = m_pSystemRecovery->listSystemBackup(noUUID);
    } else if (m_systemSyncType == static_cast<int> (RecoveryType::ImmutableMode)) {
        QStringList noUUID;
        backupInfoList = m_pSystemRecovery->listSystemBackup(noUUID);
    } else {
        qWarning()<< Q_FUNC_INFO <<", not support yet! m_systemSyncType = "<<m_systemSyncType;
        return QString();
    }

    QJsonObject jsonObject;
    QJsonArray array;
    for (auto &backupInfo : backupInfoList) {
        QJsonObject backupInfoObject = backupInfo.marshal();
        array.append(backupInfoObject);
    }
    jsonObject.insert("backup", array);
    return Utils::JsonToQString(jsonObject);
}

bool RecoveryService::IsRootfs(const QString &rootUUID)
{
    DevicePtr pDevice(new Device(rootUUID));
    if (pDevice->getDeviceInfo().isNull()) {
        qCritical() << QString("unknow uuid %1").arg(rootUUID);
        return false;
    }

    //rootUUID为当前系统的根分区uuid
    if (pDevice->getDeviceInfo()->mountPoint == "/") {
        return true;
    }
    FSTabInfoPtr fsTabPtr = FSTab::findFSTabByUUID(m_fsTabInfoList, rootUUID);
    if (!fsTabPtr.isNull()) {
        if ("/" == fsTabPtr->mountPoint) {
            return true;
        }
    }
    //指定的rootUUID为空,可能运行于live系统中。需要挂载该分区，然后根据目录结构识别是否为根分区
    if (pDevice->getDeviceInfo()->mountPoint.isEmpty()) {
        if (!pDevice->mount("/mnt")) {
            qInfo() << QString("mount %1 to /mnt failed").arg(pDevice->getDeviceInfo()->deviceName);
            return false;
        }

        bool ret = FSTab::isSystemDirectory(rootUUID, "/mnt");

        if (!pDevice->umount()) {
            qInfo() << QString("umount %1 failed").arg(pDevice->getDeviceInfo()->deviceName);
        }
        return ret;
    }

    return false;
}

bool RecoveryService::MountByFstab(const QString &rootPath)
{
    return false;
}

bool RecoveryService::GetSystemBackupDirSizeBytes(const QString &rootUUID, const QString &backupDeviceUUID,
                                                  const QString &dirPath, const QStringList &excludeDir,
                                                  quint64 &totalBytes, QString &errMsg)
{
    errMsg = "";
    bool samePartition = false;
    if (!Utils::calculateDirSize(dirPath, excludeDir, totalBytes, errMsg, samePartition)) {
    //    qCritical()<<"GetBackupDirSizeBytes: calc dir: "<<dirPath<<" failed, errMsg = "<<errMsg;
        return false;
    }
    //qInfo()<<"dirPath = "<<dirPath<<", totalBytes = "<<totalBytes;

    return true;
}

void RecoveryService::FillErrMsg(ErrorCode errCode, quint64 backupSizeBytes, int liveFlag,
                                 const QString &errMsg, QString &jsonContent)
{
    jsonContent.clear();
    QJsonObject jsonObj;
    jsonObj.insert("errCode", errCode);
    jsonObj.insert("backupSizeBytes", static_cast<qint64>(backupSizeBytes));
    jsonObj.insert("liveFlag", liveFlag);
    jsonObj.insert("errMsg", errMsg);
    jsonContent = Utils::JsonToQString(jsonObj);
}

QString RecoveryService::CheckBackupDiskSpace(const QString &rootUUID, const QString &backupDeviceUUID)
{
    if (!this->checkCaller()) {
        return tr("Invalid Client!");
    }

    QString jsonContent;
    quint64 expectedBackupSizeBytes = 0;
    int liveFlag = 0;
    QString errMsg;
    if (this->IsInLive()) {
        qWarning() << "CheckBackupDiskSpace in live, to do ...";
        liveFlag = 1;
        this->FillErrMsg(UnKnow, expectedBackupSizeBytes, liveFlag, errMsg, jsonContent);
        return  jsonContent;
    }

    QStringList noExcludeDirs;
    quint64 optSizeBytes = 0;
    if (!this->GetSystemBackupDirSizeBytes(rootUUID, backupDeviceUUID, "/opt", noExcludeDirs, optSizeBytes, errMsg)) {
    //    qCritical()<<"CheckBackupDiskSpace: calculateDirSize /opt failed ";
        this->FillErrMsg(UnKnow, expectedBackupSizeBytes, liveFlag, errMsg, jsonContent);
        return jsonContent;
    }

    QStringList varExcludeDirs = {"--exclude=/var/run/*", "--exclude=/var/cache/*"};
    quint64 varSizeBytes = 0;
    if (!this->GetSystemBackupDirSizeBytes(rootUUID, backupDeviceUUID, "/var", varExcludeDirs, varSizeBytes, errMsg)) {
    //    qCritical()<<"CheckBackupDiskSpace: calculateDirSize /var failed";
        this->FillErrMsg(UnKnow, expectedBackupSizeBytes, liveFlag, errMsg, jsonContent);
        return jsonContent;
    }

    const quint64 reserveSizeBytes = static_cast<quint64> (500 * MiB);
    DevicePtr pRootDevice(new Device(rootUUID));
    pRootDevice->calculateDiskSpace();
    DeviceInfoPtr pRootDevInfo = pRootDevice->getDeviceInfo();
    if (nullptr == pRootDevInfo) {
        this->FillErrMsg(NullPointer, expectedBackupSizeBytes, liveFlag, errMsg, jsonContent);
        return jsonContent;
    }
    quint64 backupSizeBytes = optSizeBytes + varSizeBytes + reserveSizeBytes + pRootDevInfo->usedBytes;
    //qDebug()<<"backup="<<backupSizeBytes<<", opt="<<optSizeBytes<<", var="<<varSizeBytes;

    if (rootUUID == backupDeviceUUID) {
        if (pRootDevInfo->availableBytes < backupSizeBytes) {
        //    qCritical()<<"root Insufficient disk space, available = "<<pRootDevInfo->availableBytes;
            this->FillErrMsg(InsufficientDiskSpace, backupSizeBytes, liveFlag, errMsg, jsonContent);
            return jsonContent;
        }
        return jsonContent;
    }

    DevicePtr pBackupDevice(new Device(backupDeviceUUID));
    pBackupDevice->calculateDiskSpace();
    DeviceInfoPtr pBackupDevInfo = pBackupDevice->getDeviceInfo();
    if (nullptr == pBackupDevInfo) {
        this->FillErrMsg(NullPointer, expectedBackupSizeBytes, liveFlag, errMsg, jsonContent);
        return jsonContent;
    }
    if (pBackupDevInfo->availableBytes < backupSizeBytes) {
    //    qCritical()<<"backup Insufficient disk space, available = "<<pBackupDevInfo->availableBytes;
        this->FillErrMsg(InsufficientDiskSpace, backupSizeBytes, liveFlag, errMsg, jsonContent);
        return jsonContent;
    }

    this->FillErrMsg(OK, expectedBackupSizeBytes, liveFlag, errMsg, jsonContent);
    return jsonContent;
}

int RecoveryService::CheckGhostBackupDiskSpace(const QString &selectDir)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    QDir dir(selectDir);
    if (!dir.exists()) {
        sendErrorReply(QDBusError::ErrorType::InvalidArgs,QString("invalid args!"));
        return static_cast<int> (ErrorCode::InvalidDirectory);
    }

    return m_pUImgRecovery->CheckGhostBackupDiskSpace(selectDir);
}

int RecoveryService::SystemRestore(const QString &request)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

//    QJsonObject jsonObject = request.marshal();
//    QString reqStr = Utils::JsonToQString(jsonObject);
//    qWarning()<<"SystemRestore, req = "<<reqStr.toLocal8Bit().data();
    QJsonObject jsonObject = Utils::QStringToJson(request);
    if (jsonObject.isEmpty()) {
        return ParamError;
    }

    SystemRestoreRequest systemRestoreRequest;
    systemRestoreRequest.unmarshal(jsonObject);

    BRContext::getInstance()->setRun(true);
    int opType = static_cast<int>(OperateType::SystemRestore);
    Context ctx(systemRestoreRequest.operateID, systemRestoreRequest.username, opType);
    BRContext::getInstance()->setContext(ctx);

    ErrorCode errorCode = m_pSystemRecovery->systemRestore(systemRestoreRequest);
    BRContext::getInstance()->setRun(false);
    if (errorCode == OK) {
        OperateLog operateLog;
        operateLog.time = QDateTime::currentMSecsSinceEpoch();
        operateLog.username = systemRestoreRequest.username;
        operateLog.operateType = OperateType::SystemRestore;
        operateLog.status = OperateStatus::Running;
        operateLog.remark = ""; // 跟产品再次确认后，系统还原备注为空
        // if (!systemRestoreRequest.backupInfo.systemVersion.isEmpty() ||
        // !systemRestoreRequest.backupInfo.submissionVersion.isEmpty()) {
        //     operateLog.remark = QString("%1 --> %2").arg(systemRestoreRequest.backupInfo.systemVersion)
        //             .arg(systemRestoreRequest.backupInfo.submissionVersion);
        // }
        DBInterface::getInstance()->addOperateLog(operateLog);
    }
    return errorCode;
}

int RecoveryService::UserDataRestore(const UserDataRestoreRequest &request)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    BRContext::getInstance()->setRun(true);
    int opType = static_cast<int>(OperateType::UserDataRestore);
    Context ctx(request.operateID, request.username, opType);
    BRContext::getInstance()->setContext(ctx);

    ErrorCode errorCode = m_pUserDataRecovery->userDataRestore(request);
    if (errorCode == OK) {
        OperateLog operateLog;
        operateLog.time = QDateTime::currentMSecsSinceEpoch();
        operateLog.username = request.username;
        operateLog.operateType = OperateType::UserDataRestore;
        operateLog.status = OperateStatus::Running;
        DBInterface::getInstance()->addOperateLog(operateLog);
    } else {
        BRContext::getInstance()->setRun(false);
    }

    return errorCode;
}

void RecoveryService::Reboot()
{
    if (!this->checkCaller()) {
        return;
    }

    Process::spawnCmd("systemctl", {"reboot"});
}

QString RecoveryService::ListUserDataBackup(const QString &username)
{
    if (!this->checkCaller()) {
        return tr("Invalid Client!");
    }

    QString result;
    if (nullptr == m_pUserDataRecovery) {
        GetRootUUID();
        if (AutoInitSystemRecoveryType(m_rootUUID) != OK) {
            qCritical()<<Q_FUNC_INFO<<"error: call AutoInitSystemRecoveryType failed";
            return result;
        }
    }

    if (nullptr == m_pUserDataRecovery) {
        qCritical()<<Q_FUNC_INFO<<"error: m_pUserDataRecovery is nullptr!";
        return result;
    }

    BackupInfoList backupInfoList = m_pUserDataRecovery->listUserDataBackup(username);

    const int sysEnvTypeImmutable = 1;
    QJsonObject jsonObject;
    QJsonArray array;
    for (auto &backupInfo : backupInfoList) {
        if (m_isImmutableSys) {
            if (sysEnvTypeImmutable != backupInfo.systemEnvType) {
                continue;
            }
        } else {
            if (sysEnvTypeImmutable == backupInfo.systemEnvType) {
                continue;
            }
        }
        QJsonObject backupInfoObject = backupInfo.marshal();
        array.append(backupInfoObject);
    }
    jsonObject.insert("backup", array);

    result = Utils::JsonToQString(jsonObject);

    return result;
}

int RecoveryService::RemoveBackupInfo(const UserDataRestoreRequest &request)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }
//    qWarning()<<"RemoveBackupInfo, req = "<<request.toLocal8Bit().data();

    BRContext::getInstance()->setRun(true);
    int opType = static_cast<int>(OperateType::removeBackup);
    Context ctx(request.operateID, request.username, opType);
    BRContext::getInstance()->setContext(ctx);

    ErrorCode errorCode = UnKnow;
    OperateType reqOpType = static_cast<OperateType> (request.backupInfo.operateType);
    if (reqOpType == OperateType::UserDataBackup) {
        errorCode = m_pUserDataRecovery->removeUserDataBackup(request);
    } else if (reqOpType == OperateType::SystemBackup || reqOpType == OperateType::ImmutableSystemBackup) {
        errorCode = m_pSystemRecovery->removeSystemBackup(request);
    } else if (reqOpType == OperateType::GhostBackup) {
        errorCode = m_pUImgRecovery->removeUImgBackup(request);
    }

    if (errorCode == OK) {
        OperateLog operateLog;
        operateLog.time = QDateTime::currentMSecsSinceEpoch();
        operateLog.username = request.username;
        operateLog.operateType = OperateType::removeBackup;
        operateLog.status = OperateStatus::Running;
        DBInterface::getInstance()->addOperateLog(operateLog);
    } else {
        BRContext::getInstance()->setRun(false);
    }
    return errorCode;
}

int RecoveryService::CreateUImg(const SystemCloneRequest &request)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    BRContext::getInstance()->setRun(true);
    int opType = static_cast<int>(request.operateType);
    Context ctx(request.operateID, request.username, opType);
    BRContext::getInstance()->setContext(ctx);
//    qWarning()<<"CreateUImg, req: "<<request.toLocal8Bit().data();

    return m_pUImgRecovery->createUImg(request);
}

QString RecoveryService::QueryUImgBackup(const GhostLogQuery &request)
{
    QString result;
    GhostLogList logList = DBInterface::getInstance()->queryGhostLog(request);
    QJsonObject logs;
    QJsonArray array;
    for (auto &log : logList) {
        QJsonObject logJsonObject = log.marshal();
        array.append(logJsonObject);
    }
    logs.insert("logs", array);

    result = Utils::JsonToQString(logs);

    return result;
}

QString RecoveryService::ListOperateLogs(const OperateLogQuery &request)
{
    if (!this->checkCaller()) {
        return tr("Invalid Client!");
    }

    QString result;
    OperateLogList operateLogList = DBInterface::getInstance()->queryOperateLog(request);

    QJsonObject logs;
    QJsonArray array;
    for (auto &log : operateLogList) {
        QJsonObject logJsonObject = log.marshal();
        array.append(logJsonObject);
    }
    logs.insert("logs", array);

    result = Utils::JsonToQString(logs);

    return result;
}

int RecoveryService::DimFileToLoopDeviceFile(const QString &dimFilePath, const QString &imgFilePath)
{
    sendErrorReply(QDBusError::ErrorType::NotSupported,QString("not supported!"));
    return -1; // not used now
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    if (m_pDimFileRecovery->dimFileTrans(dimFilePath, imgFilePath)) {
        return OK;
    } else {
        return UnKnow;
    }
}

int RecoveryService::ClearDimFileRestoreCfg(const QString &imgFilePath)
{
    sendErrorReply(QDBusError::ErrorType::NotSupported,QString("not supported!"));
    return -1; // not used now
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    if (m_pDimFileRecovery->clearDimFileRestoreCfg(imgFilePath)) {
        return OK;
    } else {
        return UnKnow;
    }
}

int RecoveryService::CheckDimFileSpace(const QString &dimFilesPath)
{
    sendErrorReply(QDBusError::ErrorType::NotSupported,QString("not supported!"));
    return -1; // not used now
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    return m_pDimFileRecovery->checkDimFileSpace(dimFilesPath);
}

bool RecoveryService::HasInitBackDimFile()
{
    if (!this->checkCaller()) {
        return false;
    }

    return m_pDimFileRecovery->hasInitBackDimFile();
}

int RecoveryService::CheckIncSystemBackupSpace(const QString &backupDeviceUUID)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    SystemBackupRequest sysReq;
    sysReq.destUUID = backupDeviceUUID;

    ErrorCode errorCode = m_pSystemRecovery->checkIncSystemBackupSpace(sysReq);
    return errorCode;
}

int RecoveryService::CheckIncSystemBackupSpace(const SystemBackupRequest &request)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    ErrorCode errorCode = m_pSystemRecovery->checkIncSystemBackupSpace(request);
    return errorCode;
}

int RecoveryService::CheckFullSystemBackupDiskSpace(const QString &rootUUID, const QString &backupDeviceUUID)
{
    if (!this->checkCaller()) {
        return static_cast<int> (ErrorCode::InvalidClient);
    }

    SystemBackupRequest sysReq;
    sysReq.rootUUID = rootUUID;
    sysReq.destUUID = backupDeviceUUID;

    ErrorCode errorCode = m_pSystemRecovery->checkFullSystemBackupSpace(sysReq);
    return errorCode;
}

int RecoveryService::OStreeFactoryRestore(const SystemRestoreRequest &request)
{
    if (!this->checkCaller()) {
        qInfo()<<"OStreeFactoryRestore, invalid caller";
        return static_cast<int> (ErrorCode::InvalidClient);
    }

     if (!Utils::isOStree()) {
         qInfo()<<"OStreeFactoryRestore, is not ostree";
         sendErrorReply(QDBusError::ErrorType::NotSupported,QString("is not ostree!"));
         return -1;
     }

    BRContext::getInstance()->setRun(true);
    int opType = static_cast<int>(OperateType::FactoryRestore);
    Context ctx(request.operateID, request.username, opType);
    BRContext::getInstance()->setContext(ctx);

    if (m_ostreeWorker == nullptr) {
        m_ostreeWorker = new OSTreeWorkerThread(OperateType::FactoryRestore);

        connect(m_ostreeWorker, &OSTreeWorkerThread::success, [=] {
            OperateLog operateLog;
            operateLog.time = QDateTime::currentMSecsSinceEpoch();
            operateLog.username = request.username;
            operateLog.operateType = OperateType::FactoryRestore;
            operateLog.status = OperateStatus::Running;
            operateLog.remark = ""; // 跟产品再次确认后，系统还原备注为空
            // if (!factoryRestoreReq.backupInfo.systemVersion.isEmpty() ||
            //     !factoryRestoreReq.backupInfo.submissionVersion.isEmpty()) {
            //     operateLog.remark = QString("%1 --> %2").arg(factoryRestoreReq.backupInfo.systemVersion)
            //             .arg(factoryRestoreReq.backupInfo.submissionVersion);
            // }
            DBInterface::getInstance()->addOperateLog(operateLog);
            onSuccess("");
        });

        connect(m_ostreeWorker, &OSTreeWorkerThread::error, this, &RecoveryService::onError);

        connect(m_ostreeWorker, &QThread::finished, [=] {
            m_ostreeWorker->deleteLater();
            m_ostreeWorker = nullptr;
            BRContext::getInstance()->setRun(false);
            qInfo()<<"m_ostreeWorker OStreeFactoryRestore finished";
        });
    }

    if (m_ostreeWorker != nullptr) {
        m_ostreeWorker->setRequest(request);
        if (!m_ostreeWorker->isRunning()) {
            m_ostreeWorker->start();
        }
    }

    return 0;
}

void RecoveryService::ClearRollbackAutoNotify()
{
    QString rootPath = "/";
    QString recoveryConf = rootPath + UOS_RECOVERY_INI;

    QSettings settings(recoveryConf, QSettings::IniFormat);
    settings.beginGroup(RESTORE_GROUP);
    settings.setValue(RESTORE_ROLLBACK_STATUS, -1);
    settings.setValue(RESTORE_ROLLBACK_VERSION, "");
    settings.setValue(RESTORE_ROLLBACK_BEFORE_VERSION, "");
    settings.endGroup();

    const QString uosRecoveryNotifyPath = "/etc/xdg/autostart/uos-recovery-notify.desktop";
    if (QFile::exists(uosRecoveryNotifyPath)) {
        QFile::remove(uosRecoveryNotifyPath);
    }
}

bool RecoveryService::IsRunning()
{
    bool isRun = false;
    static bool isOSTree = Utils::isOStree();
    if (isOSTree) {
        isRun = m_pSystemRecovery->isRunning();
        if (isRun) {
            return true;
        }
    }

    return BRContext::getInstance()->isRunning();
}

int RecoveryService::GetContext(QString &operateID, QString &userName, int &opType, int &progress)
{
    Context ctx = BRContext::getInstance()->getContext();
    operateID = ctx.operateID;
    userName = ctx.userName;
    opType = ctx.opType;
    if (m_opIDProgress.contains(operateID)) {
        progress = m_opIDProgress[operateID];
    } else {
        progress = 0;
    }
    qWarning()<<"RecoveryService::GetContext, operateID = "<<operateID<<", opType = "<<opType<<", progress = "<<progress;

    return 0;
}
