#pragma once

#include "segment.hpp"

#include <QList>
#include <QObject>
#include <QString>
#include <QTimer>
#include <QVariantList>
#include <QtQml>

namespace ZShell::llm {

class LlmClient;

class ChatGeneration : public QObject {
	Q_OBJECT
	QML_ELEMENT
	QML_UNCREATABLE("Chat generations are managed by ChatMessage")

	Q_PROPERTY(
		QString content READ content WRITE setContent NOTIFY contentChanged)
	Q_PROPERTY(QString reasoning READ reasoning NOTIFY reasoningChanged)
	Q_PROPERTY(
		qint64 reasoningElapsedMs READ reasoningElapsedMs NOTIFY
			elapsedMsChanged)
	Q_PROPERTY(
		qint64 contentElapsedMs READ contentElapsedMs NOTIFY elapsedMsChanged)
	Q_PROPERTY(qint64 toolsElapsedMs READ toolsElapsedMs NOTIFY elapsedMsChanged)
	Q_PROPERTY(bool streaming READ streaming NOTIFY streamingChanged)
	Q_PROPERTY(
		bool reasoningActive READ reasoningActive NOTIFY reasoningActiveChanged)
	Q_PROPERTY(bool hasRunningTool READ hasRunningTool NOTIFY toolStateChanged)
	Q_PROPERTY(qint64 timestamp READ timestamp CONSTANT)
	Q_PROPERTY(
		QList<ZShell::llm::LlmSegment*> segments READ segments NOTIFY
			segmentsChanged)
	Q_PROPERTY(int toolCallCount READ toolCallCount NOTIFY segmentsChanged)
	Q_PROPERTY(
		bool toolApprovalPending READ toolApprovalPending NOTIFY
			toolApprovalPendingChanged)
	Q_PROPERTY(
		QVariantList pendingToolCalls READ pendingToolCalls NOTIFY
			toolApprovalPendingChanged)

	public:
	explicit ChatGeneration(qint64 timestamp, QObject* parent = nullptr);

	[[nodiscard]] QString content() const;
	[[nodiscard]] QString reasoning() const;
	[[nodiscard]] bool reasoningActive() const;
	[[nodiscard]] qint64 reasoningElapsedMs() const;
	[[nodiscard]] qint64 contentElapsedMs() const;
	[[nodiscard]] qint64 toolsElapsedMs() const;
	[[nodiscard]] bool streaming() const { return m_streaming; }
	[[nodiscard]] qint64 timestamp() const { return m_timestamp; }
	[[nodiscard]] QList<LlmSegment*> segments() const { return m_segments; }
	[[nodiscard]] int toolCallCount() const;
	[[nodiscard]] bool hasRunningTool() const;
	[[nodiscard]] bool hasPendingTool() const;

	[[nodiscard]] bool toolApprovalPending() const { return m_approvalPending; }
	void setApprovalPending(bool value);
	[[nodiscard]] QVariantList pendingToolCalls() const;

	Q_INVOKABLE void approveTools();
	Q_INVOKABLE void denyTools();

	void setContent(const QString& value);
	void appendContent(const QString& piece);
	void appendReasoning(const QString& piece);
	void setStreaming(bool value);

	[[nodiscard]] LlmSegment* openContentSegment();
	[[nodiscard]] LlmSegment* openReasoningSegment();
	[[nodiscard]] LlmSegment* beginToolCall(
		const QString& name, const QString& toolCallId);
	void addSegment(LlmSegment* segment);
	void closeOpenSegments();

	Q_SIGNALS:
	void contentChanged();
	void reasoningChanged();
	void reasoningActiveChanged();
	void elapsedMsChanged();
	void streamingChanged();
	void toolStateChanged();
	void segmentsChanged();
	void toolApprovalPendingChanged();

	private:
	void updateReasoningActive();
	LlmClient* client() const;

	QTimer m_timer;
	QList<LlmSegment*> m_segments;
	bool m_reasoningActive = false;
	bool m_streaming = false;
	bool m_approvalPending = false;
	qint64 m_timestamp;
};

} // namespace ZShell::llm
