//
// Created by uos on 2022/3/17.
//

#include "AdvancedModule.h"
#include "utils/Device.h"
#include "utils/Utils.h"
#include "utils/global.h"
#include "AdvancedWidget.h"
#include "AdvancedGhostWidget.h"
#include "common/ProgressWidget.h"
#include "common/ResultWidget.h"
#include "common/SelectPartitionDialog.h"
#include "common/CommonFunc.h"

#include <QFileDialog>
#include <QDesktopServices>
#include <QStorageInfo>

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

}

AdvancedModule::~AdvancedModule()
{

}

void AdvancedModule::initialize()
{
    if (m_advancedWidget == nullptr) {
        m_advancedWidget = new AdvancedWidget();
    }

    if (m_advancedGhostWidget == nullptr) {
        m_advancedGhostWidget = new AdvancedGhostWidget();
    }

    if(m_progressWidget == nullptr) {
        m_progressWidget = new ProgressWidget("maintitle", "subtitle", "");
    }

    connect(m_advancedWidget, &AdvancedWidget::showGhostWidgetSignal, this, &AdvancedModule::onShowGhostWidget);
    connect(m_advancedGhostWidget, &AdvancedGhostWidget::cancelBtnSignal, this, &AdvancedModule::onBack);
    connect(m_advancedGhostWidget, &AdvancedGhostWidget::checkSpaceSignal, this, &AdvancedModule::onCheckSpace, Qt::QueuedConnection);
    connect(m_advancedGhostWidget, &AdvancedGhostWidget::startMakeBtnSignal, this, &AdvancedModule::onStartMake, Qt::QueuedConnection);
    connect(m_recoveryInterface, &ComDeepinDaemonUosrecoveryInterface::ReportProgress, this, &AdvancedModule::updateProgress);
    connect(m_recoveryInterface, &ComDeepinDaemonUosrecoveryInterface::Error, this, &AdvancedModule::onError);
    connect(m_recoveryInterface, &ComDeepinDaemonUosrecoveryInterface::ReportSpace, this, &AdvancedModule::onSpaceChanged);
    connect(m_recoveryInterface, &ComDeepinDaemonUosrecoveryInterface::Success, this, &AdvancedModule::onSuccess);
}

QString AdvancedModule::name() const
{
    return "AdvancedModule";
}

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

QString AdvancedModule::text() const
{
    return QString(tr("Advanced"));
}

void AdvancedModule::active()
{
    m_frameProxy->popAllWidget();
    m_frameProxy->setCurrentWidget(this, m_advancedWidget);
}

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

void AdvancedModule::onBackHome()
{
    if (nullptr != m_frameProxy) {
        m_advancedGhostWidget->setDestDirText("");
        m_advancedGhostWidget->setResultInfo("");
        m_frameProxy->backHome();
    }
}

bool AdvancedModule::onCheckSpace(const QString &selectDir)
{
    if (selectDir.isEmpty()) {
        return false;
    }

    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_advancedGhostWidget->setResultInfo(tips, false);
        qWarning()<<"onCheckSpace, another task is running, operateID = "<<operateID<<", userName = "<<userName
               <<", opType = "<<opType<<", progress = "<<progress;
        return false;
    }

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

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

    return true;
}

void AdvancedModule::onStartMake(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_advancedGhostWidget->setResultInfo(tips, false);
        qWarning()<<"onStartMake, another task is running, operateID = "<<operateID<<", userName = "<<userName
                  <<", opType = "<<opType<<", progress = "<<progress;
        return;
    }

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

    m_curOperateID = req.operateID;

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

}

void AdvancedModule::onShowGhostWidget()
{
    m_advancedGhostWidget->setDestDirText("");
    if (m_isSelinuxEnabled) {
        QString selinuxMsg = tr("The system has enabled peripherals management, cannot use full machine backup.");
        m_advancedGhostWidget->setResultInfo(selinuxMsg, false);
        m_advancedGhostWidget->setWidgetEnable(false);
    } else {
        m_advancedGhostWidget->setResultInfo("", false);
    }
    m_frameProxy->setCurrentWidget(this, m_advancedGhostWidget);
}

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

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

    Response rsp;
    rsp.unmarshal(jsonObject);
    if (m_curOperateID != rsp.operateID) {
        return;
    }

    if (m_progressWidget != nullptr) {
        auto curProgress = rsp.progress;
        auto remainSecond = rsp.remainSecond;
        m_progressWidget->setRemainTime(remainSecond);
        m_progressWidget->setValue(curProgress);
        // if (curProgress >= 100) {
        //     onShowResult(true, operateType, "");
        // }

    //    qInfo() << "updateProgress : " << jsonObject;
    }
}

