//
// Created by zxm on 2022/12/29.
//
#include "CommonFunc.h"
#include <QDir>
#include <QFile>
#include <QSettings>
#include <QDebug>
#include "utils/global.h"
#include "utils/Utils.h"
#include "utils/Device.h"
#include <udisks2-qt6/ddiskdevice.h>
#include <udisks2-qt6/dblockdevice.h>
#include <udisks2-qt6/ddiskmanager.h>
#include <udisks2-qt6/dblockpartition.h>
#include <selinux/selinux.h>
#include <QStandardPaths>
#include <QString>
#include <QtDBus/QDBusInterface>
#include <QtDBus/QtDBus>

QString Common::GetOperateTypeDes(int opType)
{
    static QMap<int, QString> allOpType = {
            {static_cast<int>(OperateType::SystemBackup), tr("System Backup")},
            {static_cast<int>(OperateType::SystemRestore), tr("System Restore")},
            {static_cast<int>(OperateType::UserDataBackup), tr("Data backup")},
            {static_cast<int>(OperateType::UserDataRestore), tr("Data restore")},
            {static_cast<int>(OperateType::removeBackup), tr("Delete backup")},
            {static_cast<int>(OperateType::GhostBackup), tr("System Clone")},
            {static_cast<int>(OperateType::CheckUserDataBackupSpace), tr("Calculating file size...")},
            {static_cast<int>(OperateType::FactoryRestore), tr("Reset to factory settings")}
    };

    QString opTypeDes = "";
    if (allOpType.contains(opType)) {
        opTypeDes = allOpType.value(opType);
    }

    return opTypeDes;
}

void Common::getHistoryBackup(QList<BackupFileInfo> &backupList, const QString &backupPath, bool needUmount) {
    backupList.clear();
    auto &obj = Utils::readPathfromJsonFile(backupPath);
    for (const auto &key : obj.keys()) {
        QString mountPoint;
        const QStringList &devices = DDiskManager::blockDevices({});
        for (const auto &path : devices) {
            QScopedPointer<DBlockDevice> device(DDiskManager::createBlockDevice(path));
            if (key != device->idUUID()) {
                continue;
            }
            // 如果磁盘已经挂载会返回一个提示信息,否则会返回挂载点
            device->setWatchChanges(true);
            const QString &mountInfo = device->mount({}).simplified();
            // 根据返回的内容是否包含空格判断是已经挂载还是没有挂载
            if (mountInfo.contains(" ")) {
                // 已经挂载
                mountPoint = device->mountPoints().first();
            } else {
                mountPoint = mountInfo;
            }
            break;
        }

        if (mountPoint.isEmpty()) {
            continue;
        }

        const QJsonObject &subObj = obj[key].toObject();
        for (const auto &subKey : subObj.keys()) {
            QString backupFilePath = subObj[subKey].toString();
            QDir backDir(mountPoint + backupFilePath);
            QStringList dimList = backDir.entryList({"*.dim"});
            QStringList tarFileList = backDir.entryList({"*.tar"});
            if (!dimList.isEmpty() && tarFileList.count() == 2) {
                QDateTime dateTime = QDateTime::fromSecsSinceEpoch(subKey.toUInt());
                BackupFileInfo info;
                info.backupUUID = key;
                info.backupTime = dateTime.toString("yyyy/MM/dd");
                info.backupPath = backupFilePath;
                info.createTime = subKey;
                info.backupFullPath = mountPoint + backupFilePath;
                backupList.append(info);
            }
        }

        //v降序排序
        std::sort(backupList.begin(), backupList.end(), [](const BackupFileInfo &left, const BackupFileInfo &right) {
            return left.createTime.toUInt() > right.createTime.toUInt();
        });

#ifndef QT_DEBUG
        if (needUmount) {
            Common::umountDeviceByUUID(key);
        }
#endif
    }
}

void Common::umountDeviceByUUID(const QString &uuid)
{
    const QStringList &devices = DDiskManager::blockDevices({});
    for (const QString &path : devices) {
        QScopedPointer<DBlockDevice> device(DDiskManager::createBlockDevice(path));
        if (device->idUUID() == uuid) {
            device->unmount({});
            break;
        }
    }
}

bool Common::isSupportV20BackupAndRestore()
{
    QString liveSystemDir = "/usr/doppel/";
    if (!QDir(liveSystemDir).exists()) {
        qWarning() << "dir '/usr/doppel/' not exists";
        return false;
    }

    QString liveSystemPath = liveSystemDir + "filesystem.squashfs";
    QFile liveFile(liveSystemPath);
    if (!liveFile.exists()) {
        qWarning()<<"liveSystemPath not exist!, liveSystemPath: "<<liveSystemPath;
        return false;
    }

    QString recoveryPath = "/etc/deepin/system-recovery.conf";
    if (!QFile(recoveryPath).exists()) {
        qWarning()<<"recoveryPath not exist!, recoveryPath: "<<recoveryPath;
        return false;
    }

    QSettings settings(recoveryPath, QSettings::IniFormat);
    QString recoveryUUID = settings.value("UUID").toString();
    if (recoveryUUID.isEmpty()) {
        qWarning()<<"recovery UUID is empty ";
        return false;
    }

    QString fstabFile = "/etc/fstab";
    QStringList sysMountPointList;
    bool isFullInstall = Device::isFullDiskInstall(fstabFile, sysMountPointList);
    if (!isFullInstall) {
        qWarning()<<"not full disk install, sysMountPointList: "<< sysMountPointList;
        return false;
    }

    return true;
}

