#include "aiwriterdialog.h"
#include "aiquickdialog.h"
#include "private/transbutton.h"
#include "private/filltextbutton.h"
#include "private/hinttextedit.h"
#include "private/circlebutton.h"
#include "private/writertextedit.h"
#include "../wordwizard.h"
#include "app/serverwrapper.h"
#include <dbus/fcitxinputserver.h>
#include <gui/gutils.h>
#include <utils/dconfigmanager.h>
#include "private/welcomedialog.h"
#include "utils/esystemcontext.h"
#include "global_define.h"
#include "app/application.h"
#include <report/writerpoint.h>
#include <report/writerfunctionpoint.h>
#include <report/eventlogutil.h>

#include "global_define.h"
#include "global_key_define.h"
#include "conversation/conversationmanager.h"
#include "session/sessionmanager.h"
#include "conversation/conversationrecord.h"
#include "assistant/assistantmanager.h"
#include "database/appdatabase.h"
#include "database/usedmodeltable.h"
#include "audiocontroler.h"
#include "services/accountservice/freeaccountservice.h"
#include "gui/window/windowmanager.h"
#include "model/modelvendor.h"

#include <DFontSizeManager>
#include <DGuiApplicationHelper>
#include <DDialog>
#include <DPaletteHelper>

#include "ddedockobject.h"

#include <QScreen>
#include <QTimer>
#include <QClipboard>
#include <QTextBlockFormat>
#include <QScrollBar>

#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(logWordWizard)

#ifdef COMPILE_ON_V25
#include <ddeshellwayland.h>
#endif

DWIDGET_USE_NAMESPACE
DGUI_USE_NAMESPACE
DCORE_USE_NAMESPACE
using namespace uos_ai;

static constexpr char WARNING_ICON[] = ":/assets/images/warning.svg";

AiWriterDialog &AiWriterDialog::instance() {
    static AiWriterDialog instance;
    return instance;
}

AiWriterDialog::AiWriterDialog(DWidget *parent) : DAbstractDialog(parent)
{
    // 避免Dialog关闭后，带起主窗口（~Qt::Dialog）
    this->setWindowFlags((this->windowFlags() | Qt::WindowStaysOnTopHint) & ~Qt::Dialog);
    // Initialize chat session
    chatSession = uos_ai::SessionManager::instance("uos-ai-wordwizard-writer");
    connect(chatSession.data(), &uos_ai::SessionManager::sessionEvent, this, &AiWriterDialog::onEvent, Qt::QueuedConnection);
    initUi();
    initConnect();

    // 第一次使用填充功能，提醒用户重启输入法服务
    m_isFirstFill = DConfigManager::instance()->value(AIQUICK_GROUP, AIQUICK_ISFIRSTFILL, true).toBool();

}

AiWriterDialog::~AiWriterDialog() {
}

