#include "application.h"
#include "serverwrapper.h"
#include "llmutils.h"
#include "functionhandler.h"
#include "chatwindow.h"
#include "localmodelserver.h"
#include "wordwizard/gui/aiquickdialog.h"
#include "wordwizard/gui/aiwriterdialog.h"
#include "wordwizard/wordwizard.h"
#include "dbwrapper.h"
#include "private/welcomedialog.h"
#include "esystemcontext.h"

#include <QtDBus>
#include <QCommandLineParser>

DWIDGET_USE_NAMESPACE
UOSAI_USE_NAMESPACE

constexpr char APP_NAME_KEY[]   =   "uos-ai-assistant";

static const QList<QCommandLineOption> options = {
    {
        {"c", "chat"},
        "ChatWindow Auto Display"
    }, {
        {"f", "functions"},
        "print function calling list"
    },
    {
        {"t", "talk"},
        "ChatWindow for talk Auto Display"
    },
    {
        {"w", "wordwizard"},
        "Display Scratch toolbar when hidden"
    }
};

Application::Application(int &argc, char **argv)
    : DApplication(argc, argv)
{
    setApplicationName(APP_NAME_KEY);
    setObjectName(APP_NAME_KEY);
    setOrganizationName("deepin");
    loadTranslator();
    setApplicationDisplayName(tr("UOS AI"));
    setApplicationVersion(DApplication::buildVersion(APP_VERSION));
    setProductName(tr("UOS AI"));
    setProductIcon(QIcon::fromTheme("uos-ai-assistant"));
    setApplicationDescription(tr("UOS AI is a desktop smart assistant, your personal assistant! You can communicate with it using text or voice, and it can help answer questions, provide information, and generate images based on your descriptions."));
    setAttribute(Qt::AA_UseHighDpiPixmaps);
    setQuitOnLastWindowClosed(false);
    setWindowIcon(QIcon::fromTheme("uos-ai-assistant"));


    connect(ServerWrapper::instance(), &ServerWrapper::sigToLaunchMgmt, this, &Application::onLaunchMgmt);
    connect(ServerWrapper::instance(), &ServerWrapper::sigToLaunchChat, this, &Application::onLaunchChat);
    connect(ServerWrapper::instance(), &ServerWrapper::sigToLaunchAbout, this, &Application::onLaunchAbout);
    connect(ServerWrapper::instance(), &ServerWrapper::sigToLaunchWordWizard, this, &Application::onLaunchWordWizard);
    connect(ServerWrapper::instance(), &ServerWrapper::sigInputPrompt, this, &Application::onInputPrompt);
    connect(ServerWrapper::instance(), &ServerWrapper::sigAppendPrompt, this, &Application::onAppendPrompt);
    connect(ServerWrapper::instance(), &ServerWrapper::sigAddKnowledgeBase, this, &Application::onAddKnowledgeBasefile);
    connect(&LocalModelServer::getInstance(), &LocalModelServer::sigToLaunchMgmtNoShow, this, &Application::onLaunchMgmtNoShow);
}

Application::~Application()
{
    if (m_mgmtWindow)
        m_mgmtWindow->deleteLater();

    if (m_chatWindow)
        m_chatWindow->deleteLater();
}

void Application::initialization()
{
    ServerWrapper::instance()->initialization();

    setAttribute(Qt::AA_UseHighDpiPixmaps);
    initShortcut();
    handleArgumentsParser(arguments());
}

void Application::initShortcut()
{
    //v25 treeland环境下不注册快捷键
    if (ESystemContext::isTreeland())
        return;

    QDBusInterface shortcutDbus("com.deepin.daemon.Keybinding", "/com/deepin/daemon/Keybinding", "com.deepin.daemon.Keybinding", QDBusConnection::sessionBus());

    QString wordwizardShortcut = tr("UOS AI FollowAlong/Write");

    QDBusMessage msg = shortcutDbus.call("SearchShortcuts", "UOS AI");
    QString info = msg.arguments().at(0).toString();
    QJsonDocument jsonDoc = QJsonDocument::fromJson(info.toUtf8());
    QJsonArray jsonArray = jsonDoc.array();

    for (const QJsonValue &value : jsonArray) {
        QJsonObject jsonObj = value.toObject();
        QString id = jsonObj.value("Id").toString();
        QString accel = jsonObj.value("Accels").toArray().at(0).toString();
        QString name = jsonObj.value("Name").toString();
        QString exec = jsonObj.value("Exec").toString();
        //UOS AI快捷键更换
        if (id == "UOS AI") {
            if (id == name && accel == "<Super>C")
                shortcutDbus.call("ModifyCustomShortcut", id, name, exec, "<Super>A");
        } else if (id == "UOS AI Talk") {
            if (id == name && accel == "<Control><Super>C")
                shortcutDbus.call("ModifyCustomShortcut", id, name, exec, "<Control><Super>A");
        } else {
            //划词写作快捷键中英文切换
            if (id != wordwizardShortcut && id == name)
                shortcutDbus.call("DeleteCustomShortcut", id);
        }
    }

    shortcutDbus.call("AddCustomShortcut", wordwizardShortcut, "/usr/bin/uos-ai-assistant --wordwizard", "<Super><Space>");
    shortcutDbus.call("AddCustomShortcut", "UOS AI", "/usr/bin/uos-ai-assistant --chat", "<Super>A");
    shortcutDbus.call("AddCustomShortcut", "UOS AI Talk", "/usr/bin/uos-ai-assistant --talk", "<Control><Super>A");
}