void AdvancedModule::onSuccess(const QString &msg)
{
    QJsonObject jsonObject = Utils::QStringToJson(msg);
    int operateType = jsonObject.value("operateType").toInt();
    if (operateType != static_cast<int>(OperateType::GhostBackup)) {
        return;
    }

    Response rsp;
    rsp.unmarshal(jsonObject);
    if (m_curOperateID != rsp.operateID) {
        return;
    }

    onShowResult(true, operateType, "");
}

void AdvancedModule::onShowProgress(const QString &mainTitle, const QString &subTitle, const QString &warning)
{
    if (m_progressWidget == nullptr) {
        m_progressWidget = new ProgressWidget(mainTitle, subTitle, warning);
    } else {
        m_progressWidget->setMainTitle(mainTitle);
        m_progressWidget->setSubTitle(subTitle);
        m_progressWidget->setWarning(warning);
    }

    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);

    m_progressWidget->start();
}

void AdvancedModule::onShowResult(bool success, int operateType, const QString &errorMsg)
{
    QString resultText = "";
    QString btnText = "";

    if (operateType == static_cast<int>(OperateType::GhostBackup)) {
        resultText = success ? tr("Creation successful!") : tr("Sorry, creation failed!");
        btnText = tr("OK", "button");
    } else {
        return;
    }

    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 ResultWidget(success, resultText, errorMsg, btnText, false);
        connect(m_resultWidget, &ResultWidget::done, this, &AdvancedModule::onBackHome);
        connect(m_resultWidget, &ResultWidget::viewBtnClicked, this, [=](){
            // 显示ghost镜像路径
            QDesktopServices::openUrl(QUrl("file://" + m_advancedGhostWidget->getDestDirText()));
        });
    } else {
        m_resultWidget->set(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 AdvancedModule::onError(const QString &errMsg)
{
    QJsonObject jsonObject = Utils::QStringToJson(errMsg);
    int opType = -1;
    if (jsonObject.contains("operateType")) {
        opType = jsonObject.value("operateType").toInt();
    }

    if (opType != static_cast<int>(OperateType::GhostBackup)) {
        return;
    }

    Response rsp;
    rsp.unmarshal(jsonObject);
    if (m_curOperateID != rsp.operateID) {
        qWarning()<<"onError, m_curOperateID: "<<m_curOperateID<<", operateID: "<<rsp.operateID;
        return;
    }

    onShowResult(false, opType, "");
}

void AdvancedModule::onSpaceChanged(const QString &space)
{
    QJsonObject jsonObject = Utils::QStringToJson(space);
    int operateType = jsonObject.value("operateType").toInt();
    if (operateType == static_cast<int>(OperateType::CheckGhostBackupSpace)) {
        qDebug()<<"AdvancedModule::onSpaceChanged, space = "<<space.toLocal8Bit().data();
        // 处理提示
        int errCode = jsonObject.value("errCode").toInt();
        QString destPath = jsonObject.value("destDir").toString();
        if (errCode == OK) {
            m_backupSizeBytes = jsonObject.value("backupSizeBytes").toVariant().toLongLong();
            m_advancedGhostWidget->setDestDirText(destPath);
            m_advancedGhostWidget->setResultInfo("");
            QDir mediaDir("/media");
            QString realMediaDir = mediaDir.canonicalPath();
            if (destPath.startsWith("/media/") || destPath.startsWith(realMediaDir)) {
                m_advancedGhostWidget->setErrorInfo(tr("Choosing an external disk will reduce the production speed and success rate. Please choose carefully."));
            } else {
                m_advancedGhostWidget->setErrorInfo("");
            }
        } else if (errCode == InsufficientDiskSpace) {
            m_advancedGhostWidget->setDestDirText("");
            m_advancedGhostWidget->setResultInfo(tr("Insufficient space in the selected directory. Please reselect one."));
        } else {
            m_advancedGhostWidget->setResultInfo(jsonObject.value("errMsg").toString());
        }

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

void AdvancedModule::setLVM(bool isLVM)
{
    m_isLVMOnly = isLVM;
    if (nullptr != m_advancedWidget) {
        m_advancedWidget->setLVM(isLVM);
    }
}

void AdvancedModule::setEncrypted(bool isEncrypted)
{
    m_isEncrypted = isEncrypted;
    if (nullptr != m_advancedWidget) {
        m_advancedWidget->setEncrypted(isEncrypted);
    }
}