void AiWriterDialog::initUi() {
    this->setFixedWidth(650);
    this->setMaximumHeight(600);
#ifdef COMPILE_ON_V25
    this->move(100, 100);
#endif

    //标题栏
    DTitlebar *titleBar = new DTitlebar(this);
    titleBar->setMenuVisible(false);
    titleBar->setBackgroundTransparent(true);

    m_logoBt = new TransButton(this);
    m_logoBt->setIcon(QIcon::fromTheme("uos-ai-assistant"));
    m_logoBt->setIconSize(QSize(25, 25));
    dynamic_cast<TransButton *>(m_logoBt)->setIconNoPressColor();

    DLabel *titleLable = new DLabel(tr("AI Writer"), this);
    DFontSizeManager::instance()->bind(titleLable, DFontSizeManager::T7, QFont::Normal);

    QHBoxLayout *allTitleLayout = new QHBoxLayout();
    allTitleLayout->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
    allTitleLayout->addSpacing(9);
    allTitleLayout->addWidget(m_logoBt);
    allTitleLayout->addWidget(titleLable);
    allTitleLayout->addWidget(titleBar);

    m_queryEdit = new HintTextEdit(this);
    m_queryEdit->setPlaceholderText(tr("Enter what you want to create (press Enter to generate/Esc to exit)"));
    m_queryEdit->setFrameShape(QFrame::NoFrame);
    m_queryEdit->setLineWrapMode(QTextEdit::WidgetWidth);
    m_queryEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    DPalette queryPalette = m_queryEdit->palette();
    queryPalette.setColor(DPalette::Base, QColor(255, 255, 255, 0));
    m_queryEdit->setPalette(queryPalette);
    m_queryEditMin = static_cast<int>(m_queryEdit->document()->size().height()) + 4;
    m_queryEdit->setFixedHeight(m_queryEditMin);
    m_queryEditMax = this->maximumHeight();
    m_queryEdit->installEventFilter(this);
    DFontSizeManager::instance()->bind(m_queryEdit, DFontSizeManager::T7, QFont::Normal);

    m_sendBt = new CircleButton(this);
    m_sendBt->setIcon(QIcon::fromTheme("uos-ai-assistant_aisend"));
    m_sendBt->setIconSize(QSize(14, 14));
    m_sendBt->setFixedSize(QSize(24, 24));

    m_querySpace = new DWidget(this);
    // 实际大小应该是15-4=11，实测后选择用8
    m_querySpace->setFixedWidth(8);

    QHBoxLayout *queryLayout = new QHBoxLayout();
    queryLayout->setContentsMargins(10, 0, 2, 0);
    queryLayout->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
    queryLayout->addWidget(m_queryEdit);
    queryLayout->addWidget(m_sendBt);
    queryLayout->addWidget(m_querySpace);

    m_sendBt1 = new CircleButton(this);
    m_sendBt1->setIcon(QIcon::fromTheme("uos-ai-assistant_aisend"));
    m_sendBt1->setIconSize(QSize(14, 14));
    m_sendBt1->setFixedSize(QSize(24, 24));
    m_sendBt1->setVisible(false);

    QHBoxLayout *sendLayout = new QHBoxLayout();
    sendLayout->setContentsMargins(15, 0, 15, 0);
    sendLayout->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
    sendLayout->addStretch();
    sendLayout->addWidget(m_sendBt1);

    m_sendVSpace = new DWidget(this);
    m_sendVSpace->setFixedHeight(0);
    m_sendVSpace->setVisible(false);

    m_querySep = new DWidget(this);
    m_querySep->setFixedSize(2, 1);
    DPalette querySepPalette = m_querySep->palette();
    querySepPalette.setColor(DPalette::Window, QColor(0, 0, 0, 76));
    m_querySep->setPalette(querySepPalette);
    m_querySep->setAutoFillBackground(true);
    m_querySep->setVisible(false);

    m_queryTextEdit = new QueryTextEdit(this);
    m_queryTextEdit->setContentsMargins(0, 0, 0, 0);
    m_queryTextEdit->setReadOnly(true);
    m_queryTextEdit->setFrameShape(QFrame::NoFrame);
    m_queryTextEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    m_queryTextEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    m_queryTextEdit->setWordWrapMode(QTextOption::WrapMode::WrapAnywhere);
    DPalette queryTextPalette = m_queryTextEdit->palette();
    queryTextPalette.setColor(DPalette::Base, QColor(255, 255, 255, 0));
    queryTextPalette.setColor(DPalette::Text, QColor(0, 0, 0, int(255 * 0.5)));
    m_queryTextEdit->setPalette(queryTextPalette);
    m_queryTextEdit->setFixedHeight(1);
    m_queryTextEdit->setFixedWidth(this->width() - 25 - m_querySep->width() - 4);
    DFontSizeManager::instance()->bind(m_queryTextEdit, DFontSizeManager::T7, QFont::Normal);
    m_queryTextEdit->setVisible(false);

    QHBoxLayout *queryTextLayout = new QHBoxLayout();
    queryTextLayout->setContentsMargins(15, 0, 10, 0);
    queryTextLayout->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
    queryTextLayout->addWidget(m_querySep);
    queryTextLayout->addWidget(m_queryTextEdit);

    m_queryHSep = new DWidget(this);
    m_queryHSep->setFixedSize(this->width() - 30, 1);
    DPalette queryHSepPalette = m_queryHSep->palette();
    queryHSepPalette.setColor(DPalette::Window, QColor(0, 0, 0, 25));
    m_queryHSep->setPalette(queryHSepPalette);
    m_queryHSep->setAutoFillBackground(true);
    m_queryHSep->setVisible(false);

    m_queryHSepVSpace = new DWidget(this);
    m_queryHSepVSpace->setFixedHeight(5);
    m_queryHSepVSpace->setVisible(false);

    QVBoxLayout *queryHSepLayout = new QVBoxLayout();
    queryHSepLayout->setContentsMargins(15, 0, 15, 0);
    queryHSepLayout->addWidget(m_queryHSep);
    queryHSepLayout->addWidget(m_queryHSepVSpace);

    int articleBtWidth = 82;
    m_articleBt = new FillTextButton(tr("Article"), this);
    m_articleBt->setFixedWidth(articleBtWidth);
    m_articleBt->setIcon(QIcon::fromTheme("uos-ai-assistant_article"));
    m_articleBt->setIconSize(QSize(22, 22));
    m_outlineBt = new FillTextButton(tr("Outline"), this);
    m_outlineBt->setFixedWidth(articleBtWidth);
    m_outlineBt->setIcon(QIcon::fromTheme("uos-ai-assistant_outline"));
    m_outlineBt->setIconSize(QSize(22, 22));
    m_notifyBt = new FillTextButton(tr("Notification"), this);
    m_notifyBt->setFixedWidth(articleBtWidth);
    m_notifyBt->setIcon(QIcon::fromTheme("uos-ai-assistant_notify"));
    m_notifyBt->setIconSize(QSize(22, 22));
    m_reportBt = new FillTextButton(tr("Research Report"), this);
    m_reportBt->setFixedWidth(articleBtWidth);
    m_reportBt->setIcon(QIcon::fromTheme("uos-ai-assistant_report"));
    m_reportBt->setIconSize(QSize(22, 22));
    m_speechBt = new FillTextButton(tr("Speeches"), this);
    m_speechBt->setFixedWidth(articleBtWidth);
    m_speechBt->setIcon(QIcon::fromTheme("uos-ai-assistant_speech"));
    m_speechBt->setIconSize(QSize(22, 22));
    m_summaryBt = new FillTextButton(tr("Work Summary"), this);
    m_summaryBt->setFixedWidth(articleBtWidth);
    m_summaryBt->setIcon(QIcon::fromTheme("uos-ai-assistant_worksummary"));
    m_summaryBt->setIconSize(QSize(22, 22));
    m_publicBt = new FillTextButton(tr("Tweets"), this);
    m_publicBt->setFixedWidth(articleBtWidth);
    m_publicBt->setIcon(QIcon::fromTheme("uos-ai-assistant_official_accounts"));
    m_publicBt->setIconSize(QSize(22, 22));
    DFontSizeManager::instance()->bind(m_articleBt, DFontSizeManager::T10, QFont::Normal);
    DFontSizeManager::instance()->bind(m_outlineBt, DFontSizeManager::T10, QFont::Normal);
    DFontSizeManager::instance()->bind(m_notifyBt, DFontSizeManager::T10, QFont::Normal);
    DFontSizeManager::instance()->bind(m_reportBt, DFontSizeManager::T10, QFont::Normal);
    DFontSizeManager::instance()->bind(m_speechBt, DFontSizeManager::T10, QFont::Normal);
    DFontSizeManager::instance()->bind(m_summaryBt, DFontSizeManager::T10, QFont::Normal);
    DFontSizeManager::instance()->bind(m_publicBt, DFontSizeManager::T10, QFont::Normal);

    QHBoxLayout *articalLayout = new QHBoxLayout();
    articalLayout->setContentsMargins(15, 0, 15, 0);
    articalLayout->setAlignment(Qt::AlignCenter);
    articalLayout->addWidget(m_articleBt);
    articalLayout->addStretch();
    articalLayout->addWidget(m_outlineBt);
    articalLayout->addStretch();
    articalLayout->addWidget(m_notifyBt);
    articalLayout->addStretch();
    articalLayout->addWidget(m_reportBt);
    articalLayout->addStretch();
    articalLayout->addWidget(m_speechBt);
    articalLayout->addStretch();
    articalLayout->addWidget(m_summaryBt);
    articalLayout->addStretch();
    articalLayout->addWidget(m_publicBt);

    m_articleVSpace = new DWidget(this);
    m_articleVSpace->setFixedHeight(0);
    m_articleVSpace1 = new DWidget(this);
    m_articleVSpace1->setFixedHeight(5);

    m_replyEdit = new WriterTextEdit(this);
    m_replyEdit->setFrameShape(QFrame::NoFrame);
    m_replyEdit->document()->setDocumentMargin(10);
    m_replyEdit->setReadOnly(true);
    //m_replyEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    DPalette replyPalette = m_replyEdit->palette();
    replyPalette.setColor(DPalette::Base, QColor(255, 255, 255, 0));
    m_replyEdit->setPalette(replyPalette);
    m_replyEdit->installEventFilter(this);
    m_replyEdit->setVisible(false);
    DFontSizeManager::instance()->bind(m_replyEdit, DFontSizeManager::T7, QFont::Normal);

    m_highlighter = new MarkdownHighlighter(m_replyEdit->document());

    m_errorInfoLabel = new DLabel(this);
    m_errorInfoLabel->setWordWrap(true);
    m_errorInfoLabel->setFixedWidth(this->maximumWidth() - 30);
    m_errorInfoLabel->setContextMenuPolicy(Qt::NoContextMenu);
    m_errorInfoLabel->setVisible(false);

    m_cancelBt = new DPushButton(tr("Cancel"), this);
    m_cancelBt->setFixedHeight(20);
    m_cancelBt->setFixedWidth(m_cancelBt->sizeHint().width());
    DPalette cancelPalette = m_cancelBt->palette();
    cancelPalette.setColor(DPalette::ButtonText, QColor(0, 0, 0, 178));
    m_cancelBt->setPalette(cancelPalette);
    DFontSizeManager::instance()->bind(m_cancelBt, DFontSizeManager::T8, QFont::Normal);
    m_cancelBt->setVisible(false);

    m_inProgressMovie = new QMovie(":assets/images/light/light-loading.gif");
    m_inProgressLabel = new DLabel(this);
    m_inProgressLabel->setMovie(m_inProgressMovie);
    // original size 800x83
    int inProgressWidth = this->width() - 30;
    float inProgressRatio = 800.0F / inProgressWidth;
    int inProgressHeight = 38;//static_cast<int>(83.0F / inProgressRatio);
    m_inProgressLabel->setFixedSize(inProgressWidth, inProgressHeight);
    m_inProgressLabel->setScaledContents(true);
    m_inProgressLabel->setVisible(false);

    m_progressVSpace = new DWidget(this);
    m_progressVSpace->setFixedHeight(1);
    m_progressVSpace->setVisible(false);

    QVBoxLayout *replyLayout = new QVBoxLayout();
    replyLayout->setContentsMargins(5, 0, 2, 0);
    replyLayout->setAlignment(Qt::AlignLeft);
    replyLayout->addWidget(m_replyEdit);

    QVBoxLayout *replyLayout1 = new QVBoxLayout();
    replyLayout1->setContentsMargins(15, 0, 15, 0);
    replyLayout1->setAlignment(Qt::AlignLeft);
    replyLayout1->addWidget(m_inProgressLabel);
    replyLayout1->addWidget(m_progressVSpace);
    replyLayout1->addWidget(m_errorInfoLabel);
    replyLayout1->addWidget(m_cancelBt);

    // replace
    m_replaceBt = new TransButton(tr("Paste to Text"), this);
    m_replaceBt->setIcon(QIcon::fromTheme("uos-ai-assistant_replace"));
    m_replaceBt->setIconSize(QSize(16, 16));
    DPalette replacePalette = m_replaceBt->palette();
    replacePalette.setColor(DPalette::ButtonText, QColor(0, 0, 0, 178));
    m_replaceBt->setPalette(replacePalette);
    DFontSizeManager::instance()->bind(m_replaceBt, DFontSizeManager::T8, QFont::Normal);
    m_replaceBt->setVisible(false);

    m_replySep = new DWidget(this);
    m_replySep->setFixedSize(1, 15);
    DPalette replyPalette1 = m_replySep->palette();
    replyPalette1.setColor(DPalette::Window, QColor(0, 0, 0, 25));
    m_replySep->setPalette(replyPalette1);
    m_replySep->setAutoFillBackground(true);
    m_replySep->setVisible(false);

    // again
    m_againBt = new TransButton(tr("Regenerate"), this);
    m_againBt->setIcon(QIcon::fromTheme("uos-ai-assistant_again"));
    m_againBt->setIconSize(QSize(16, 16));
    m_againBt->setPalette(replacePalette);
    DFontSizeManager::instance()->bind(m_againBt, DFontSizeManager::T8, QFont::Normal);
    m_againBt->setVisible(false);

    // copy
    m_copyBt = new TransButton(tr("Copy"), this);
    m_copyBt->setIcon(QIcon::fromTheme("uos-ai-assistant_copy"));
    m_copyBt->setIconSize(QSize(16, 16));
    m_copyBt->setPalette(replacePalette);
    DFontSizeManager::instance()->bind(m_copyBt, DFontSizeManager::T8, QFont::Normal);
    m_copyBt->setVisible(false);

    QHBoxLayout *replyFunLayout = new QHBoxLayout();
    replyFunLayout->setContentsMargins(10, 0, 10, 0);
    replyFunLayout->setAlignment(Qt::AlignLeft);
    replyFunLayout->addWidget(m_replaceBt);
    //replyFunLayout->addSpacing(5);
    replyFunLayout->addWidget(m_replySep);
    //replyFunLayout->addSpacing(5);
    replyFunLayout->addWidget(m_againBt);
    replyFunLayout->addWidget(m_copyBt);

    m_attentionLabel = new DLabel(this);
    m_attentionLabel->setText(tr("The content generated by AI is for reference only, please pay attention to the accuracy of the information."));
    QFont attentionFont = m_attentionLabel->font();
    attentionFont.setPixelSize(10);
    attentionFont.setWeight(QFont::Normal);
    m_attentionLabel->setFont(attentionFont);
    DPalette attentionPalette = m_attentionLabel->palette();
    attentionPalette.setColor(DPalette::WindowText, QColor(0, 0, 0, 102));
    m_attentionLabel->setPalette(attentionPalette);
    m_attentionLabel->setVisible(false);

    m_modelBt = new TransButton(this);
    m_modelBt->setIcon(QIcon::fromTheme("uos-ai-assistant_modelbt"));
    m_modelBt->setIconSize(QSize(16, 16));
    dynamic_cast<TransButton *>(m_modelBt)->setIconNoPressColor(true);
    m_modelBt->installEventFilter(this);
    m_attentionLabel->setFixedWidth(this->width() - 25 - m_modelBt->sizeHint().width() - 4);
    m_modelBt->setVisible(false);

    QHBoxLayout *attentionLayout = new QHBoxLayout();
    attentionLayout->setContentsMargins(15, 0, 10, 0);
    attentionLayout->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
    attentionLayout->addWidget(m_attentionLabel);
    attentionLayout->addWidget(m_modelBt);

    m_vSpace = new DWidget(this);
    m_vSpace->setFixedHeight(3);
    m_vSpace->setVisible(false);

    QVBoxLayout *allLayout = new QVBoxLayout(this);
    allLayout->setContentsMargins(0, 0, 0, 0);
    allLayout->setAlignment(Qt::AlignTop);
    allLayout->addLayout(allTitleLayout);
    allLayout->addLayout(queryLayout);
    allLayout->addLayout(sendLayout);
    allLayout->addWidget(m_sendVSpace);
    allLayout->addLayout(queryTextLayout);
    allLayout->addLayout(queryHSepLayout);
    allLayout->addWidget(m_articleVSpace1);
    allLayout->addLayout(articalLayout);
    allLayout->addWidget(m_articleVSpace);
    allLayout->addLayout(replyLayout);
    allLayout->addLayout(replyLayout1);
    allLayout->addWidget(m_vSpace);
    allLayout->addLayout(replyFunLayout);
    allLayout->addSpacing(3);
    allLayout->addLayout(attentionLayout);
    allLayout->addSpacing(5);
    allLayout->addStretch();
    this->setLayout(allLayout);

    m_isNeedResetUi = true;

#ifdef COMPILE_ON_V25
    if (ESystemContext::isTreeland()) {
        this->createWinId();
        DDEShellWayland::get(windowHandle())->setRole(QtWayland::treeland_dde_shell_surface_v1::role_overlay);
    }
#endif
}