void Application::launchMgmtWindow(bool showAddllmPage, bool onlyUseAgreement, bool isFromAiQuick, const QString &locateTitle)
{
    if (!checkAgreement())
        return;

    launchMgmtWindowNoShow();
    m_mgmtWindow->showEx(showAddllmPage, onlyUseAgreement, isFromAiQuick, locateTitle);
}

void Application::launchMgmtWindowNoShow()
{
    if (!m_mgmtWindow) {
        m_mgmtWindow = new MgmtWindow();
        connect(m_mgmtWindow, &MgmtWindow::sigGenPersonalFAQ, this, &Application::sigGenPersonalFAQ, Qt::UniqueConnection);
        if (m_wordWizard) {
            bool isHidden = m_wordWizard->queryHiddenStatus();
            m_mgmtWindow->onWordWizardHiddenStatus(isHidden);
            connect(m_wordWizard, &WordWizard::signalHiddenwidget, m_mgmtWindow, &MgmtWindow::onWordWizardHiddenStatus, Qt::UniqueConnection) ;
            connect(m_mgmtWindow, &MgmtWindow::signalWordWizardStatusChanged, m_wordWizard, &WordWizard::onChangeHiddenStatus);
        }
    }
}

void Application::launchChatWindow(int index)
{
    if (!checkAgreement())
        return;

    if (!m_chatWindow) {
        m_chatWindow = ESystemContext::createWebWindow<ChatWindow>();
        connect(this, &Application::sigGenPersonalFAQ, m_chatWindow, &ChatWindow::onGenPersonalFAQ, Qt::UniqueConnection);
        connect(m_chatWindow, &ChatWindow::sigToAddKnowledgeBase, this, [this](const QStringList &knowledgeBasefile){
            this->launchMgmtWindowNoShow();
            this->m_mgmtWindow->showEx(false, false, false, tr("Knowledge Base Management"));
            this->m_mgmtWindow->onAddKnowledgeBase(knowledgeBasefile);
        });
    }

    m_chatWindow->showWindow(static_cast<ChatIndex>(index));
}

void Application::launchAboutWindow()
{
    if (!m_chatWindow) return;

    m_chatWindow->showAboutWindow();
}

void Application::launchWordWizard()
{
    // 设置窗口存在时，置顶设置窗口，而不是打开写作助手，避免写作助手打开了被遮挡又关不掉
    if (m_mgmtWindow && m_mgmtWindow->isVisible()) {
        m_mgmtWindow->activateWindow();
        return;
    }

    if (!m_wordWizard) return;

    m_wordWizard->onShortcutPressed();
}

void Application::launchAiQuickDialog(int type, QString query, QPoint pos)
{
    AiQuickDialog *aiQuickDlg = new AiQuickDialog();
    aiQuickDlg->setQuery(type, query, pos);
    aiQuickDlg->show();
}

void Application::launchAiWriterDialog()
{
    AiWriterDialog::instance().show();
}

void Application::initWordWizard(WordWizard *wizard)
{
    m_wordWizard = wizard;
    if (m_wordWizard)
        connect(m_wordWizard, &WordWizard::sigToLaunchAiQuick, this, &Application::onLaunchAiQuick, Qt::UniqueConnection) ;
}

