/*
 * Copyright (C) 2025, KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * Authors: Wenjie Xiang <xiangwenjie@kylinos.cn>
 *
 */

#include "idm-actions-manager.h"
#include "side-bar-abstract-item.h"
#include "directory-view-widget.h"
#include "idm-dbus-manager.h"
#include "file-launch-manager.h"
#include "file-launch-action.h"
#include "file-lauch-dialog.h"
#include "fm-window.h"
#include "plugin-manager.h"
#include "clipboard-utils.h"
#include "kysdk/applications/kmessagebox.h"
#include "properties-window-factory-plugin-manager.h"
#include "file-utils.h"
#include <QInputDialog>
#include <QMessageBox>
#include <QMenu>
#include <QFileDialog>
#include <QApplication>
#include <QDebug>
#include <QProcess>
#include <menu-plugin-iface.h>
#include <kinputdialog.h>
#include <kmessagebox.h>
#include <gio/gdesktopappinfo.h>
#include "menu-plugin-manager.h"
using namespace Peony;

static IDMActionsManager *globalInstance = nullptr;


IDMActionsManager *IDMActionsManager::getInstance()
{
    if (!globalInstance) {
        globalInstance = new IDMActionsManager;
    }
    return globalInstance;
}

const QList<QAction *> IDMActionsManager::constructIDMActions(SideBarAbstractItem *item, FMWindowIface *window, DirectoryViewWidget *view, IDMActionsManager::MenuType type)
{
    QList<QAction *> l;
    m_realselectUris.clear();
    QStringList selectUris;
    for (auto selectUri : getSelections(view, item)) {
       selectUris.append(FileUtils::urlDecode(selectUri));
    }
    QString directoryUri = FileUtils::urlDecode(getDirectoryUri(view, item));
    bool isFileSelected = !selectUris.isEmpty();
    bool ismultiselected = selectUris.count() > 1 ? true : false;
    bool isRootDirectory = directoryUri == "idm:///" ? true : false;
    bool isDynamic = false;

    QString uri = selectUris.isEmpty() ? "" : selectUris.first();
    QString realUri = getRealUriFromUri(uri);
    QString spaceName;
    if (MenuType::SideBar == type) {
        spaceName = directoryUri.remove("idm:///");
    } else if (MenuType::DirectoryView == type){
        if (isFileSelected && isRootDirectory) {
            spaceName = selectUris.first().remove("idm:///");
        } else if (!isRootDirectory) {
            spaceName = directoryUri.remove("idm:///");
        }
    }
    m_realselectUris = selectUris;
    auto info = FileInfo::fromUri(uri);
    auto displayName = info->displayName();
    auto mimeType = info->mimeType();
    GAppInfo *m_app_info = nullptr;
    if(info->isDesktopFile()){
        GDesktopAppInfo *appInfo = g_desktop_app_info_new_from_filename(info->filePath().toUtf8().constData());
        m_app_info  = G_APP_INFO(appInfo);
    }else{
        GAppInfo *appinfo = g_app_info_get_default_for_type(mimeType.toUtf8().constData(), false);
        auto app_info = G_APP_INFO(appinfo);
        m_app_info = static_cast<GAppInfo*>(g_object_ref(app_info));
    }
    GIcon *icon = g_app_info_get_icon(m_app_info);
    QIcon  appicon;
    auto iconName = FileUtils::getIconStringFromGIcon(icon);
    if (iconName.startsWith("/")) {
        appicon.addFile(iconName);
    } else {
        appicon = QIcon::fromTheme(iconName);
    }
    appicon = appicon.isNull() ? QIcon::fromTheme("document-open-symbolic") : appicon;

    qDebug() << __func__ << spaceName << realUri;
    if (!spaceName.isEmpty()) {
        isDynamic = IDMDBusManager::getInstance()->getSpacestatus(spaceName);
    }

    auto renamAction = new QAction(QIcon::fromTheme("rename-symbolic"),tr("Rename Spaces"));
    connect(renamAction, &QAction::triggered, this, [=](){
        QWidget *widget = QApplication::topLevelAt(QCursor::pos());
        kdk::KInputDialog d(widget);
        connect(&d, &kdk::KInputDialog::textValueChanged, [&](const QString &currentText) {
                 if (currentText.toUtf8().size() > 255) {
                     QString truncatedText = truncateToByteLimit(currentText, 255);
                     d.blockSignals(true);
                     d.setTextValue(truncatedText);
                     d.blockSignals(false);
                 }
             });
        d.setWindowFlag(Qt::Dialog);
        d.setInputMode(kdk::KInputDialog::TextInput);
        d.setLabelText(tr("Space Name"));
        d.setPlaceholderText(tr("Please input space name"));
        d.setTextValue(spaceName);
        d.setOkButtonText(tr("OK"));
        d.setCancelButtonText(tr("Cancel"));
        d.setWindowTitle(tr("Rename Space"));
        QIcon themIcon = QIcon::fromTheme("kylin-data-manager");
        d.setWindowIcon(themIcon);
        d.setWindowFlag(Qt::Dialog);
        if (d.exec()) {
            QString text = d.textValue();
            if (!text.isNull() && !text.isEmpty()) {
                bool isSuccess = IDMDBusManager::getInstance()->renameSpace(spaceName, text);
                if (!isSuccess){
                     QMessageBox::warning(widget,"", tr("Failed to rename space %1 to %2, %2 is existed.").arg(spaceName).arg(text), QMessageBox::Ok);
                }
            }
        }
    });

    auto editCondition = new QAction(QIcon::fromTheme("edit-symbolic"),tr("Edit Space Condition"));
    connect(editCondition, &QAction::triggered, this, [=](){
        QString uri = "idm:///" + QString(QUrl::toPercentEncoding(spaceName));
        Q_EMIT IDMDBusManager::getInstance()->editSpaceConditionSignal(uri);
    });

    auto refreshAction = new QAction(QIcon::fromTheme("window-close-symbolic"),tr("Close dynamically"));
    if (!isDynamic) {
        refreshAction->setText(tr("Open dynamically"));
    }
    //更新状态
    connect(refreshAction, &QAction::triggered, this, [=](){
        IDMDBusManager::getInstance()->updateSpacestatus(spaceName, !isDynamic);
    });

    auto deleteAction = new QAction(QIcon::fromTheme("user-trash-symbolic"),tr("Delete Spaces"));
    connect(deleteAction, &QAction::triggered, this, [=](){
        QString showSpaceName = "“" +spaceName+ "”";
        QString title = QString(tr("Are you sure to delete space %1 ?")).arg(showSpaceName);
        QWidget *widget = QApplication::topLevelAt(QCursor::pos());
        kdk::KMessageBox questionBox(widget);
        questionBox.setIconPixmap(QPixmap (5,5));
        questionBox.setIcon(kdk::KMessageBox::Icon::Question);
        auto cancelButton = new QPushButton(tr("Cancel"));
        auto okButton = new QPushButton(tr("OK"));
        questionBox.addButton(cancelButton, kdk::KMessageBox::RejectRole);
        questionBox.addButton(okButton, kdk::KMessageBox::AcceptRole);
        okButton->setProperty("isImportant", true);
        okButton->setDefault(true);
        okButton->setFocus();
        questionBox.setText(title);
        questionBox.createWinId();
        questionBox.exec();
        auto resultButton = questionBox.clickedButton();
        if (resultButton == okButton){
            IDMDBusManager::getInstance()->removeSpace(spaceName);
        }
    });
    int spaceId = IDMDBusManager::getInstance()->getSpaceIdByName(spaceName);
    bool isDeFaultSpace  = spaceId == 1 ? true:false;
    deleteAction->setDisabled(isDeFaultSpace);
    auto openAction = new QAction(appicon,tr("Open"));
    connect(openAction, &QAction::triggered, this, [=](){
        if (window) {
            auto info = FileInfo::fromUri(uri);
            if (info->isDir()) {
                window->goToUri(uri, true);
            } else {
                FileLaunchManager::openAsync(uri, false, false);
            }
        }
    });

    auto openWithAction = new QAction(QIcon::fromTheme("openwith-symbolic"),tr("Open With"));
    QMenu *openWithMenu = new QMenu;
    openWithMenu->setProperty("skipHighlightIconEffect", true);
    auto recommendActions = Peony::FileLaunchManager::getRecommendActions(realUri);
    auto fallbackActions = Peony::FileLaunchManager::getFallbackActions(realUri);
    if (recommendActions.count() == 0 && fallbackActions.count() == 0)
    {
        auto action = Peony::FileLaunchManager::getDefaultAction(realUri);
        if (action != NULL && action->getAppInfoDisplayName().length() > 0)
            recommendActions.append(action);
    }
    for (auto action : recommendActions) {
        action->setParent(openWithMenu);
        openWithMenu->addAction(static_cast<QAction*>(action));
    }

    for (auto action : fallbackActions) {
        action->setParent(openWithMenu);
        openWithMenu->addAction(static_cast<QAction*>(action));
    }
    openWithMenu->addSeparator();
    openWithMenu->addAction(tr("More applications"), this, [=]() {
        Peony::FileLauchDialog d(realUri);
        d.exec();
    });
    openWithAction->setMenu(openWithMenu);

    auto openLocationAction = new QAction(QIcon::fromTheme("folder-visiting-symbolic"),tr("Open The Folder Location"));
    openLocationAction->setEnabled(!ismultiselected);
    QString realPath;
    if (!realUri.isEmpty()) {
        realPath = QString(realUri).remove("file://");
    }
    connect(openLocationAction, &QAction::triggered, this, [=](){
        if (!realUri.isEmpty()) {
            QFileInfo info(realPath);
            QString directoryPath = "file://" + info.absolutePath();
            if (window) {
                auto windowIface = window->create(directoryPath);
                auto newWindow = dynamic_cast<QWidget *>(windowIface);
                newWindow->setAttribute(Qt::WA_DeleteOnClose);
                newWindow->show();
            }
        }
    });
    openLocationAction->setEnabled(selectUris.count() == 1);

    auto separator = new QAction;
    separator->setSeparator(true);

    auto separator1 = new QAction;
    separator1->setSeparator(true);

    auto separator2 = new QAction;
    separator2->setSeparator(true);

    auto separator3 = new QAction;
    separator3->setSeparator(true);


    auto separator4 = new QAction;
    separator4->setSeparator(true);


    auto separator5 = new QAction;
    separator5->setSeparator(true);

    auto separator6 = new QAction;
    separator6->setSeparator(true);

    auto separator7 = new QAction;
    separator7->setSeparator(true);

    QDir DocumentDir(g_get_user_special_dir(G_USER_DIRECTORY_DOCUMENTS));
    auto addFileToSpaceAction = new QAction(QIcon::fromTheme("list-add-symbolic"),tr("Add File"));
    connect(addFileToSpaceAction, &QAction::triggered, this, [=]{
        QFileDialog fileDialog;
        // fixme: 居中窗口
        //fileDialog.windowHandle()->setTransientParent(qApp->topLevelWindows().first());
        fileDialog.setWindowTitle(tr("Choose file to add into space"));
        fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
        fileDialog.setFileMode(QFileDialog::AnyFile);
        fileDialog.setDirectory(DocumentDir);
        if (fileDialog.exec()) {
            auto urls = fileDialog.selectedUrls();
            if (!urls.isEmpty()) {
                //userAddFilesToSpace
                QStringList filePaths;
                for (auto url : urls) {
                    filePaths.append(url.path());
                }
                qDebug() << "urls: " << urls << "filePaths:" << filePaths << spaceName;

                QList<QString> filesList = IDMDBusManager::getInstance()->getFilesInSpaceByName(spaceName);
                QSet<QString> allFiles(filesList.begin(), filesList.end());
                if (allFiles.contains(filePaths.first())) {
                    QMessageBox::warning(nullptr, nullptr, tr("File %1 is existed in space, please not add file repeatly.").arg(filePaths.first()));
                    return;
                }

                IDMDBusManager::getInstance()->userAddFilesToSpace(filePaths, spaceName);
            }
        }
    });

    // auto searchRelatedAction = new QAction(tr("Search Related Files"));
    // connect(searchRelatedAction, &QAction::triggered, this, [=]{

    // });

    bool isUserAddedFile = false;
    auto moveToSpaceAction = new QAction(QIcon::fromTheme("app-group-movein-symbolic"),tr("Move to space"));
    auto moveToSpaceMenu = new QMenu("");
    auto allSpacesNames = IDMDBusManager::getInstance()->getAllSpacesName();
    moveToSpaceMenu->setToolTipsVisible(true);
    for (auto currentSpaceName : allSpacesNames) {
        QFontMetrics fm(qApp->font());
        QString elidedText = fm.elidedText(currentSpaceName, Qt::ElideRight, 300);
        auto action = moveToSpaceMenu->addAction(elidedText);
        if(currentSpaceName != elidedText){
            QFont spaceNameFont = moveToSpaceMenu->font();
            auto realDisplayName = Peony::FileUtils::wrapTextForTooltip(currentSpaceName, QCursor::pos(), spaceNameFont);
            action->setToolTip(realDisplayName);
        }
        action->setIcon(QIcon::fromTheme("document-open-recent"));
        connect(action, &QAction::triggered, this, [=]{
            if (currentSpaceName != spaceName) {
                if (!selectUris.isEmpty()) {
                    for (auto uri : selectUris) {
                        QStringList filePaths;
                        QString tempUri = getRealUriFromUri(uri);
                        QString realPath = tempUri.remove("file://");
                        filePaths.append(realPath);
                        QList<QString> filesList = IDMDBusManager::getInstance()->getFilesInSpaceByName(currentSpaceName);
                        QSet<QString> allFiles(filesList.begin(), filesList.end());
                        if (allFiles.contains(realPath)) {
                            QMessageBox::warning(nullptr, nullptr, tr("File %1 is existed in space, please not add file repeatly.").arg(realPath));
                            continue;
                        }
                        bool ok = IDMDBusManager::getInstance()->removeFilesFromSpace(spaceName, filePaths);
                        if(ok){
                            IDMDBusManager::getInstance()->userAddFilesToSpace(filePaths,currentSpaceName);
                        }
                    }
                }

            }
        });
    }
    moveToSpaceAction->setMenu(moveToSpaceMenu);

    auto removeFromSpaceAction = new QAction(QIcon::fromTheme("user-trash-symbolic"),tr("Remove From Space"));
    connect(removeFromSpaceAction, &QAction::triggered, this, [=]{

        if (!qApp->property("skipAskRemoveFile").toBool()) {
            kdk::KMessageBox questionBox;
            questionBox.setIcon(kdk::KMessageBox::Icon::Question);
            auto cancelButton = new QPushButton(tr("Cancel"));
            auto okButton = new QPushButton(tr("OK"));
            questionBox.addButton(cancelButton, kdk::KMessageBox::RejectRole);
            questionBox.addButton(okButton, kdk::KMessageBox::AcceptRole);
            okButton->setProperty("isImportant", true);
            okButton->setDefault(true);
            okButton->setFocus();
            QString text = tr("<font color='gray'>After removal,when files in the space are aggregated and updated,these files will be no longer be displayed</font>");
            QString filetext = tr("<font color='gray'>After removal,when files in the space are aggregated and updated,this files will be no longer be displayed</font>");

            if(ismultiselected)
                questionBox.setText(tr("Do you want to remove selected %1 item(s)?<br>").arg(selectUris.size())+"<br>"+text);
            else
                questionBox.setText(tr("Do you want to remove this file?<br>")+"<br>"+"<br>"+filetext);
            auto checkbox = new QCheckBox;
            checkbox->setText(tr("Do not ask again"));
            questionBox.setCheckBox(checkbox);
            questionBox.exec();
            auto resultButton = questionBox.clickedButton();

            if (resultButton != okButton)
                return;
            if (checkbox->isChecked()) {
                qApp->setProperty("skipAskRemoveFile", true);
            }
        }

        if (!selectUris.isEmpty()) {
            QStringList paths;
            for (auto selectUri : selectUris) {
                QString tempUri = getRealUriFromUri(selectUri);
                QString realPath = tempUri.remove("file://");
                paths.append(realPath);
            }
            IDMDBusManager::getInstance()->removeFilesFromSpace(spaceName, paths);
        }
    });
    removeFromSpaceAction->setVisible(isFileSelected);




    auto pluginManager = Peony::PluginManager::getInstance();
    QString pluginName = "libpeony-menu-plugin-kylin-aiassistant.so";
    auto plugininterface = pluginManager->getPluginByFileName(pluginName);
    bool isAiassistantAavilable = false;
    QProcess process;
    QString packageName = "kylin-aiassistant";
    process.start("dpkg", QStringList() << "-l" << packageName);
    if (!process.waitForFinished()) {
         isAiassistantAavilable = false;
      }
      QString output = process.readAllStandardOutput();
      if (output.contains(packageName)) {
          isAiassistantAavilable =  true;
      } else {
          isAiassistantAavilable =  false;
      }
    QList<QAction*> assistantActions;
    bool showAiassistant = false;
    if (plugininterface && isAiassistantAavilable) {
        auto plugin = static_cast<Peony::MenuPluginInterface *>(plugininterface);
        QStringList uris;
        for(auto selectUri : selectUris)
        {
            QString realUri = getRealUriFromUri(selectUri);
            uris << realUri;
        }
        assistantActions = plugin->menuActions(Peony::MenuPluginInterface::DirectoryView, nullptr, uris);
        showAiassistant = true;
        if(!assistantActions.isEmpty())
        {
            assistantActions.first()->setVisible(isFileSelected);
            connect(assistantActions.first(),&QAction::triggered,this,[=]{
                QProcess::startDetached("kylin-aiassistant",{"-l"});
            });
        }
    }

    auto copyAction = new QAction(QIcon::fromTheme("edit-copy-symbolic"),tr("Copy"));
    connect(copyAction, &QAction::triggered, this, [=]{
        QStringList uris;
        if(ismultiselected)
        {
            for(auto path : selectUris)
            {
                QString realPath = getRealUriFromUri(path);
                uris << realPath;
            }
        }
        else
        {
            if (!realUri.isEmpty()) {
                uris << realUri;
            }
        }
        ClipboardUtils::getInstance()->setClipboardFiles(uris, false, false);
    });
    copyAction->setVisible(isFileSelected);

    auto saveAsAction = new QAction(QIcon::fromTheme("document-save-as-symbolic"),tr("Save As"));
    connect(saveAsAction, &QAction::triggered, this, [=]{
        if (realPath.isEmpty())
            return;
        QString filesuffix = QFileInfo(realPath).suffix();
        QString filter = QString(tr("file")+" (*.%1);;"+tr("All File")+" (*)").arg(filesuffix);
        QFileDialog dialog(nullptr);
        dialog.setAcceptMode(QFileDialog::AcceptSave);
        dialog.setOption(QFileDialog::DontConfirmOverwrite);
        dialog.setNameFilter(filter);
        if(dialog.exec() == QDialog::Accepted)
        {
            QString filePath = dialog.selectedFiles().first();
            QFileInfo fileinfo(filePath);
            QString DirectoryPath = fileinfo.absolutePath();
            bool iswriteble = QFileInfo(DirectoryPath).isWritable();
            if(iswriteble)
            {
                if(QFile::exists(filePath) && realPath != filePath)
                    QFile::remove(filePath);
                QFile::copy(realPath, filePath);
            }
            else
            {
                kdk::KMessageBox questionBox;
                questionBox.setIcon(kdk::KMessageBox::Icon::Warning);
                auto cancelButton = new QPushButton(tr("Cancel"));
                auto okButton = new QPushButton(tr("OK"));
                questionBox.addButton(cancelButton, kdk::KMessageBox::RejectRole);
                questionBox.addButton(okButton, kdk::KMessageBox::AcceptRole);
                okButton->setProperty("isImportant", true);
                okButton->setDefault(true);
                okButton->setFocus();
                questionBox.setText(tr("This location has no write permission.please change the storage location"));
                questionBox.exec();
            }
        }
    });
    saveAsAction->setVisible(isFileSelected);
    saveAsAction->setEnabled(!ismultiselected);


    auto pluginIds = MenuPluginManager::getInstance()->getPluginIds();
    //sort plugiins by name, so the menu option orders is relatively fixed
    std::sort(pluginIds.begin(), pluginIds.end());

//    auto labelAction = new QAction(tr("Label"));
//    if (labelAction->isVisible()) {
//        auto labelActionMenu = new QMenu;
//        auto labelModel = FileLabelModel::getGlobalModel();
//        auto labels = labelModel->getAllFileLabelItems();
//        Peony::FileInfoJob j(uri);
//        j.querySync();
//        auto fileLabelIds = labelModel->getFileLabelIds(uri);
//        for (auto label : labels) {
//            auto labelAction = labelActionMenu->addAction(label->name());
//            labelAction->setCheckable(true);
//            if (fileLabelIds.contains(label->id())) {
//                labelAction->setChecked(true);
//            }
//            connect(labelAction, &QAction::triggered, this, [=](bool checked){
//                Peony::FileInfoJob j(uri);
//                j.querySync();
//                if (checked) {
//                    labelModel->addLabelToFile(uri, label->id());
//                } else {
//                    labelModel->removeFileLabel(uri, label->id());
//                }
//            });
//        }
//        labelAction->setMenu(labelActionMenu);
//    }

    auto propertiesAction = new QAction(QIcon::fromTheme("properties"),tr("Properties"));
    connect(propertiesAction, &QAction::triggered, this, [=](){
        auto w = PropertiesWindowFactoryPluginManager::getInstance()->create(QStringList()<<selectUris);
        PropertiesWindowFactoryPluginManager::getInstance()->show();
    });

    switch (type) {
    case MenuType::SideBar: {
        l.append(renamAction);
        l.append(deleteAction);
        l.append(separator6);
        l.append(editCondition);
        l.append(refreshAction);       
        break;
    }
    case MenuType::DirectoryView: {
        if ((isRootDirectory && !selectUris.isEmpty() && !ismultiselected) ||
                (!isRootDirectory && selectUris.isEmpty())) {
            l.append(addFileToSpaceAction);
            l.append(separator6);
            l.append(renamAction);
            l.append(deleteAction);
            l.append(separator7);
            l.append(editCondition);
        } else if (!isRootDirectory && !selectUris.isEmpty()) {
            l.append(openAction);
            l.append(openWithAction);
            l.append(openLocationAction);
            l.append(separator);
            // l.append(searchRelatedAction);          
            l.append(copyAction);
            l.append(saveAsAction);
            l.append(separator2);
            for (auto id : pluginIds) {
                if(id == "Peony File Labels Menu Extension"){
                    auto plugin = MenuPluginManager::getInstance()->getPlugin(id);
                    auto actions = plugin->menuActions(MenuPluginInterface::DirectoryView, directoryUri, m_realselectUris);
                    for (auto action : actions) {
                        l.append(action);
                    }
                    l.append(separator5);
                }
             }
            l.append(separator3);
            l.append(moveToSpaceAction);
            l.append(removeFromSpaceAction);
            l.append(separator4);
            if(showAiassistant){
                l.append(assistantActions);
            }
            l.append(separator1);
            l.append(propertiesAction);
        }
        break;
    }
    default:
        break;
    }

    return l;
}

