#pragma once

#include <QJsonArray>
#include <QJsonObject>
#include <QList>
#include <QObject>
#include <QString>

#include <functional>

namespace ZShell::llm {

class LlmTool : public QObject {
	Q_OBJECT

	public:
	explicit LlmTool(QObject* parent = nullptr);
	~LlmTool() override;

	[[nodiscard]] virtual QString name() const = 0;
	[[nodiscard]] virtual QString description() const = 0;
	[[nodiscard]] virtual QJsonObject parameters() const = 0;

	// toolCallId identifies the call within the conversation; tools may use
	// it to name files they write for later retrieval.
	virtual void execute(
		const QString& toolCallId,
		const QJsonObject& args,
		std::function<void(const QJsonObject& result)> done) = 0;
	virtual void cancel();

	[[nodiscard]] QJsonObject specification() const;

	// The endpoint's context size in tokens (0 = unknown). Set by LlmClient.
	[[nodiscard]] int contextSize() const { return m_contextSize; }
	void setContextSize(int value);

	// Rough cap, in characters, for tool output embedded in a tool result
	// message. Scales with the context size so a single result cannot
	// consume the whole window.
	[[nodiscard]] int inlineBudgetChars() const;

	// ~4 characters per token; a deliberately coarse estimate used for
	// budgeting (never for exact accounting).
	static constexpr int CharsPerToken = 4;
	// Inline budget when the context size is unknown.
	static constexpr int DefaultInlineChars = 64 * 1024;

	protected:
	int m_contextSize = 0;
};

class ToolRegistry : public QObject {
	Q_OBJECT

	Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)

	public:
	explicit ToolRegistry(QObject* parent = nullptr);

	[[nodiscard]] bool enabled() const { return m_enabled; }
	void setEnabled(bool value);

	void registerTool(LlmTool* tool);
	[[nodiscard]] LlmTool* tool(const QString& name) const;
	[[nodiscard]] QJsonArray specifications() const;
	void cancelAll();

	// Propagated to every registered tool, including later ones.
	void setContextSize(int value);

	Q_SIGNALS:
	void enabledChanged();

	private:
	bool m_enabled = true;
	int m_contextSize = 0;
	QList<LlmTool*> m_tools;
};

} // namespace ZShell::llm
