#include <gio/gio.h>

#include "wordwizard.h"
#include "llmutils.h"
#include "private/xeventmonitor.h"
#ifdef COMPILE_ON_V25
#include "treelandclipboard.h"
#else
#include "private/xclipboard.h"
#endif
#include "wrapper/wizardwrapper.h"
#include "serverwrapper.h"
#include "fcitxinputserver.h"
#include "gui/aiwriterdialog.h"
#include <gui/chat/private/eaiexecutor.h>
#include "dconfigmanager.h"
#include "util.h"
#include "esystemcontext.h"
#include <report/followfunctionpoint.h>
#include <report/eventlogutil.h>

#include <QApplication>
#include <QScreen>
#include <QDBusInterface>
#include <QDBusConnection>
#include <QDesktopServices>
#include <QUrl>
#include <QReadWriteLock>
#include <QTimer>

#include <DDialog>
#include <DLabel>

DWIDGET_USE_NAMESPACE
using namespace uos_ai;

volatile bool WordWizard::kIsFcitxWritable = true;

WordWizard::WordWizard(QObject *parent) : QObject(parent)
{
    QScreen *primaryScreen = QGuiApplication::primaryScreen();
    m_scaleFactor = primaryScreen->devicePixelRatio();

    m_timer = new QTimer(this);
    m_timer->setInterval(100);
    m_timer->setSingleShot(true);

    initWordWizard();
    initConnect();
    FcitxInputServer::getInstance();
}

WordWizard::~WordWizard()
{
    delete m_eventMonitor;
    m_eventMonitor = nullptr;

    disconnect(&FcitxInputServer::getInstance(), &FcitxInputServer::signalFocusIn, this, &WordWizard::onFocusIn);
    disconnect(&FcitxInputServer::getInstance(), &FcitxInputServer::signalFocusOut, this, &WordWizard::onFocusOut);
}

void WordWizard::initWordWizard()
{
    m_selectwid = &WizardWrapper::instance();

    m_selectclip = BaseClipboard::instance();
    m_eventMonitor = BaseMonitor::instance();

    m_writerwid = &AiWriterDialog::instance();

    bool isHidden = DConfigManager::instance()->value(WORDWIZARD_GROUP, WORDWIZARD_ISHIDDEN).toBool();
    m_isHidden = isHidden;
    m_selectwid->isShowHiddenAction(!isHidden);
}

void WordWizard::initConnect()
{
    connect(m_selectclip, &BaseClipboard::selectWords, this, [ = ]{
        if (m_isMousePress) {
            if (m_isMouseRelease && m_timer->isActive()) {
                //先鼠标释放,再剪贴板内容变动
                onShowScribeWord();
                m_timer->stop();
            } else
                m_timer->start();
        }
    });
    connect(m_timer, &QTimer::timeout, this, [ = ]{
        m_isMouseRelease = false;
    });

    connect(m_selectwid, &WizardWrapper::signalWizardBtnClicked, this, &WordWizard::onWizardBtnClicked);
    connect(m_selectwid, &WizardWrapper::signalMoreMenuTriggered, this, &WordWizard::onMoreMenuTriggered);
    connect(m_selectwid, &WizardWrapper::signalRequestServer, this, &WordWizard::onSaveClipText);
    connect(m_selectwid, &WizardWrapper::signalCloseBtnClicked, this, &WordWizard::onCloseBtnClicked);
    connect(m_selectwid, &WizardWrapper::signalIconBtnClicked, this, [&] {
        this->onIconBtnClicked(m_selectclip->getClipText());
        m_selectwid->close();
    });

    connect(m_eventMonitor, SIGNAL(mousePress(int, int)), this, SLOT(onGlobalMousePress(int, int)), Qt::QueuedConnection);
    connect(m_eventMonitor, SIGNAL(mouseRelease(int, int)), this, SLOT(onGlobalMouseRelease(int, int)), Qt::QueuedConnection);
    connect(m_eventMonitor, SIGNAL(keyEscapePress()), this, SLOT(onKeyEscapePress()), Qt::QueuedConnection);

    // 依赖fcitx：当前可写状态
    connect(&FcitxInputServer::getInstance(), &FcitxInputServer::signalFocusIn, this, &WordWizard::onFocusIn);
    connect(&FcitxInputServer::getInstance(), &FcitxInputServer::signalFocusOut, this, &WordWizard::onFocusOut);
}