void AiWriterDialog::resetUi() {
    m_curErr = AiQuickDialog::ERROR_TYPE_NONE;
    m_queryEdit->setFirstQueryFlag(true);
    m_query.clear();
    m_queryEdit->setText(m_query);
    m_queryEdit->setPlaceholderText(tr("Enter what you want to create (press Enter to generate/Esc to exit)"));
    m_queryEditMax = this->maximumHeight();
    m_queryEdit->setLineWrapMode(QTextEdit::WidgetWidth);

    m_querySep->setVisible(false);
    m_queryTextEdit->setVisible(false);
    m_queryHSep->setVisible(false);
    m_queryHSepVSpace->setVisible(false);

    m_reply.clear();
    m_replyBak.clear();
    m_replyCacheMutex.lock();
    m_replyCache.clear();
    m_replyCacheIdx = 0;
    m_replyCacheMutex.unlock();

    this->setArticleBtPartVisible(true);
    m_curBt = nullptr;
    m_inProgressLabel->setVisible(false);
    m_inProgressMovie->stop();
    m_progressVSpace->setVisible(false);
    m_errorInfoLabel->setVisible(false);
    m_cancelBt->setVisible(false);

    m_replyEdit->clear();
    m_replyEdit->setFixedHeight(0);
    m_replyEdit->setVisible(false);
    this->setReplyPartVisible(false);

    this->asyncAdjustSize();
}

void AiWriterDialog::initConnect() {
    connect(m_logoBt, &DPushButton::clicked, this, &AiWriterDialog::onBtClicked);
    connect(m_sendBt, &DPushButton::clicked, this, &AiWriterDialog::onBtClicked);
    connect(m_sendBt1, &DPushButton::clicked, this, &AiWriterDialog::onBtClicked);
    connect(m_articleBt, &DPushButton::clicked, this, &AiWriterDialog::onBtClicked);
    connect(m_outlineBt, &DPushButton::clicked, this, &AiWriterDialog::onBtClicked);
    connect(m_notifyBt, &DPushButton::clicked, this, &AiWriterDialog::onBtClicked);
    connect(m_reportBt, &DPushButton::clicked, this, &AiWriterDialog::onBtClicked);
    connect(m_speechBt, &DPushButton::clicked, this, &AiWriterDialog::onBtClicked);
    connect(m_summaryBt, &DPushButton::clicked, this, &AiWriterDialog::onBtClicked);
    connect(m_publicBt, &DPushButton::clicked, this, &AiWriterDialog::onBtClicked);
    connect(m_cancelBt, &DPushButton::clicked, this, &AiWriterDialog::onBtClicked);
    connect(m_replaceBt, &DPushButton::clicked, this, &AiWriterDialog::onBtClicked);
    connect(m_againBt, &DPushButton::clicked, this, &AiWriterDialog::onBtClicked);
    connect(m_copyBt, &DPushButton::clicked, this, &AiWriterDialog::onBtClicked);

    connect(m_queryEdit, &DTextEdit::textChanged, this, &AiWriterDialog::onTextChanged);

    connect(m_errorInfoLabel, &DLabel::linkActivated, this, &AiWriterDialog::onOpenConfigDialog);

    connect(m_replyEdit->verticalScrollBar(), &QScrollBar::valueChanged, this, [&] (int value) {
        if (!m_isReplyEditCursorEnd && value == m_replyEdit->verticalScrollBar()->maximum()) {
            m_isReplyEditCursorEnd = true;
        } else if (m_isReplyEditCursorEnd && m_replyEdit->verticalScrollBar()->maximum() - value > 50) {
            m_isReplyEditCursorEnd = false;
        }
    });

    // theme
    this->onUpdateSystemTheme(DGuiApplicationHelper::instance()->themeType());
    connect(DGuiApplicationHelper::instance(), &DGuiApplicationHelper::themeTypeChanged, this, &AiWriterDialog::onUpdateSystemTheme);
    connect(DGuiApplicationHelper::instance(), &DGuiApplicationHelper::fontChanged, this, &AiWriterDialog::onFontChanged);
}