bool Common::isLiveCD()
{
    QFile cmdLine("/proc/cmdline");
    if (!cmdLine.exists()) {
        qWarning()<<"isLiveCD /proc/cmdline not exists!";
        return false;
    }

    if (!cmdLine.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning()<<"isLiveCD open /proc/cmdline failed";
        return false;
    }

    QString unions;
    QString bootImage;
    QString boot;
    QString root;
    const QList<QByteArray> &list = cmdLine.readAll().split(' ');
    for (const QByteArray &cmd : list) {
        if (cmd.startsWith("BOOT_IMAGE=")) {
            bootImage = cmd.split('=').last().simplified();
            continue;
        } else if (cmd.startsWith("boot=")) {
            boot = cmd.split('=').last().simplified();
            continue;
        } else if (cmd.startsWith("union=")) {
            unions = cmd.split('=').last().simplified();
            continue;
        } else if (cmd.startsWith("root=live:")) {
            root = cmd.split(':').first().simplified(); // root=live:CDLABEL=UOS
            continue;
        }
    }
    cmdLine.close();

    if ("live" == boot) {
        if ("overlay" == unions) {
            return true;
        }

        if ("root=live" == root) {
            return true; // loongson
        }
    }

    return false;
}

bool Common::isSeUser()
{
    bool isSelinuxUser = false;
    QString userPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
    QString userName = userPath.section("/", -1, -1);
    char *seuser = new char[32];
    char *level = new char[32];

    if (0 == getseuserbyname(userName.toStdString().c_str(), &seuser, &level)) {
        QString seUser = QString((const char *)seuser);
        if ("sysadm_u" == seUser || "root" == seUser) {
            isSelinuxUser = true;
        }
    }
    delete[] seuser;
    delete[] level;

    return isSelinuxUser;
}

bool Common::isSecurityEnhanced()
{
    static const QString DBUS_SECURITY_ENHANCE_SERVICE = "com.deepin.daemon.SecurityEnhance";
    static const QString DBUS_SECURITY_ENHANCE_PATH = "/com/deepin/daemon/SecurityEnhance";
    static const QString DBUS_SECURITY_ENHANCE_INTERFACE = DBUS_SECURITY_ENHANCE_SERVICE;
    static const QString DBUS_SECURITY_ENHANCE_STATUS = "Status";
    static const QString STATUS_OPEN = "open";

    QDBusInterface inter(DBUS_SECURITY_ENHANCE_SERVICE,
                         DBUS_SECURITY_ENHANCE_PATH,
                         DBUS_SECURITY_ENHANCE_INTERFACE,
                         QDBusConnection::systemBus());
    QDBusReply<QString> statusReply = inter.call(DBUS_SECURITY_ENHANCE_STATUS);
    if (statusReply.isValid()) {
        QString status = statusReply.value();
        if (STATUS_OPEN == status) {
            return true;
        }
    }

    return false;
}

QStringList Common::getDenyUserSecurityList(const QString &entity)
{
    QStringList denyList;
    static const QString DBUS_USEC1_ACCESS_CONTROL_SERVICE = "org.deepin.usec1";
    static const QString DBUS_USEC1_ACCESS_CONTROL_PATH = "/org/deepin/usec1/AccessControl";
    static const QString DBUS_USEC1_ACCESS_CONTROL_INTERFACE = "org.deepin.usec1.AccessControl";
    static const QString DBUS_USEC1_ACCESS_CONTROL_GET_POLICY = "GetPolicy";

    QDBusInterface inter(DBUS_USEC1_ACCESS_CONTROL_SERVICE,
                         DBUS_USEC1_ACCESS_CONTROL_PATH,
                         DBUS_USEC1_ACCESS_CONTROL_INTERFACE,
                         QDBusConnection::systemBus());
    QDBusReply<QString> policyReply = inter.call(DBUS_USEC1_ACCESS_CONTROL_GET_POLICY, entity);
    if (!policyReply.isValid()) {
        return denyList;
    }

    QString DenyPolicy = policyReply.value();
    QJsonObject jsonObj = Utils::QStringToJson(DenyPolicy);
    if (jsonObj.contains("policies")) {
        QJsonArray policyArray = jsonObj.value("policies").toArray();
        int arraySize = policyArray.size();
        for (int i = 0; i < arraySize; ++i) {
            QJsonObject policyObj = policyArray.at(i).toObject();
            if (policyObj.contains("objects")) {
                QJsonArray objValArray = policyObj.value("objects").toArray();
                int objSize = objValArray.size();
                for (int j = 0; j < objSize; ++j) {
                    QJsonObject obj = objValArray.at(j).toObject();
                    QJsonArray objArray = obj.value("operations").toArray();
                    if (objArray.contains("deny")) {
                        QString denyPath = obj.value("object").toString();
                        qWarning()<<"denyPath: "<<denyPath;
                        denyList.append(denyPath);
                    }
                }
            }
        }
    }

    return denyList;
}