void WordWizard::onGlobalMousePress(int x, int y)
{
#ifdef COMPILE_ON_V25
    if (ESystemContext::isTreeland()) {
        m_isMousePress = true;

        if (m_selectwid->isWidgetVisible())
            if (m_selectwid->isMouseInside())
                return;
            else {
                m_selectwid->hide();
                m_selectwid->setWidgetVisible(false);
            }

        m_selectclip->clearClipText();
        m_isClose = false;
        return;
    }
#endif
    m_isMousePress = true;
    m_mouseClickX = x / m_scaleFactor;
    m_mouseClickY = y / m_scaleFactor;

    m_cursorPos = QPoint(m_mouseClickX, m_mouseClickY);
    getScreenRect();
    adjustMulScreenPos(m_cursorPos);

    m_mouseClickX = m_cursorPos.x();
    m_mouseClickY = m_cursorPos.y();

    QRect geoRect = m_selectwid->geometry();
    if ((geoRect.contains(m_cursorPos) || m_selectwid->isMouseInside()) && m_selectwid->isVisible()) {
        return;
    }

    if (m_selectwid->isVisible())
        m_selectwid->hide();

    m_selectclip->clearClipText();
    m_isClose = false;
    return;
}

void WordWizard::onGlobalMouseRelease(int x, int y)
{
#ifdef COMPILE_ON_V25
    if (ESystemContext::isTreeland()) {
        //剪贴板先触发，再触发释放（立即释放）
        // !m_isMouseRelease防止滚轮连续触发多次鼠标释放事件
        if (m_timer->isActive() && !m_isMouseRelease) {
            m_timer->stop();
            m_isMouseRelease = true;
            onShowScribeWord();
            return;
        }
        m_timer->start();

        m_isMouseRelease = true;

        if(!m_selectclip->isScribeWordsVisible() || m_selectwid->isWidgetVisible())
            return;

        //1. 剪贴板先触发，再触发释放（延迟释放）2.双击选中
        onShowScribeWord();
        return;
    }
#endif
    m_mouseReleaseX = x / m_scaleFactor;
    m_mouseReleaseY = y / m_scaleFactor;

    m_cursorPos = QPoint(m_mouseReleaseX, m_mouseReleaseY);
    getScreenRect();
    adjustMulScreenPos(m_cursorPos);

    m_mouseReleaseX = m_cursorPos.x();
    m_mouseReleaseY = m_cursorPos.y();

    //剪贴板先触发，再触发释放（立即释放）
    // !m_isMouseRelease防止滚轮连续触发多次鼠标释放事件
    if (m_timer->isActive() && !m_isMouseRelease) {
        m_timer->stop();
        m_isMouseRelease = true;
        onShowScribeWord();
        return;
    }
    m_timer->start();

    m_isMouseRelease = true;


    if(!m_selectclip->isScribeWordsVisible())
        return;

    QRect geoRect = m_selectwid->geometry();
    if(geoRect.contains(m_mouseClickX, m_mouseClickY) || m_selectwid->isVisible())
        return;

    //1. 剪贴板先触发，再触发释放（延迟释放）2.双击选中
    onShowScribeWord();
}

void WordWizard::onKeyEscapePress()
{
    m_isClose = true;
    m_selectwid->close();
    emit m_selectwid->signalEscEvent();
}

void WordWizard::onShowScribeWord()
{
#ifdef COMPILE_ON_V25
    if (ESystemContext::isTreeland()) {
        bool isShow = true;
        if (m_stopShow)
            isShow = false;
        if (!m_isShortcut && m_isHidden)
            isShow = false;

        if (isShow) {
            m_selectwid->setButtonHidden(!m_isShortcut);

            m_Point = QPoint(-1, -1);

            m_selectwid->showScribeWordsAtCursorPosition(m_screenRect, m_Point, m_isMouseRelease, m_isShortcut);
            m_isClose = false;

            m_isMousePress = false;
            m_isMouseRelease = false;
        }
        return;
    }
#endif
    bool isShow = true;
    if (m_stopShow)
        isShow = false;
    if (!m_isShortcut && m_isHidden)
        isShow = false;
    if (m_selectwid->isVisible() || (m_isShortcut && !m_isClose && !m_isHidden))
        isShow = false;

    if (isShow) {
        m_selectwid->setButtonHidden(!m_isShortcut);

        if (m_isShortcut)
            m_Point = QCursor::pos();
        else
            m_Point = QPoint(m_mouseReleaseX, m_mouseReleaseY + 16);

        m_selectwid->showScribeWordsAtCursorPosition(m_screenRect, m_Point, m_isMouseRelease, m_isShortcut);
        m_isClose = false;

        m_isMousePress = false;
        m_isMouseRelease = false;
    }
}