void AiWriterDialog::onUpdateSystemTheme(const DGuiApplicationHelper::ColorType &themeType)
{
    this->onQueryTextChanged(true);
    this->update();

    if (themeType == DGuiApplicationHelper::LightType) {
        DPalette queryPalette = m_queryEdit->palette();
        queryPalette.setColor(DPalette::Base, QColor(255, 255, 255, 0));
        m_queryEdit->setPalette(queryPalette);

        DPalette replyPalette = m_replyEdit->palette();
        replyPalette.setColor(DPalette::Base, QColor(255, 255, 255, 0));
        m_replyEdit->setPalette(replyPalette);

        DPalette querySepPalette = m_querySep->palette();
        querySepPalette.setColor(DPalette::Window, QColor(0, 0, 0, 76));
        m_querySep->setPalette(querySepPalette);

        DPalette queryHSepPalette = m_queryHSep->palette();
        queryHSepPalette.setColor(DPalette::Window, QColor(0, 0, 0, 25));
        m_queryHSep->setPalette(queryHSepPalette);

        DPalette queryTextPalette = m_queryTextEdit->palette();
        queryTextPalette.setColor(DPalette::Base, QColor(255, 255, 255, 0));
        queryTextPalette.setColor(DPalette::Text, QColor(0, 0, 0, int(255 * 0.5)));
        m_queryTextEdit->setPalette(queryTextPalette);

        DPalette copyPalette = m_copyBt->palette();
        copyPalette.setColor(DPalette::ButtonText, QColor(0, 0, 0, 153));
        if (!this->isActiveWindow()) {
            DPalette parentPb = DGuiApplicationHelper::instance()->applicationPalette();
            copyPalette.setColor(DPalette::ButtonText, parentPb.color(DPalette::Inactive, DPalette::ButtonText));
        }
        m_copyBt->setPalette(copyPalette);
        m_cancelBt->setPalette(copyPalette);
        m_replaceBt->setPalette(copyPalette);
        m_againBt->setPalette(copyPalette);

        DPalette replyPalette1 = m_replySep->palette();
        replyPalette1.setColor(DPalette::Window, QColor(0, 0, 0, 25));
        m_replySep->setPalette(replyPalette1);

        DPalette attentionPalette = m_attentionLabel->palette();
        attentionPalette.setColor(DPalette::WindowText, QColor(0, 0, 0, 102));
        if (!this->isActiveWindow()) {
            DPalette parentPb = DGuiApplicationHelper::instance()->applicationPalette();
            attentionPalette.setColor(DPalette::WindowText, parentPb.color(DPalette::Inactive, DPalette::WindowText));
        }
        m_attentionLabel->setPalette(attentionPalette);

        m_inProgressMovie->setFileName(":assets/images/light/light-loading.gif");
        if (m_inProgressMovie->state() == QMovie::Running) {
            m_inProgressMovie->stop();
            m_inProgressMovie->start();
        }
    } else {
        DPalette queryPalette = m_queryEdit->palette();
        queryPalette.setColor(DPalette::Base, QColor(0, 0, 0, 0));
        m_queryEdit->setPalette(queryPalette);

        DPalette replyPalette = m_replyEdit->palette();
        replyPalette.setColor(DPalette::Base, QColor(0, 0, 0, 0));
        m_replyEdit->setPalette(replyPalette);

        DPalette querySepPalette = m_querySep->palette();
        querySepPalette.setColor(DPalette::Window, QColor(255, 255, 255, 76));
        m_querySep->setPalette(querySepPalette);

        DPalette queryHSepPalette = m_queryHSep->palette();
        queryHSepPalette.setColor(DPalette::Window, QColor(255, 255, 255, 25));
        m_queryHSep->setPalette(queryHSepPalette);

        DPalette queryTextPalette = m_queryTextEdit->palette();
        queryTextPalette.setColor(DPalette::Base, QColor(255, 255, 255, 0));
        queryTextPalette.setColor(DPalette::Text, QColor(255, 255, 255, int(255 * 0.5)));
        m_queryTextEdit->setPalette(queryTextPalette);

        DPalette copyPalette = m_copyBt->palette();
        copyPalette.setColor(DPalette::ButtonText, QColor(255, 255, 255, 153));
        if (!this->isActiveWindow()) {
            DPalette parentPb = DGuiApplicationHelper::instance()->applicationPalette();
            copyPalette.setColor(DPalette::ButtonText, parentPb.color(DPalette::Inactive, DPalette::ButtonText));
        }
        m_copyBt->setPalette(copyPalette);
        m_cancelBt->setPalette(copyPalette);
        m_replaceBt->setPalette(copyPalette);
        m_againBt->setPalette(copyPalette);

        DPalette replyPalette1 = m_replySep->palette();
        replyPalette1.setColor(DPalette::Window, QColor(255, 255, 255, 25));
        m_replySep->setPalette(replyPalette1);

        DPalette attentionPalette = m_attentionLabel->palette();
        attentionPalette.setColor(DPalette::WindowText, QColor(255, 255, 255, 102));
        if (!this->isActiveWindow()) {
            DPalette parentPb = DGuiApplicationHelper::instance()->applicationPalette();
            attentionPalette.setColor(DPalette::WindowText, parentPb.color(DPalette::Inactive, DPalette::WindowText));
        }
        m_attentionLabel->setPalette(attentionPalette);

        m_inProgressMovie->setFileName(":assets/images/dark/dark-loading.gif");
        if (m_inProgressMovie->state() == QMovie::Running) {
            m_inProgressMovie->stop();
            m_inProgressMovie->start();
        }
    }
    this->update();
}

void AiWriterDialog::showDialog() {
    qCDebug(logWordWizard) << "Showing writer dialog";

    if (!this->isVisible()) {
        if (m_isNeedResetUi) {
            this->adjustSize();
            this->adjustDialogInitPosition();
        }
        this->show();

        // tid:1001600003 event:writer
        ReportIns()->writeEvent(report::WriterPoint().assemblingData());
    }
    this->activateWindow();
}

void AiWriterDialog::closeDialog() {
    qCDebug(logWordWizard) << "Closing writer dialog";
}

void AiWriterDialog::showEvent(QShowEvent *event) {
    qCDebug(logWordWizard) << "Showing, need reset UI:" << m_isNeedResetUi;
    DAbstractDialog::showEvent(event);
    m_isShow = true;
}

void AiWriterDialog::reject() {
    //qInfo() << QString("reject() trigger this->close()");
    m_isNeedResetUi = true;
    this->close();

    return DAbstractDialog::reject();
}

void AiWriterDialog::closeEvent(QCloseEvent *event) {
    qCDebug(logWordWizard) << "Closing writer dialog";
    if (!m_chatTask.id.isEmpty()) {
        chatSession->cancelSession(m_chatTask.id);
    }
    m_isShow = false;
    m_isNeedResetUi = true;
    this->resetUi();

    return DAbstractDialog::closeEvent(event);
}

void AiWriterDialog::changeEvent(QEvent *event) {
    //qInfo() << QString("changeEvent()");
    if (this->isVisible()) {
        this->onUpdateSystemTheme(DGuiApplicationHelper::instance()->themeType());
    }

    return DAbstractDialog::changeEvent(event);
}

void AiWriterDialog::moveEvent(QMoveEvent *event) {
    if (m_initPos != QPoint(0, 0) && event->oldPos() == m_initPos && event->pos() == QPoint(0, 0)) {
        qDebug() << QString("need forbid pos reset to 0");
        QTimer::singleShot(10, this, [&] {
            this->move(m_initPos);
        });
    }
    DAbstractDialog::moveEvent(event);
}

bool AiWriterDialog::eventFilter(QObject *watched, QEvent *event) {
    //qInfo() << event->type();
    // 是否启用此代码取决于基类
    if (false) {
        // 响应ESC按键
        if (event->type() == QEvent::KeyPress) {
            QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>(event);
            if (keyEvent->key() == Qt::Key_Escape) {
                m_isNeedResetUi = true;
                this->close();
                return true;
            }
        } else if (event->type() == QEvent::KeyRelease) {
            QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>(event);
            if (keyEvent->key() == Qt::Key_Escape) {
                return true;
            }
        }
    }

    if (watched == m_queryEdit) {
        if (event->type() == QEvent::KeyPress) {
            QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>(event);
            if (keyEvent->modifiers() == Qt::ControlModifier && (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter)) {
                if (!m_queryEdit->isFirstQuery()) {
                    return true;
                }

                m_queryEdit->insertPlainText("\n");
                return true;
            } else if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) {
                if (m_sendBt->isEnabled()) {
                    m_sendBt->click();
                }
                return true;
            }
        } else if (event->type() == QEvent::KeyRelease) {
            QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>(event);
            if (keyEvent->modifiers() == Qt::ControlModifier && (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter)) {
                return true;
            } else if (keyEvent->key() == Qt::Key_Return || keyEvent->key() == Qt::Key_Enter) {
                return true;
            }
        }
    }

    return DAbstractDialog::eventFilter(watched, event);
}

