#pragma once

#include <QMutex>
#include <QObject>
#include <QString>
#include <QVariantList>
#include <QtQml>

#include <cstdint>
#include <memory>
#include <vector>

class QQmlEngine;
class QJSEngine;

namespace ZShell::llm {

class CodeHighlighter : public QObject {
	Q_OBJECT
	QML_ELEMENT
	QML_SINGLETON

	public:
	Q_INVOKABLE void highlight(
		const QString& code,
		const QString& language,
		QObject* target,
		int token);

	static CodeHighlighter* create(QQmlEngine*, QJSEngine*);

	struct Grammar {
		std::vector<std::string> libs;
		std::vector<std::string> symbols;
		std::vector<const char*> queries;
	};

	private:
	struct State {
		bool bad = false; // permanent failure, do not retry
		void* lib = nullptr;
		const void* lang = nullptr; // const TSLanguage*
		void* query = nullptr;		// TSQuery*
	};

	[[nodiscard]] static const QHash<QString, QString>& aliases();
	[[nodiscard]] static uint8_t roleFor(const char* name, uint32_t length);
	[[nodiscard]] static const char* roleName(uint8_t role);
	[[nodiscard]] static QString resolveId(const QString& language);
	[[nodiscard]] static QString cacheKey(
		const QString& id, const QString& code);
	[[nodiscard]] QVariantList doHighlight(
		const QString& code, const QString& language) const;
	[[nodiscard]] QVariantList lookupSpans(
		const QString& code, const QString& language) const;
	void storeSpans(
		const QString& code,
		const QString& language,
		const QVariantList& spans) const;

	struct SpanCacheEntry {
		QString code; // re-compared on lookup; a hash collision can
					  // never deliver the wrong spans
		QVariantList spans;
	};

	mutable QHash<QString, std::shared_ptr<const State>> m_states;
	mutable QMutex m_stateMutex;
	mutable QHash<QString, SpanCacheEntry> m_spanCache;
	mutable QStringList m_spanCacheOrder; // LRU order, oldest first
	mutable int m_spanCacheBytes = 0;
	mutable QMutex m_cacheMutex;
	static CodeHighlighter* s_instance;
};

} // namespace ZShell::llm