bool WordWizard::launchUosBrowser(QString url) {
    bool ret = false;
    static QString mimetype = "x-scheme-handler/http";
    GList *list = g_app_info_get_all_for_type(mimetype.toStdString().c_str());
    // 遍历列表
    for (GList *l = list; l != nullptr; l = l->next) {
        // 注意：不需要手动释放 GAppInfo 对象，因为它们是由 g_app_info_get_all_for_type 管理的
        GAppInfo *appInfo = G_APP_INFO(l->data);
        const char *APP_PATH = g_app_info_get_executable(appInfo);
        qInfo() << QString("find browser(%1)").arg(APP_PATH);
        // /usr/bin/browser
        if (QString(APP_PATH).endsWith("/browser")) {
            GList *g_files = nullptr;
            GFile *f = g_file_new_for_uri(url.toStdString().c_str());
            g_files = g_list_append(g_files, f);
            GError *gError = nullptr;
            qInfo() << QString("browser %1").arg(url);
            gboolean ok = g_app_info_launch(appInfo, g_files, nullptr, &gError);
            if (gError) {
                qWarning() << "Error when trying to open desktop file with gio: " << gError->message;
                g_error_free(gError);
            }
            if (!ok) {
                qWarning() << "Failed to open desktop file with gio: g_app_info_launch returns false";
            } else {
                ret = true;
            }
            g_list_free_full(g_files, g_object_unref);
            break;
        }

        // 如果是玲珑环境，浏览器被玲珑接管，参数需要直接跟网址（如果玲珑环境安装了多个浏览器，会使用新安装的那个）
        // /usr/bin/ll-cli
        if (QString(APP_PATH).endsWith("/ll-cli")) {
            GList *g_files = nullptr;
            GFile *f = g_file_new_for_uri(url.toStdString().c_str());
            g_files = g_list_append(g_files, f);
            GError *gError = nullptr;
            qInfo() << QString("ll-cli %1").arg(QString("run org.deepin.browser -- browser ") + url);
            gboolean ok = g_app_info_launch(appInfo, g_files, nullptr, &gError);
            if (gError) {
                qWarning() << "Error when trying to open desktop file with gio: " << gError->message;
                g_error_free(gError);
            }
            if (!ok) {
                qWarning() << "Failed to open desktop file with gio: g_app_info_launch returns false";
            } else {
                ret = true;
            }
            g_list_free_full(g_files, g_object_unref);
            break;
        }
    }
    // 释放 GList 列表
    g_list_free_full(list, g_object_unref);

    return ret;
}

bool WordWizard::launchDefaultBrowser(QString url) {
    static QString mimetype = "x-scheme-handler/http";
    GAppInfo *defaultApp = g_app_info_get_default_for_type(mimetype.toLocal8Bit().constData(), FALSE);
    if (!defaultApp) {
        return false;
    }

    bool ret = false;
    GList *g_files = nullptr;
    GFile *f = g_file_new_for_uri(url.toStdString().c_str());
    g_files = g_list_append(g_files, f);
    GError *gError = nullptr;
    qInfo() << QString("browser %1").arg(url);
    gboolean ok = g_app_info_launch(defaultApp, g_files, nullptr, &gError);
    if (gError) {
        qWarning() << "Error when trying to open desktop file with gio: " << gError->message;
        g_error_free(gError);
    }
    if (!ok) {
        qWarning() << "Failed to open desktop file with gio: g_app_info_launch returns false";
    } else {
        ret = true;
    }
    g_list_free_full(g_files, g_object_unref);
    g_object_unref(defaultApp);

    return ret;
}

bool WordWizard::onSearchBtnClicked(const QString &text)
{
    static const int MAX_TEXT_SIZE = 905;
    QString subtext = text;
    subtext.replace("\n", " ");
    if (subtext.size() > MAX_TEXT_SIZE) {
        subtext = subtext.mid(0, MAX_TEXT_SIZE);
    }

    const QString URL = "https://www.sou.com/?q=" + subtext + "&src=360tob_union_add";
    if (!launchUosBrowser(URL)) {
        qInfo() << QString("default browser %1").arg(URL);
        return launchDefaultBrowser(URL);
    }

    return true;
}

void WordWizard::onHiddenActionTriggered()
{
    DDialog dialog("",tr("After hidden, the UOS AI FollowAlong can still be called by pressing Super+Space bar.\nAnd can also be re-enabled in UOS AI settings."));
    dialog.setFixedWidth(380);
    dialog.setIcon(QIcon(":assets/images/tips.svg"));
    dialog.addSpacing(20);
    DLabel imageLabel;
    QString iconName = "uos-ai-assistant_intro";
    if (!Util::checkLanguage())
        iconName = "uos-ai-assistant_intro_en";
    imageLabel.setPixmap(QPixmap(QIcon::fromTheme(iconName).pixmap(198,129)));
    dialog.addContent(&imageLabel, Qt::AlignCenter);
    dialog.addButton(tr("Cancel", "button"), false, DDialog::ButtonNormal);
    dialog.addButton(tr("Hidden", "button"), true, DDialog::ButtonRecommend);
    auto labelList = dialog.findChildren<QLabel *>();
    for (auto messageLabel : labelList) {
        if ("MessageLabel" == messageLabel->objectName())
            messageLabel->setFixedWidth(dialog.width() - 20);
    }
    if (DDialog::Accepted == dialog.exec()) {
        onChangeHiddenStatus(false);
        emit signalHiddenwidget(true);
    }
}