void AiWriterDialog::onBtClicked() {
    if (sender() == m_curBt) { // 重复点同一个类别型button，不响应
        return;
    }

    if (sender() == m_logoBt) {
        WordWizard::onIconBtnClicked();
        return;
    }

    if (sender() == m_sendBt || sender() == m_sendBt1) {
        if (!WelcomeDialog::isAgreed()) {
            WelcomeDialog::instance(false)->exec();
            return;
        }
        this->sendAiRequst();
        return;
    }

    // tid:1001600004 event:writer_function
    if (sender() == m_articleBt) {
        qCDebug(logWordWizard) << "click article button";
        QString topic;
        if (m_curBt) {
            m_curBt->clearPressColor();
        } else {
            topic = m_queryEdit->toPlainText();
        }
        m_curBt = dynamic_cast<FillTextButton *>(sender());
        m_queryEdit->setText(QString(tr("Help me write an essay on the topic of [%1] with [clear structure and rich content].")).arg(topic.isEmpty() ? tr("Artificial Intelligence") : topic));
        m_queryEdit->moveCursor(QTextCursor::End);
        m_queryEdit->setFocus();
        ReportIns()->writeEvent(report::WriterFunctionPoint("essay").assemblingData());
        return;
    }

    if (sender() == m_outlineBt) {
        qCDebug(logWordWizard) << "click outline button";
        QString topic;
        if (m_curBt) {
            m_curBt->clearPressColor();
        } else {
            topic = m_queryEdit->toPlainText();
        }
        m_curBt = dynamic_cast<FillTextButton *>(sender());
        m_queryEdit->setText(QString(tr("Help me write an outline on the topic of [%1], which will be used for [PPT production].")).arg(topic.isEmpty() ? tr("Artificial Intelligence") : topic));
        m_queryEdit->moveCursor(QTextCursor::End);
        m_queryEdit->setFocus();
        ReportIns()->writeEvent(report::WriterFunctionPoint("outline").assemblingData());
        return;
    }

    if (sender() == m_notifyBt) {
        qCDebug(logWordWizard) << "click notify button";
        QString topic;
        if (m_curBt) {
            m_curBt->clearPressColor();
        } else {
            topic = m_queryEdit->toPlainText();
        }
        m_curBt = dynamic_cast<FillTextButton *>(sender());
        m_queryEdit->setText(QString(tr("Help me write a notice about [%1], the receiver is [All Employees] and the sender is [Administration Department].")).arg(topic.isEmpty() ? tr("National Day Holiday") : topic));
        m_queryEdit->moveCursor(QTextCursor::End);
        m_queryEdit->setFocus();
        ReportIns()->writeEvent(report::WriterFunctionPoint("notice").assemblingData());
        return;
    }

    if (sender() == m_reportBt) {
        qCDebug(logWordWizard) << "click report button";
        QString topic;
        if (m_curBt) {
            m_curBt->clearPressColor();
        } else {
            topic = m_queryEdit->toPlainText();
        }
        m_curBt = dynamic_cast<FillTextButton *>(sender());
        m_queryEdit->setText(QString(tr("Help me write a research report on [%1], at least it needs to include [status description, problem analysis, countermeasures and suggestions, research conclusions].")).arg(topic.isEmpty() ? tr("Artificial Intelligence") : topic));
        m_queryEdit->moveCursor(QTextCursor::End);
        m_queryEdit->setFocus();
        ReportIns()->writeEvent(report::WriterFunctionPoint("report").assemblingData());
        return;
    }

    if (sender() == m_speechBt) {
        qCDebug(logWordWizard) << "click speech button";
        QString topic;
        if (m_curBt) {
            m_curBt->clearPressColor();
        } else {
            topic = m_queryEdit->toPlainText();
        }
        m_curBt = dynamic_cast<FillTextButton *>(sender());
        m_queryEdit->setText(QString(tr("Help me write a speech on the topic of [%1] for [Company Leaders], requiring [clear structure and vivid language].")).arg(topic.isEmpty() ? tr("Artificial Intelligence") : topic));
        m_queryEdit->moveCursor(QTextCursor::End);
        m_queryEdit->setFocus();
        ReportIns()->writeEvent(report::WriterFunctionPoint("speech").assemblingData());
        return;
    }

    if (sender() == m_summaryBt) {
        qCDebug(logWordWizard) << "click summary button";
        QString topic;
        if (m_curBt) {
            m_curBt->clearPressColor();
        } else {
            topic = m_queryEdit->toPlainText();
        }
        m_curBt = dynamic_cast<FillTextButton *>(sender());
        m_queryEdit->setText(QString(tr("Help me write a summary of my recent work, including [%1] and [results], requiring a [formal] tone.")).arg(topic.isEmpty() ? tr("work content") : topic));
        m_queryEdit->moveCursor(QTextCursor::End);
        m_queryEdit->setFocus();
        ReportIns()->writeEvent(report::WriterFunctionPoint("summary").assemblingData());
        return;
    }

    if (sender() == m_publicBt) {
        qCDebug(logWordWizard) << "click public button";
        QString topic;
        if (m_curBt) {
            m_curBt->clearPressColor();
        } else {
            topic = m_queryEdit->toPlainText();
        }
        m_curBt = dynamic_cast<FillTextButton *>(sender());
        m_queryEdit->setText(QString(tr("Help me write a public tweet on the topic of [%1], requiring [clear structure] and [relaxed] tone.")).arg(topic.isEmpty() ? tr("Artificial Intelligence") : topic));
        m_queryEdit->moveCursor(QTextCursor::End);
        m_queryEdit->setFocus();
        ReportIns()->writeEvent(report::WriterFunctionPoint("tweet").assemblingData());
        return;
    }

    if (sender() == m_replaceBt) {
        qCInfo(logWordWizard) << "Replace button clicked, isFirstFill:" << m_isFirstFill;
        if (m_isFirstFill) { // 第一次使用填充功能，提醒用户重启输入法服务
            DDialog dlg(this);
            dlg.setWindowFlags(dlg.windowFlags() | Qt::WindowStaysOnTopHint);
            dlg.setMinimumWidth(380);
            dlg.setIcon(QIcon(":assets/images/tips.svg"));
            dlg.setMessage(tr("The function can be used only after restarting the input method. The tutorial is as follows:\n1. Right-click on the input method icon in the taskbar.\n2. Click \"Restart\"."));
            DLabel imageLabel(this);
            imageLabel.setPixmap(QPixmap(QIcon::fromTheme("uos-ai-assistant_fcitxrestart").pixmap(160, 160)));
            dlg.addContent(&imageLabel, Qt::AlignCenter);
            dlg.addButton(tr("Ok", "button"), true, DDialog::ButtonNormal);
            connect(&dlg, &DDialog::finished, this, [&] {
                qCDebug(logWordWizard) << "fcitx restart accepted";
                m_isFirstFill = false;
                DConfigManager::instance()->setValue(AIQUICK_GROUP, AIQUICK_ISFIRSTFILL, false);
            });
            dlg.exec();
        } else {
            this->setVisible(false);
            QTimer::singleShot(200, this, &AiWriterDialog::fillToText);
        }
        return;
    }

    if (sender() == m_againBt) {
        //qInfo() << QString("again");
        sendAiRequst(true);
        return;
    }

    if (sender() == m_copyBt) {
        this->copyText(m_reply);
        return;
    }

    if (sender() == m_cancelBt) {
        //qInfo() << QString("cancel");
        m_curErr = AiQuickDialog::ERROR_TYPE_NONE;
        this->enableReplyFunBt(true);

        m_reply        = m_replyBak;
        m_replyEditMax = m_replyEditMaxBak;
        m_replyEdit->setPlainText(m_reply);
        m_replyEdit->setVisible(true);
        m_errorInfoLabel->setVisible(false);
        m_cancelBt->setVisible(false);

        this->asyncAdjustSize();
        return;
    }
}

void AiWriterDialog::onTextChanged()
{
    if (m_queryEdit->toPlainText().isEmpty()) {
        this->clearArticleBtPressColor();
    }
    this->onQueryTextChanged();
}

void AiWriterDialog::onQueryTextChanged(bool isActiveChange) {
    m_queryEdit->blockSignals(true);

    QTextDocument *document = m_queryEdit->document();
    QTextCursor cursor(document);

    DPalette parentPb = DGuiApplicationHelper::instance()->applicationPalette();
    QColor defaultTextColor = parentPb.color(DPalette::Normal, DPalette::WindowText);
    QColor highTextColor = parentPb.color(DPalette::Normal, DPalette::Highlight);
    if (!this->isActiveWindow()) {
        defaultTextColor = parentPb.color(DPalette::Inactive, DPalette::WindowText);
    }

    cursor.select(QTextCursor::Document);
    QTextCharFormat defaultFormat;
    defaultFormat.setForeground(defaultTextColor);
    cursor.mergeCharFormat(defaultFormat);

    QRegularExpressionMatchIterator matchIterator = m_queryEdit->highLightRegex.globalMatch(m_queryEdit->toPlainText());
    QTextCharFormat highFormat;
    highFormat.setForeground(highTextColor);

    cursor.beginEditBlock();
    cursor.movePosition(QTextCursor::Start);

    while (matchIterator.hasNext()) {
        QRegularExpressionMatch match = matchIterator.next();
        cursor.setPosition(match.capturedStart());
        cursor.setPosition(match.capturedEnd(), QTextCursor::KeepAnchor);
        cursor.setCharFormat(highFormat);
        cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, 1);
    }
    cursor.endEditBlock();

    m_queryEdit->blockSignals(false);

    if (!isActiveChange) {
        this->asyncAdjustSize();
    }
}

