/*
  SPDX-FileCopyrightText: 2025-2026 Laurent Montel <montel@kde.org>

  SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "textautogeneratemessage.h"
#include "textautogenerateanswerinfo.h"
#include "textautogeneratemessageutils.h"

#include <QDateTime>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
using namespace Qt::Literals::StringLiterals;
using namespace TextAutoGenerateText;
bool TextAutoGenerateMessage::sAllowInsertText = false;

TextAutoGenerateMessage::TextAutoGenerateMessage() = default;

TextAutoGenerateMessage::~TextAutoGenerateMessage() = default;

QDebug operator<<(QDebug d, const TextAutoGenerateText::TextAutoGenerateMessage &t)
{
    d.space() << "content:" << t.content();
    d.space() << "htmlGenerated:" << t.htmlGenerated();
    d.space() << "sender:" << t.sender();
    d.space() << "dateTime:" << t.dateTime();
    d.space() << "in progress:" << t.inProgress();
    d.space() << "uuid:" << t.uuid();
    d.space() << "answerUuid:" << t.answerUuid();
    d.space() << "mouseHover:" << t.mouseHover();
    d.space() << "editingMode:" << t.editingMode();
    if (t.messageInfo()) {
        d.space() << "message Info:" << *t.messageInfo();
    }
    if (t.messageAttachments()) {
        d.space() << "Message Attachments:" << *t.messageAttachments();
    }

    return d;
}

QString TextAutoGenerateMessage::content() const
{
    return mContent;
}

void TextAutoGenerateMessage::generateHtml(const QString &searchText, int hightLightStringIndex)
{
    int numberOfTextSearchFound = 0;
    mHtmlGenerated =
        TextAutoGenerateMessageUtils::convertTextToHtml(mContent, mUuid, searchText, numberOfTextSearchFound, hightLightStringIndex, sAllowInsertText);
    mNumberOfTextSearched = numberOfTextSearchFound;
}

void TextAutoGenerateMessage::setContent(const QString &newContent)
{
    mContent = newContent;
}

TextAutoGenerateMessage::Sender TextAutoGenerateMessage::sender() const
{
    return mSender;
}

void TextAutoGenerateMessage::setSender(TextAutoGenerateMessage::Sender newSender)
{
    mSender = newSender;
}

qint64 TextAutoGenerateMessage::dateTime() const
{
    return mDateTime;
}

void TextAutoGenerateMessage::setDateTime(qint64 newDateTime)
{
    mDateTime = newDateTime;
    const QLocale locale;
    mDateTimeStr = locale.toString(QDateTime::fromSecsSinceEpoch(mDateTime));
}

bool TextAutoGenerateMessage::isValid() const
{
    return mSender != TextAutoGenerateMessage::Sender::Unknown;
}

bool TextAutoGenerateMessage::inProgress() const
{
    return messageStateValue(MessageState::InProgress);
}

void TextAutoGenerateMessage::setInProgress(bool newInProgress)
{
    assignMessageStateValue(MessageState::InProgress, newInProgress);
}

bool TextAutoGenerateMessage::operator==(const TextAutoGenerateMessage &other) const
{
    bool result = other.uuid() == mUuid && other.inProgress() == inProgress() && other.sender() == mSender && other.dateTime() == mDateTime
        && other.content() == mContent && other.answerUuid() == mAnswerUuid && other.editingMode() == editingMode();
    if (!result) {
        return false;
    }
    if (mInfo != other.info()) {
        return false;
    }
    if (messageInfo() && other.messageInfo()) {
        if (*messageInfo() == (*other.messageInfo())) {
            result = true;
        } else {
            return false;
        }
    } else if (!messageInfo() && !other.messageInfo()) {
        result = true;
    } else {
        return false;
    }
    if (messageAttachments() && other.messageAttachments()) {
        if (*messageAttachments() == (*other.messageAttachments())) {
            result = true;
        } else {
            return false;
        }
    } else if (!messageAttachments() && !other.messageAttachments()) {
        result = true;
    } else {
        return false;
    }

    return result;
}

QByteArray TextAutoGenerateMessage::uuid() const
{
    return mUuid;
}

void TextAutoGenerateMessage::setUuid(const QByteArray &newUuid)
{
    mUuid = newUuid;
}

QString TextAutoGenerateMessage::htmlGenerated() const
{
    return mHtmlGenerated;
}

QByteArray TextAutoGenerateMessage::answerUuid() const
{
    return mAnswerUuid;
}

void TextAutoGenerateMessage::setAnswerUuid(const QByteArray &newAnswerUuid)
{
    mAnswerUuid = newAnswerUuid;
}

QString TextAutoGenerateMessage::dateTimeStr() const
{
    return mDateTimeStr;
}

bool TextAutoGenerateMessage::mouseHover() const
{
    return messageStateValue(MessageState::HoverHighlight);
}

void TextAutoGenerateMessage::setMouseHover(bool newMouseHover)
{
    assignMessageStateValue(MessageState::HoverHighlight, newMouseHover);
}

bool TextAutoGenerateMessage::editingMode() const
{
    return messageStateValue(MessageState::Edited);
}

void TextAutoGenerateMessage::setEditingMode(bool newEditingMode)
{
    assignMessageStateValue(MessageState::Edited, newEditingMode);
}

bool TextAutoGenerateMessage::textToSpeechInProgress() const
{
    return messageStateValue(MessageState::TextToSpeechInProgress);
}

void TextAutoGenerateMessage::setTextToSpeechInProgress(bool newTextToSpeechInProgress)
{
    assignMessageStateValue(MessageState::TextToSpeechInProgress, newTextToSpeechInProgress);
}

QString TextAutoGenerateMessage::modelName() const
{
    if (mMessageInfo) {
        return mMessageInfo->modelName();
    }
    return {};
}

const TextAutoGenerateAnswerInfo *TextAutoGenerateMessage::messageInfo() const
{
    if (mMessageInfo) {
        return mMessageInfo.data();
    }
    return nullptr;
}

QList<QByteArray> TextAutoGenerateMessage::tools() const
{
    if (mMessageInfo) {
        return mMessageInfo->tools();
    }
    return {};
}

QString TextAutoGenerateMessage::instanceName() const
{
    if (mMessageInfo) {
        return mMessageInfo->instanceName();
    }
    return {};
}

QString TextAutoGenerateMessage::engineName() const
{
    if (mMessageInfo) {
        return mMessageInfo->engineName();
    }
    return {};
}

void TextAutoGenerateMessage::setMessageInfo(const TextAutoGenerateAnswerInfo &messageInfo)
{
    if (!mMessageInfo) {
        mMessageInfo = new TextAutoGenerateAnswerInfo(messageInfo);
    } else {
        mMessageInfo.reset(new TextAutoGenerateAnswerInfo(messageInfo));
    }
}

QString TextAutoGenerateMessage::senderToString() const
{
    switch (mSender) {
    case Sender::Unknown:
        return {};
    case Sender::User:
        return u"user"_s;
    case Sender::Assistant:
        return u"llm"_s;
    case Sender::System:
        return u"system"_s;
    case Sender::Tool:
        return u"tool"_s;
    }
    Q_UNREACHABLE();
}

TextAutoGenerateMessage::Sender TextAutoGenerateMessage::senderFromString(const QString &str)
{
    if (str == "user"_L1) {
        return TextAutoGenerateMessage::Sender::User;
    } else if (str == "llm"_L1) {
        return TextAutoGenerateMessage::Sender::Assistant;
    } else if (str == "system"_L1) {
        return TextAutoGenerateMessage::Sender::System;
    } else if (str == "tool"_L1) {
        return TextAutoGenerateMessage::Sender::Tool;
    } else {
        return TextAutoGenerateMessage::Sender::Unknown;
    }
}

QByteArray TextAutoGenerateMessage::serialize(const TextAutoGenerateMessage &msg, bool toBinary)
{
    QJsonDocument d;
    QJsonObject o;
    o["identifier"_L1] = QString::fromLatin1(msg.mUuid);
    if (!msg.mAnswerUuid.isEmpty()) {
        o["answerIdentifier"_L1] = QString::fromLatin1(msg.mAnswerUuid);
    }
    o["text"_L1] = msg.mContent;
    if (msg.sender() != Sender::User) {
        if (auto info = msg.messageInfo(); info) {
            TextAutoGenerateAnswerInfo::serialize(*info, o);
        }
    }
    if (auto att = msg.messageAttachments(); att) {
        const QJsonArray array = TextAutoGenerateAttachments::serialize(*att);
        if (!array.isEmpty()) {
            o["attachments"_L1] = array;
        }
    }
    o["sender"_L1] = msg.senderToString();
    o["dateTime"_L1] = msg.mDateTime;
    const auto msgInfo = msg.info();
    if (msgInfo.isValid()) {
        o["replyInfo"_L1] = TextAutoGenerateText::TextAutoGenerateTextReplyInfo::serialize(msgInfo);
    }

    if (toBinary) {
        return QCborValue::fromJsonValue(o).toCbor();
    }
    d.setObject(o);
    return d.toJson(QJsonDocument::Indented);
}

TextAutoGenerateMessage TextAutoGenerateMessage::deserialize(const QJsonObject &o)
{
    TextAutoGenerateMessage msg;

    msg.setUuid(o["identifier"_L1].toString().toLatin1());
    msg.setAnswerUuid(o["answerIdentifier"_L1].toString().toLatin1());
    msg.setContent(o["text"_L1].toString());
    msg.generateHtml();

    const TextAutoGenerateAnswerInfo *const messageInfoDeserialized = TextAutoGenerateAnswerInfo::deserialize(o);
    if (messageInfoDeserialized->isValid()) {
        msg.setMessageInfo(*messageInfoDeserialized);
    }
    delete messageInfoDeserialized;

    // TODO fix argument
    const TextAutoGenerateAttachments *const attDeserialized = TextAutoGenerateAttachments::deserialize(o["attachments"_L1].toArray(), {});
    if (!attDeserialized->isEmpty()) {
        msg.setMessageAttachments(*attDeserialized);
    }
    delete attDeserialized;
    msg.setDateTime(o["dateTime"_L1].toInteger());
    msg.setSender(senderFromString(o["sender"_L1].toString()));

    msg.setInfo(TextAutoGenerateText::TextAutoGenerateTextReplyInfo::deserialize(o["replyInfo"_L1].toObject()));
    return msg;
}

QJsonObject TextAutoGenerateMessage::convertToOllamaChatJson(bool hasSystemMessageSupport, bool hasTextOnlySupport) const
{
    QJsonObject obj;
    if (mContent.isEmpty()) {
        return obj;
    }
    QString role;
    switch (mSender) {
    case Sender::Unknown:
        break;
    case Sender::User:
        role = u"user"_s;
        break;
    case Sender::Assistant:
        role = u"assistant"_s;
        break;
    case Sender::System:
        if (hasSystemMessageSupport) {
            role = u"system"_s;
        } else {
            role = u"user"_s;
        }
        break;
    case Sender::Tool:
        role = u"tool"_s;
        break;
    }
    if (role.isEmpty()) {
        return obj;
    }
    obj["role"_L1] = role;
    QString contentStr = mContent;
    if (mMessageAttachments && !mMessageAttachments->isEmpty()) {
        const auto attachments = mMessageAttachments->messageAttachments();
        for (const auto &att : attachments) {
            if (att.attachmentType() == TextAutoGenerateText::TextAutoGenerateAttachment::AttachmentType::File) {
                contentStr += QString::fromUtf8(att.content());
            }
        }
    }
    obj["content"_L1] = contentStr;
    if (!hasTextOnlySupport) {
        // TODO add images
        // obj["images"_L1] = //TODO add list;
    }
    return obj;
}

bool TextAutoGenerateMessage::messageStateValue(MessageState type) const
{
    return mMessageStates & type;
}

TextAutoGenerateTextReplyInfo TextAutoGenerateMessage::info() const
{
    return mInfo;
}

void TextAutoGenerateMessage::setInfo(const TextAutoGenerateText::TextAutoGenerateTextReplyInfo &newInfo)
{
    mInfo = newInfo;
}

bool TextAutoGenerateMessage::allowInsertText()
{
    return sAllowInsertText;
}

void TextAutoGenerateMessage::setAllowInsertText(bool newSAllowInsertText)
{
    sAllowInsertText = newSAllowInsertText;
}

int TextAutoGenerateMessage::numberOfTextSearched() const
{
    return mNumberOfTextSearched;
}

const TextAutoGenerateAttachments *TextAutoGenerateMessage::messageAttachments() const
{
    if (mMessageAttachments) {
        return mMessageAttachments.data();
    }
    return nullptr;
}

void TextAutoGenerateMessage::setMessageAttachments(const TextAutoGenerateAttachments &newMessageAttachment)
{
    if (!mMessageAttachments) {
        mMessageAttachments = new TextAutoGenerateAttachments(newMessageAttachment);
    } else {
        mMessageAttachments.reset(new TextAutoGenerateAttachments(newMessageAttachment));
    }
}

TextAutoGenerateMessage::MessageStates TextAutoGenerateMessage::messageStates() const
{
    return mMessageStates;
}

void TextAutoGenerateMessage::setMessageStates(MessageStates newMessageStates)
{
    mMessageStates = newMessageStates;
}

void TextAutoGenerateMessage::assignMessageStateValue(MessageState type, bool status)
{
    if (status) {
        mMessageStates |= type;
    } else {
        mMessageStates &= ~type;
    }
}
#include "moc_textautogeneratemessage.cpp"