void WordWizard::onChangeXEventMonitorStatus(bool isopen)
{
    if (isopen) {
        m_eventMonitor->start();
        m_stopShow = true;
    } else {
        m_eventMonitor->terminate();
        m_selectwid->close();
        m_stopShow = true;
    }
}

void WordWizard::onMoreMenuTriggered(int wizardtype, const QPoint &cursorPos)
{
    if (wizardtype == WIZARD_TYPE_HIDDEN) {
        onHiddenActionTriggered();
        m_selectwid->close();
        m_selectwid->setWidgetVisible(false);
    }
    else {
        emit sigToLaunchAiQuick(wizardtype, m_clipText, cursorPos);
        m_selectwid->close();
        m_selectwid->setWidgetVisible(false);

        // tid:1001600002 event:followalong_function
        ReportIns()->writeEvent(report::FollowFunctionPoint(wizardtype).assemblingData());
    }
}

void WordWizard::onWizardBtnClicked(int wizardtype, const QPoint &cursorPos)
{
    if (WIZARD_TYPE_SEARCH == wizardtype) {
        if(onSearchBtnClicked(m_selectclip->getClipText())) {
           m_selectwid->close();
           m_selectwid->setWidgetVisible(false);
        }
    }
    else {
        emit sigToLaunchAiQuick(wizardtype, m_clipText, cursorPos);
        m_selectwid->close();
        m_selectwid->setWidgetVisible(false);
    }

    // tid:1001600002 event:followalong_function
    ReportIns()->writeEvent(report::FollowFunctionPoint(wizardtype).assemblingData());
}

void WordWizard::onSaveClipText()
{
    m_clipText = m_selectclip->getClipText();
}

void WordWizard::onShortcutPressed()
{
    m_isShortcut = true;
    onSaveClipText();
    if (!m_clipText.isEmpty())
        onShowScribeWord();
    else if (kIsFcitxWritable) {
        m_writerwid->showDialog();
    }
    m_isShortcut = false;
}

void WordWizard::onCloseBtnClicked()
{
    m_isClose = true;
}

void WordWizard::onIconBtnClicked(QString text)
{
    qInfo() << QString("UOS AI Icon clicked, text(%1)").arg(text);
    // 写作助手，点击Icon只打开主窗口
    // 划词助手，点击Icon打开主窗口并带入文本
    if (text.isEmpty()) {
        EAiExec()->showChatWindow();
    } else {
        emit ServerWrapper::instance()->sigAppendPrompt(text);
    }
}

bool WordWizard::queryHiddenStatus()
{
    return m_isHidden;
}

void WordWizard::onChangeHiddenStatus(bool isShow)
{
    DConfigManager::instance()->setValue(WORDWIZARD_GROUP, WORDWIZARD_ISHIDDEN, !isShow);
    m_isHidden = !isShow;
    m_selectwid->isShowHiddenAction(isShow);
}

void WordWizard::getScreenRect()
{
    QList<QScreen *> screens = QGuiApplication::screens();

    for(QScreen *screen : screens) {
        qreal scaleFactor = screen->devicePixelRatio();
        QRect geometry = screen->geometry();
        QRect tempRect(geometry.x() / scaleFactor, geometry.y() / scaleFactor, geometry.width(), geometry.height());

        int top = tempRect.top() / scaleFactor;
        int m_cursorPosY = m_cursorPos.y() > top ? m_cursorPos.y() : top;
        if (m_cursorPosY - tempRect.bottom() > 0) {
            m_cursorPosY = tempRect.bottom() - (m_cursorPosY - tempRect.bottom());
        }
        if (tempRect.contains(m_cursorPos.x(), m_cursorPosY))
            m_screenRect = screen->geometry();
    }
}

void WordWizard::adjustMulScreenPos(QPoint &pos)
{

    qreal scaleFactor = QGuiApplication::primaryScreen()->devicePixelRatio();

    int x = m_screenRect.topLeft().x();
    int y = m_screenRect.topLeft().y();

    if (pos.x() > x / scaleFactor)
        pos.setX(pos.x() + x * (scaleFactor - 1) / scaleFactor);

    if (pos.y() > y / scaleFactor)
        pos.setX(pos.x() + y * (scaleFactor - 1) / scaleFactor);

}