void AiWriterDialog::onModelReply(int op, QString reply, int err) {
    if (err != GErrorType::NoError) {
        qCWarning(logWordWizard) << "Model reply error:" << err << "reply:" << reply;
    }
    if (!m_isShow) {
        return;
    }

    m_replyEdit->setVisible(true);
    m_inProgressLabel->setVisible(false);
    m_inProgressMovie->stop();
    m_progressVSpace->setVisible(false);

    /**
     * FREEACCOUNTEXPIRED = 9000,
     * FREEACCOUNTUSAGELIMIT = 9001,
     * FREEACCOUNTCHATUSAGELIMIT = 9002,
     * FREEACCOUNTTEXT2IMAGEUSAGELIMIT = 9003,
     * AccountInvalid = 9004,
     */
    if (err == GErrorType::NoError) {
        m_curErr = AiQuickDialog::ERROR_TYPE_NONE;
        m_isReplyEnd = false;
        this->putAiReply(reply);
    } else if (err == SessionEvent::SeFinished) { // 流式回答结束符号
        m_isReplyEnd = true;
        this->putAiReply();
    } else { // ERROR part
        m_isReplyEnd = true;
        this->setReplyPartVisible(true);
        err = std::abs(err);
        if (err == GErrorType::ModelChatUsageClaimAgain) {  // 额度用完
            m_curErr     = AiQuickDialog::ERROR_TYPE_ACCOUNT_LIMIT;
            m_curErrInfo = reply;
            this->handleError();
        } else if (err == GErrorType::ModelChatUsageLimitReached) {
            m_curErr     = AiQuickDialog::ERROR_TYPE_ACCOUNT_INVALID;
            m_curErrInfo = reply;
            this->handleError();
        } else {
            // 其他错误的兜底处理，测试说：按正常回答处理
            m_curErr = AiQuickDialog::ERROR_TYPE_NONE;
            this->putAiReply(reply);
        }
    }

    this->asyncAdjustSize();
}

void AiWriterDialog::onFontChanged(const QFont &font) {
    this->asyncAdjustSize();
}

void AiWriterDialog::onOpenConfigDialog(const QString& link) {
    QMetaObject::invokeMethod(aiApp, "showConfig", Qt::QueuedConnection, Q_ARG(int, MgmtWindow::Page::ModelList));
    // BUG-292967，同步随航的方案：打开设置窗口后，关闭面板
    m_isNeedResetUi = true;
    this->close();
}

void AiWriterDialog::setQuerySepHeight(int height) {
    m_querySep->setFixedHeight(height);
}

void AiWriterDialog::showToast(const QString &message) {
    DFloatingMessage *floatMessage = new DFloatingMessage(DFloatingMessage::TransientType, this);
    floatMessage->setMessage(message);
    floatMessage->setAttribute(Qt::WA_DeleteOnClose);
    DIconButton *bt = floatMessage->findChild<DIconButton *>();
    if (bt) {
        bt->setVisible(false);
    }
    QLabel *label = floatMessage->findChild<QLabel *>();
    if (label) {
        label->setAlignment(Qt::AlignCenter);
    }

    QRect geometry(QPoint(10, 10), floatMessage->sizeHint());
    geometry.moveCenter(this->rect().center());

    //Show the message near bottom
    geometry.moveBottom(this->rect().bottom() - 8);
    floatMessage->setGeometry(geometry);
    floatMessage->show();
}

void AiWriterDialog::copyText(const QString &text) {
    //qInfo() << QString("copy");
    this->showToast(tr("Copied"));
    QApplication::clipboard()->setText(text);
}

void AiWriterDialog::syncLlmAccount() {
    // 根据当前功能类型选择相应的 assistant
    QString assistantId = STR_KEY_UOS_AI_GENERIC; // 默认使用通用助手

    // 获取可用模型列表
    auto models = AssistantMgr->availableModels(assistantId);

    // 使用数据库查询当前模型
    auto *db = AppDatabase::instance();
    UsedModelTable usedModel = UsedModelTable::getByAssistant(db, assistantId);

    QString targetModelId;
    bool isFound = false;

    if (!usedModel.assistant().isEmpty()) {
        // 从数据库中找到了当前模型
        targetModelId = usedModel.model();
    }

    // 如果数据库中有记录，尝试查找匹配的模型
    if (!targetModelId.isEmpty()) {
        for (const ModelAccountPtr &info : models) {
            if (info->id == targetModelId) {
                isFound = true;
                m_modelId = info->id;
                m_modelInfo = tr("Current model: ") + info->model.name;
                qCInfo(logWordWizard) << "Found current model from database:" << m_modelId;
                break;
            }
        }
    }

    // 没有找到当前模型，使用与 AssistantChannel::getCurrentModel 相同的逻辑选择默认模型
    if (!isFound) {
        // 优先选择 uos_ai 模型
        for (const ModelAccountPtr &info : models) {
            if (ModelVendor::isUosProvider(info)) {
                isFound = true;
                m_modelId = info->id;
                m_modelInfo = tr("Current model: ") + info->model.name;
                qCInfo(logWordWizard) << "Using uos_ai model as default:" << m_modelId;
                break;
            }
        }
    }

    // 如果没有 uos_ai 模型，使用第一个可用模型
    if (!isFound && !models.isEmpty()) {
        isFound = true;
        const ModelAccountPtr &info = models.first();
        m_modelId = info->id;
        m_modelInfo = tr("Current model: ") + info->model.name;
        qCInfo(logWordWizard) << "Using first available model as default:" << m_modelId ;
    }

    // 没有可用模型
    if (!isFound) {
        m_modelId.clear();
        m_modelInfo = tr("Currently no model");
        qCWarning(logWordWizard) << "No available models found for assistant:" << assistantId;
    }

    m_modelBt->setToolTip(m_modelInfo);

    this->asyncAdjustSize();
}

void AiWriterDialog::handleError() {
    if (m_curErr == AiQuickDialog::ERROR_TYPE_NO_MODEL || m_curErr == AiQuickDialog::ERROR_TYPE_ACCOUNT_LIMIT || m_curErr == AiQuickDialog::ERROR_TYPE_ACCOUNT_EXPIRED || m_curErr == AiQuickDialog::ERROR_TYPE_ACCOUNT_INVALID) {
        this->enableReplyFunBt(false);
        if (!m_replyBak.isEmpty()) {
            m_cancelBt->setVisible(true);
        }
        const QColor &color = DPaletteHelper::instance()->palette(m_errorInfoLabel).color(DPalette::Normal, DPalette::Highlight);
        m_errorInfoLabel->setText(m_curErrInfo
                                  + QString("<a href=\"javascript:void(0)\" style=\"color:%1; text-decoration: none;\"> %2<img src=\"%3\"></a>")
                                        .arg(color.name())
                                        .arg(tr("Go to configure  "))
                                        .arg(GUtils::generateImage(m_errorInfoLabel, color, QSize(QFontMetrics(m_errorInfoLabel->font()).height() / 2, QFontMetrics(m_errorInfoLabel->font()).height() / 2), QStyle::PE_IndicatorArrowRight)));
        m_replyEdit->setVisible(false);
        m_errorInfoLabel->setVisible(true);

        this->asyncAdjustSize();
        return;
    }
}

void AiWriterDialog::sendAiRequst(bool isAgain) {
    m_query = m_queryEdit->toPlainText();
    m_queryEdit->clear();
    m_queryEdit->setFirstQueryFlag(false);
    m_queryEdit->setPlaceholderText(tr("Modify the content, change the tone…"));
    m_queryEdit->setLineWrapMode(QTextEdit::NoWrap);
    m_sendBt->setVisible(true);
    m_querySpace->setVisible(true);
    m_sendBt1->setVisible(false);
    m_sendVSpace->setVisible(false);

    int height = static_cast<int>(m_queryEdit->document()->size().height()) + 4;
    m_queryEdit->setFixedHeight(height);

    if (!m_reply.isEmpty()) {
        m_replyBak        = m_reply;
        m_replyEditMaxBak = m_replyEditMax;
    }
    m_reply.clear();
    m_replyEdit->clear();
    m_replyEditMax = this->maximumHeight();
    m_isReplyEditCursorEnd = true;

    m_replyCacheMutex.lock();
    m_replyCache.clear();
    m_replyCacheIdx = 0;
    m_replyCacheMutex.unlock();

    m_errorInfoLabel->setVisible(false);
    m_cancelBt->setVisible(false);
    m_replyEdit->setVisible(false);
    m_inProgressLabel->setVisible(true);
    m_inProgressMovie->start();
    m_progressVSpace->setVisible(true);

    // 每次发起请求时，隐藏下半部分控件
    this->enableReplyFunBt(false);
    this->setReplyPartVisible(false);
    this->setArticleBtPartVisible(false);

    // 每次发起请求时，复位错误码
    m_curErr = AiQuickDialog::ERROR_TYPE_NONE;
    if (!m_chatTask.id.isEmpty()) {
        chatSession->cancelSession(m_chatTask.id);
        m_chatTask.id.clear();
    }

    if (!isAgain) {
        m_prompt = m_query;
        if (!m_replyBak.isEmpty()) {
            m_prompt = QString("请按照要求【%1】对以下文本进行加工：\n%2").arg(m_query).arg(m_replyBak);
        }

        if (!m_queryTextEdit->isVisible()) {
            m_queryTextEdit->setFullText(m_prompt);
            m_querySep->setVisible(true);
            m_queryTextEdit->setVisible(true);
            m_queryHSep->setVisible(true);
            m_queryHSepVSpace->setVisible(true);
        }
    }
    qCInfo(logWordWizard) << "Sending AI request - model:" << m_modelInfo << "isAgain:" << isAgain << "query:" << m_prompt;
    m_reqId = this->sendWordWizardRequest(m_modelId, m_prompt, 0.7, true);

    this->asyncAdjustSize();
}

