#pragma once

#include "messagemodel.hpp"
#include "message.hpp"

#include <QDateTime>
#include <QObject>
#include <QString>
#include <QtQml>

namespace ZShell::llm {

class LlmClient;

class ChatSession : public QObject {
	Q_OBJECT
	QML_ELEMENT
	QML_UNCREATABLE("Chat sessions are managed by Chat.chats")

	Q_PROPERTY(QString id READ id CONSTANT)
	Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
	Q_PROPERTY(QString icon READ icon NOTIFY iconChanged)
	Q_PROPERTY(QDateTime createdAt READ createdAt CONSTANT)
	Q_PROPERTY(QDateTime updatedAt READ updatedAt NOTIFY updatedAtChanged)
	Q_PROPERTY(bool pinned READ pinned NOTIFY pinnedChanged)
	Q_PROPERTY(bool loaded READ isLoaded NOTIFY loaded)
	Q_PROPERTY(int messageCount READ messageCount NOTIFY messageCountChanged)
	Q_PROPERTY(
		ZShell::llm::ChatMessageModel* messagesModel READ messagesModel CONSTANT)

	public:
	explicit ChatSession(const QString& id, QObject* parent = nullptr);

	[[nodiscard]] QString id() const { return m_id; }
	[[nodiscard]] QString title() const { return m_title; }
	[[nodiscard]] QString icon() const { return m_icon; }
	[[nodiscard]] QDateTime createdAt() const {
		return QDateTime::fromMSecsSinceEpoch(m_createdAt);
	}
	[[nodiscard]] QDateTime updatedAt() const {
		return QDateTime::fromMSecsSinceEpoch(m_updatedAt);
	}
	[[nodiscard]] qint64 createdAtMs() const { return m_createdAt; }
	[[nodiscard]] qint64 updatedAtMs() const { return m_updatedAt; }
	[[nodiscard]] bool pinned() const { return m_pinned; }
	[[nodiscard]] int messageCount() const { return m_messageCount; }
	[[nodiscard]] ChatMessageModel* messagesModel();
	void ensureLoaded();
	[[nodiscard]] bool isLoaded() const { return m_loaded; }
	[[nodiscard]] ChatMessageModel* model() const { return m_model; }
	void markLoaded();
	[[nodiscard]] bool takeClearPending();

	[[nodiscard]] LlmClient* client() const;
	[[nodiscard]] int lastTokenCount() const { return m_lastTokenCount; }
	void setLastTokenCount(int value);

	void setTitle(const QString& value);
	void setIcon(const QString& value);
	void setUpdatedAt(qint64 value);
	void setPinned(bool value);

	void setMeta(
		const QString& title,
		qint64 createdAt,
		qint64 updatedAt,
		int messageCount);

	void adoptMessages(QList<ChatMessage*> messages);
	void persist();
	void removeMessage(ChatMessage* message);
	void clearMessages();

	Q_INVOKABLE void sendMessage(const QString& text);
	Q_INVOKABLE void retry(ZShell::llm::ChatMessage* target);
	Q_INVOKABLE void continueFrom(ZShell::llm::ChatMessage* message);
	Q_INVOKABLE void clear();

	Q_SIGNALS:
	void titleChanged();
	void iconChanged();
	void updatedAtChanged();
	void pinnedChanged();
	void messageCountChanged();
	void loaded();

	private:
	void onModelRowsChanged();
	ChatMessage* appendNewest(
		ChatMessage::Role role, const QString& content, qint64 timestamp);
	void startGeneration(ChatMessage* target);
	void setCount(int value);

	QString m_id;
	QString m_title;
	QString m_icon;
	qint64 m_createdAt = 0;
	qint64 m_updatedAt = 0;
	bool m_pinned = false;
	int m_messageCount = 0;
	ChatMessageModel* m_model = nullptr;
	bool m_loadRequested = false;
	bool m_loaded = false;
	bool m_clearPending = false;
	int m_lastTokenCount = 0;
};

} // namespace ZShell::llm