int Application::handleExistingArgument(int argc, char *argv[])
{
    QStringList arguments;
    for (int i = 0; i < argc; ++i) {
        arguments << argv[i];
    }

    QCommandLineParser parser;
    parser.addOptions(options);
    parser.parse(arguments);

    if (parser.isSet("chat")) {
        qInfo() << "Detects that an application already exists and will notify it of its launch chat window for text";
        QDBusInterface notification(DBUS_SERVER, DBUS_SERVER_PATH, DBUS_SERVER_INTERFACE, QDBusConnection::sessionBus());
        QString error = notification.call(QDBus::Block, "launchChatPage", ChatIndex::Text).errorMessage();
        if (!error.isEmpty()) {
            qCritical() << error;
            return -1;
        }
    } else if (parser.isSet("talk")) {
        qInfo() << "Detects that an application already exists and will notify it of its launch chat window for talk";
        QDBusInterface notification(DBUS_SERVER, DBUS_SERVER_PATH, DBUS_SERVER_INTERFACE, QDBusConnection::sessionBus());
        QString error = notification.call(QDBus::Block, "launchChatPage", ChatIndex::Talk).errorMessage();
        if (!error.isEmpty()) {
            qCritical() << error;
            return -1;
        }
    } else if (parser.isSet("wordwizard")) {
        QDBusInterface notification(DBUS_SERVER, DBUS_SERVER_PATH, DBUS_SERVER_INTERFACE, QDBusConnection::sessionBus());
        QString error = notification.call(QDBus::Block, "launchWordWizard").errorMessage();
        if (!error.isEmpty()) {
            qCritical() << error;
            return -1;
        }
    }

    return 0;
}

bool Application::handleWordWizardArgument(int argc, char *argv[])
{
    QStringList arguments;
    for (int i = 0; i < argc; ++i) {
        arguments << argv[i];
    }

    QCommandLineParser parser;
    parser.addOptions(options);
    parser.parse(arguments);

    return parser.isSet("wordwizard");
}

void Application::onLaunchMgmt(bool showAddllmPage, bool onlyUseAgreement, bool isFromAiQuick, const QString &locateTitle)
{
    launchMgmtWindow(showAddllmPage, onlyUseAgreement, isFromAiQuick, locateTitle);
}

void Application::onLaunchMgmtNoShow()
{
    launchMgmtWindowNoShow();
}

void Application::onLaunchChat(int index)
{
    launchChatWindow(index);
}

void Application::onLaunchAbout()
{
    launchAboutWindow();
}

void Application::onLaunchWordWizard()
{
    launchWordWizard();
}

void Application::onLaunchAiQuick(int type, QString query, QPoint pos)
{
    launchAiQuickDialog(type, query, pos);
}

void Application::onLaunchAiWriter()
{
    launchAiWriterDialog();
}

void Application::onInputPrompt(const QString &question, const QMap<QString, QString> &params)
{
    if (QThread::currentThread() != qApp->thread()) {
        qWarning() << "called thread is not main thread. do not show WelcomeDialog.";
        return;
    }

    if (!WelcomeDialog::isAgreed()) {
        WelcomeDialog::instance(false)->exec();
        return;
    }

    // 避免：窗口模式下，连续点击划词icon使得主窗口隐藏
    if (m_chatWindow == nullptr || !m_chatWindow->isActiveWindow()) {
        launchChatWindow(ChatIndex::Text);
        m_chatWindow->show();
    }
    m_chatWindow->overrideQuestion(question, params);
}

void Application::onAppendPrompt(const QString &question)
{
    if (QThread::currentThread() != qApp->thread()) {
        qWarning() << "called thread is not main thread. do not show WelcomeDialog.";
        return;
    }

    // 避免：窗口模式下，连续点击划词icon使得主窗口隐藏
    if (m_chatWindow == nullptr || !m_chatWindow->isActiveWindow()) {
        launchChatWindow(ChatIndex::Text);
        m_chatWindow->show();
    }
    m_chatWindow->appendQuestion(question);
}

bool Application::checkAgreement()
{
    if (WelcomeDialog::isAgreed())
        return true;

    if (QThread::currentThread() != qApp->thread()) {
        qWarning() << "called thread is not main thread. do not show WelcomeDialog.";
        return false;
    }

    WelcomeDialog::instance(false)->exec();
    return WelcomeDialog::isAgreed();
}

void Application::handleArgumentsParser(const QStringList &arguments)
{
    QCommandLineParser parser;
    parser.addOptions(options);
    parser.parse(arguments);

    if (parser.isSet("functions")) {
        bool notYetQueried = true;
        qInfo() << FunctionHandler::queryAppFunctions(notYetQueried);
    }

    if (parser.isSet("chat")) {
        launchChatWindow(ChatIndex::Text);
    } else if (parser.isSet("talk")) {
        launchChatWindow(ChatIndex::Talk);
    }
}

void Application::onAddKnowledgeBasefile(const QStringList &knowledgeBasefile)
{
    if (QThread::currentThread() != qApp->thread()) {
        qWarning() << "called thread is not main thread. do not show WelcomeDialog.";
        return;
    }

    if (!WelcomeDialog::isAgreed()) {
        WelcomeDialog::instance(false)->exec();
        return;
    }

    if (m_chatWindow == nullptr || !m_chatWindow->isActiveWindow()) {
        launchChatWindow(ChatIndex::Text);
        m_chatWindow->show();
    }
    m_chatWindow->addKnowledgeBase(knowledgeBasefile);
}