QString AiWriterDialog::sendWordWizardRequest(const QString &llmId, const QString &conversation, qreal temperature, bool stream)
{
    if (llmId.isEmpty() || conversation.isEmpty()) {
        qCWarning(logWordWizard) << "Empty llmId or conversation, skipping request";
        return QString();
    }

    qCDebug(logWordWizard) << "Requesting chat text with llmId:" << llmId << "temperature:" << temperature << "stream:" << stream;
    QVariantHash params = {
        {STR_KEY_STREAM, QVariant(stream)},
        {STR_KEY_THINKING, QVariant(false)},
    };

    QString sessionId = GlobalUtil::generateMsId();

    // 创建会话
    {
        auto obj = chatSession->createSession(STR_KEY_UOS_AI_CHAT, sessionId);
        if (obj.value(STR_KEY_ID).toString() != sessionId) {
            qCWarning(logWordWizard) << "createSession error: " << llmId << conversation;
            return QString();
        }
    }
    auto record = convertRequestion(sessionId, conversation);
    auto runRet = chatSession->runSession(sessionId, llmId, record, params);
    if (runRet.contains(STR_KEY_ERROR)) {
        qCWarning(logWordWizard) << "runSession error: " << runRet.value(STR_KEY_MESSAGE).toString();
        return QString();
    }

    // 保存当前会话信息到单个 ChatTask 实例
    m_chatTask.id = sessionId;
    m_chatTask.stream = stream;
    m_chatTask.record = record;

    return sessionId;
}

void AiWriterDialog::onEvent(int event, const QString &id, const QString &json)
{
    // 只处理当前会话的事件
    if (id != m_chatTask.id) {
        qCDebug(logWordWizard) << "Ignoring event for non-current session:" << id;
        return;
    }

    if (event == SessionEvent::SeError) {
        qCWarning(logWordWizard) << "Session error:" << id << json;

        // Parse error JSON to extract error code and error message
        int errorCode = -1;
        QString errorMessage;
        QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8());
        if (!doc.isNull() && doc.isObject()) {
            QJsonObject jsonObj = doc.object();
            errorCode = jsonObj.value("error").toInt(-1);
            errorMessage = jsonObj.value("error_message").toString();

            if (!errorMessage.isEmpty()) {
                this->onModelReply(0, errorMessage, errorCode);
            } else {
                this->onModelReply(0, QString(), errorCode);
            }
        } else {
            this->onModelReply(0, QString(), -1);
        }
    } else if (event == SessionEvent::SeMessage) {
        // 流式消息事件：stream=true 时才会收到
        if (!m_chatTask.stream) {
            qCDebug(logWordWizard) << "Ignoring SeMessage event for non-stream session:" << id;
            return;
        }

        QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8());
        RenderMessage msg = RenderMessage::fromJson(doc.object());
        if (msg.type == CntText) {
            qCDebug(logWordWizard) << "Received stream message:" << msg.data.toString();
            this->onModelReply(0, msg.data.toString(), GErrorType::NoError);
        }
    } else if (event == SessionEvent::SeFinished) {
        m_chatTask.id.clear();
        if (m_chatTask.stream) {
            // 流式模式下，SeFinished 表示流式输出结束，发送空内容表示结束
            qCDebug(logWordWizard) << "Stream finished for session:" << id;
            this->onModelReply(0, QString(), SessionEvent::SeFinished);
        } else {
            // 非流式模式下，SeFinished 包含完整的回复内容
            QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8());
            QString msgId = doc.object().value(STR_KEY_ID).toString();
            if (m_chatTask.record.isNull()) {
                qCWarning(logWordWizard) << "No record found for session:" << id;
                return;
            }

            auto msgNode = m_chatTask.record->messageAt(msgId);
            if (msgNode.isNull()) {
                qCWarning(logWordWizard) << "No message found for ID:" << msgId;
                return;
            }

            auto list = msgNode->getMessage();
            if (list.isEmpty()) {
                qCWarning(logWordWizard) << "No message content found for ID:" << msgId;
                return;
            }
            QString content;
            for (const MetaMessage &mm : list.last().content) {
                if (mm.type == CntText)
                    content.append(mm.data.toString());
            }

            qCDebug(logWordWizard) << "Received finished message (non-stream):" << content;
            this->onModelReply(0, content, GErrorType::NoError);
        }
    }
}

QSharedPointer<uos_ai::ConversationRecord> AiWriterDialog::convertRequestion(const QString &id, const QString &json)
{
    QSharedPointer<uos_ai::ConversationRecord> record(new uos_ai::ConversationRecord(id));
    uos_ai::MessageNodePtr msgNode(new uos_ai::MessageNode);
    msgNode->setId(GlobalUtil::generateMsId());
    msgNode->setRole(MrUser);

    record->addMessage("", msgNode);
    record->setCurrentMessage(msgNode->getId());

    const QJsonDocument &document = QJsonDocument::fromJson(json.toUtf8());
    if (document.isArray()) {
        for ( QJsonValue val : document.array()) {
            if (!val.isObject())
                continue;

            ModelMessage modelMsg;
            modelMsg.role = val.toObject().value(STR_KEY_ROLE).toString();
            MetaMessage metaMsg;
            metaMsg.type = CntText;
            metaMsg.data = val.toObject().value(STR_KEY_CONTENT).toString();
            modelMsg.content.append(metaMsg);
            msgNode->appendMessage(modelMsg);
        }
    } else {
        ModelMessage modelMsg;
        modelMsg.role = STR_KEY_USER;
        MetaMessage metaMsg;
        metaMsg.type = CntText;
        metaMsg.data = json;
        modelMsg.content.append(metaMsg);
        msgNode->appendMessage(modelMsg);
    }

    return record;
}

void AiWriterDialog::putAiReply(QString reply) {
    while (m_replyCache.isEmpty() && reply.startsWith("\n")) {
        reply.remove(0, 1);
    }
    reply.replace("\t", "    ");
    bool isStop = true;
    m_replyCacheMutex.lock();
    if (m_replyCacheIdx < m_replyCache.size()) {
        isStop = false;
    }
    m_replyCache += reply;
    m_replyCacheMutex.unlock();
    if (isStop) {
        this->smoothEachWord();
    }
}

void AiWriterDialog::smoothEachWord() {
    m_replyCacheMutex.lock();
    QString appendStr;
    if (m_replyCacheIdx < m_replyCache.size()) {
        QChar word = m_replyCache[m_replyCacheIdx];
        m_replyCacheIdx++;
        m_reply += word;
        appendStr += word;
        // 非中文的情况，按字母输出太慢了，尝试两个字母输出
        if (word.script() != QChar::Script_Han && m_replyCacheIdx < m_replyCache.size()) {
            word = m_replyCache[m_replyCacheIdx];
            m_replyCacheIdx++;
            m_reply += word;
            appendStr += word;
        }
    } else {
        m_replyCacheMutex.unlock();
        if (m_isReplyEnd && m_isShow) {
            this->setReplyPartVisible(true);
            this->enableReplyFunBt(true);
            this->asyncAdjustSize(100);
        }
        return;
    }
    m_replyCacheMutex.unlock();

    if (!m_isShow) {
        return;
    }

    m_replyEdit->insertPlainText(appendStr);
    this->asyncAdjustSize();

    QTimer::singleShot(30, this, &AiWriterDialog::smoothEachWord);
}