IDMActionsManager::IDMActionsManager(QObject *parent) : QObject(parent)
{

}

const QStringList IDMActionsManager::getSelections(DirectoryViewWidget *view, SideBarAbstractItem *item)
{
    if (view)
         return view->getSelections();
    if (item)
        return QStringList() << item->uri();
    return {};
}

const QString IDMActionsManager::getDirectoryUri(DirectoryViewWidget *view, SideBarAbstractItem *item)
{
    if (view)
        return view->getDirectoryUri();
    if (item)
        return item->uri();
    return {};
}

const QString IDMActionsManager::getRealUriFromUri(const QString &uri)
{
    QString realUri = "";
    if (!uri.isEmpty()) {
        QString tempUri = uri.split('/', Qt::SkipEmptyParts).last();
        realUri = "file://" + QUrl::fromPercentEncoding(tempUri.toLatin1());
    }
    return realUri;
}

QString truncateToByteLimit(const QString &text, int maxBytes) {
    QByteArray utf8Data = text.toUtf8();
    if (utf8Data.size() <= maxBytes) {
       return text; // 如果字节数未超限，返回原文本
    }
    // 从最大字节数位置向前查找安全的截断点
    int truncatePos = maxBytes;
    // 向前查找，直到找到一个安全的截断点（不是多字节字符的中间字节）
    while (truncatePos > 0 && (utf8Data.at(truncatePos) & 0xC0) == 0x80) {
       truncatePos--;
    }
    // 如果truncatePos为0，说明第一个字节就是多字节字符的一部分，返回空字符串
    if (truncatePos == 0) {
       return "";
    }
    // 截取安全的字节部分并转换回QString
    QByteArray safeData = utf8Data.left(truncatePos);
    return QString::fromUtf8(safeData);
}
