#include "tool.hpp"

namespace ZShell::llm {

LlmTool::LlmTool(QObject* parent) : QObject(parent) {}

LlmTool::~LlmTool() = default;

void LlmTool::setContextSize(int value) {
	if (m_contextSize == value) return;
	m_contextSize = value;
}

int LlmTool::inlineBudgetChars() const {
	if (m_contextSize <= 0) return DefaultInlineChars;
	// About a quarter of the context window, clamped so tiny contexts
	// still get a usable result and huge ones stay sane.
	const int tokens =
		qBound(512, m_contextSize / 4, DefaultInlineChars / CharsPerToken);
	return tokens * CharsPerToken;
}

void LlmTool::cancel() {}

QJsonObject LlmTool::specification() const {
	QJsonObject function;
	function[QStringLiteral("name")] = name();
	function[QStringLiteral("description")] = description();
	function[QStringLiteral("parameters")] = parameters();
	QJsonObject spec;
	spec[QStringLiteral("type")] = QStringLiteral("function");
	spec[QStringLiteral("function")] = function;
	return spec;
}

ToolRegistry::ToolRegistry(QObject* parent) : QObject(parent) {}

void ToolRegistry::setEnabled(bool value) {
	if (m_enabled == value) return;
	m_enabled = value;
	Q_EMIT enabledChanged();
}

void ToolRegistry::registerTool(LlmTool* tool) {
	if (!tool || m_tools.contains(tool)) return;
	tool->setParent(this);
	tool->setContextSize(m_contextSize);
	m_tools.append(tool);
}

LlmTool* ToolRegistry::tool(const QString& name) const {
	for (const auto* tool : m_tools)
		if (tool->name() == name) return const_cast<LlmTool*>(tool);
	return nullptr;
}

QJsonArray ToolRegistry::specifications() const {
	if (!m_enabled) return {};
	QJsonArray specs;
	for (const auto* tool : m_tools)
		specs.append(tool->specification());
	return specs;
}

void ToolRegistry::cancelAll() {
	for (auto* tool : m_tools)
		tool->cancel();
}

void ToolRegistry::setContextSize(int value) {
	if (m_contextSize == value) return;
	m_contextSize = value;
	for (auto* tool : m_tools)
		tool->setContextSize(value);
}

} // namespace ZShell::llm