void AiWriterDialog::fillToText() {
    bool isWritable = WordWizard::fcitxWritable();
    qCInfo(logWordWizard) << "Replace button clicked, isWritable:" << isWritable;
    if (!isWritable) { // 此时，再次判断是否可写
        DDialog dlg(this);
        dlg.setWindowFlags(dlg.windowFlags() | Qt::WindowStaysOnTopHint);
        dlg.setIcon(QIcon(WARNING_ICON));
        dlg.setTitle(tr("Fill failed"));
        dlg.setMessage(tr("No input box selected, please select the input box and re-fill it."));
        dlg.addButton(tr("Cancel"), false, DDialog::ButtonNormal);
        dlg.addButton(tr("Ok"), true, DDialog::ButtonRecommend);
        connect(&dlg, &DDialog::accepted, this, [&] {
           //qInfo() << QString("fill accepted");
           m_isNeedResetUi = false;
           this->setVisible(true);
        });
        connect(&dlg, &DDialog::rejected, this, [&] {
           //qInfo() << QString("fill rejected, trigger this->close()");
           m_isNeedResetUi = true;
           this->close();
        });
        connect(&dlg, &DDialog::closed, this, [&] {
           //qInfo() << QString("fill closed, trigger this->close()");
           m_isNeedResetUi = true;
           this->close();
        });
        QPoint pos;
        pos.setX(this->geometry().x() + (this->sizeHint().width() - dlg.width()) / 2);
        pos.setY(this->geometry().y() + (this->sizeHint().height() - dlg.height()) / 2);
        dlg.move(pos);
        dlg.exec();
    } else {
        FcitxInputServer::getInstance().commitString(m_reply);
        //qInfo() << QString("after fill, trigger this->close()");
        m_isNeedResetUi = true;
        this->close();
    }
}

void AiWriterDialog::setArticleBtPartVisible(bool isTrue) {
    m_articleBt->setVisible(isTrue);
    m_outlineBt->setVisible(isTrue);
    m_notifyBt->setVisible(isTrue);
    m_reportBt->setVisible(isTrue);
    m_speechBt->setVisible(isTrue);
    m_summaryBt->setVisible(isTrue);
    m_publicBt->setVisible(isTrue);
    m_articleVSpace->setVisible(isTrue);
    m_articleVSpace1->setVisible(isTrue);

    if (isTrue) {
        this->clearArticleBtPressColor();
    }
}

void AiWriterDialog::clearArticleBtPressColor() {
    m_curBt = nullptr;

    m_articleBt->clearPressColor();
    m_outlineBt->clearPressColor();
    m_notifyBt->clearPressColor();
    m_reportBt->clearPressColor();
    m_speechBt->clearPressColor();
    m_summaryBt->clearPressColor();
    m_publicBt->clearPressColor();
}

void AiWriterDialog::enableReplyFunBt(bool isTrue) {
    m_replaceBt->setEnabled(isTrue);
    m_againBt->setEnabled(isTrue);
    m_copyBt->setEnabled(isTrue);
}

void AiWriterDialog::setReplyPartVisible(bool isTrue) {
    m_replaceBt->setVisible(isTrue);
    m_replySep->setVisible(isTrue);
    m_againBt->setVisible(isTrue);
    m_copyBt->setVisible(isTrue);

    m_vSpace->setVisible(isTrue);

    m_attentionLabel->setVisible(isTrue);
    m_modelBt->setVisible(isTrue);

    dynamic_cast<WriterTextEdit *>(m_replyEdit)->setInProgress(!isTrue);
}

void AiWriterDialog::adjustQueryEditSize() {
    if (!m_queryEdit->isFirstQuery()) { // 第一轮问答后，问题框锁定高度
        m_sendBt->setEnabled(!m_queryEdit->toPlainText().isEmpty());
        return;
    }

    int height = static_cast<int>(m_queryEdit->document()->size().height()) + 4;
    m_queryEdit->setFixedHeight(height > m_queryEditMax ? m_queryEditMax : height);

    if ((QFontMetrics(m_queryEdit->font()).horizontalAdvance(m_queryEdit->toPlainText()) > this->width() - 30 - m_sendBt->sizeHint().width() - 40)
            || m_queryEdit->toPlainText().indexOf("\n") != -1) {
        m_sendBt->setVisible(false);
        m_querySpace->setVisible(false);
        m_sendBt1->setVisible(true);
        m_sendVSpace->setVisible(true);
    } else {
        m_sendBt->setVisible(true);
        m_querySpace->setVisible(true);
        m_sendBt1->setVisible(false);
        m_sendVSpace->setVisible(false);
    }

    if (m_queryEdit->toPlainText().isEmpty()) {
        m_sendBt->setEnabled(false);
        this->adjustSize();
        return;
    }

    m_sendBt->setEnabled(true);

    this->adjustSize();

    if (m_queryEditMax == this->maximumHeight() && m_sendBt1->isVisible()) {
        m_queryEditMax = this->maximumHeight() - this->sizeHint().height() + m_queryEdit->height();
        m_queryEdit->setFixedHeight(height > m_queryEditMax ? m_queryEditMax : height);
    }
}

void AiWriterDialog::adjustReplyEditSize() {
    if (!m_replyEdit->isVisible()) {
        return;
    }

    QTextBlockFormat blockFmt = m_replyEdit->textCursor().blockFormat();
    blockFmt.setLineHeight(122, QTextBlockFormat::LineHeightTypes::ProportionalHeight);
    m_replyEdit->textCursor().setBlockFormat(blockFmt);

    int height = static_cast<int>(m_replyEdit->document()->size().height()) + 4;
    m_replyEdit->setFixedHeight(height > m_replyEditMax ? m_replyEditMax : height);
    this->adjustSize();

    if (this->sizeHint().height() + 50 > this->maximumHeight()
            && this->sizeHint().height() < this->maximumHeight()
            && m_replyEditMax == this->maximumHeight()) {
        m_replyEditMax = m_replyEdit->height() + (this->maximumHeight() - this->sizeHint().height());
    } else if (this->sizeHint().height() > this->maximumHeight()) {
        m_replyEditMax -= this->sizeHint().height() - this->maximumHeight();
        m_replyEdit->setFixedHeight(m_replyEditMax);
    } else if (this->sizeHint().height() < this->maximumHeight() && height > m_replyEditMax) {
        m_replyEditMax = m_replyEdit->height() + (this->maximumHeight() - this->sizeHint().height());
        m_replyEdit->setFixedHeight(height > m_replyEditMax ? m_replyEditMax : height);
    }
    this->adjustSize();

    if (m_isReplyEditCursorEnd) {
        m_replyEdit->moveCursor(QTextCursor::End);
    }
}

void AiWriterDialog::adjustDialogInitPosition() {
    static DDeDockObject dock(this);
    QRect frontendWindowRect = dock.frontendWindowRect();
    DockPosition dockPosition = static_cast<DockPosition>(dock.position());

    QList<QScreen *> screens = QGuiApplication::screens();
    QPoint cursorPos = QCursor::pos();
    QRect screenRect;

    qCDebug(logWordWizard) << "height:" << this->sizeHint().height();
    bool isFound = false;
    for (const QScreen *screen : screens) {
        if (screen->geometry().contains(cursorPos)) {
            screenRect = screen->geometry();
            isFound = true;
            break;
        }
    }
    if (!isFound && !screens.isEmpty()) {
        screenRect = screens[0]->geometry();
        isFound = true;
    }
    if (isFound) {
        QPoint pos;
        pos.setX(screenRect.x() + ((screenRect.width() - this->width()) / 2));
        if (dockPosition == DockPosition::Bottom) { // 任务栏在底下
            pos.setY(screenRect.bottom() - frontendWindowRect.height() - 50 - this->sizeHint().height());
        } else {
            pos.setY(screenRect.bottom() - 50 - this->sizeHint().height());
        }

        qCDebug(logWordWizard) << "screen:" << screenRect << "pos:" << pos;
        this->move(pos);
        m_initPos = pos;

#ifdef COMPILE_ON_V25
        if (ESystemContext::isTreeland()) {
            DDEShellWayland::get(windowHandle())->setPosition(m_initPos);
        }
#endif
    }

    this->syncLlmAccount();

    m_queryEdit->setFocus();
}

void AiWriterDialog::adjustDialogPosition() {
    int bottomLimit = 0;
    QList<QScreen *> screens = QGuiApplication::screens();
    for(QScreen *screen : screens) {
        if (screen->geometry().contains(this->geometry().x() + this->sizeHint().width() / 2, this->geometry().y())) {
            bottomLimit = screen->geometry().bottom();
            break;
        }
    }
    if (bottomLimit > 0 && this->geometry().y() + this->sizeHint().height() > bottomLimit) {
        // BUG-292883 控制中心开启最佳性能，坐标点有1个像素的偏移，需要纠正
        static int lastX = this->geometry().x();
        if (qAbs(lastX - this->geometry().x()) >= 2) {
            lastX = this->geometry().x();
        }
        this->move(lastX, this->geometry().y() - (this->geometry().y() + this->sizeHint().height() - bottomLimit));
    }
}

void AiWriterDialog::asyncAdjustSize(int ms) {
    QTimer::singleShot(ms, this, [&] {
        this->adjustQueryEditSize();
        this->adjustReplyEditSize();
        this->adjustSize();
        this->adjustDialogPosition();
    });
}
