import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
//#region extensions/crypto/src/services/forum-topics.ts
/**
* Forum Topics Service — Telegram threaded mode topic management.
*
* When Telegram "Topics" mode is enabled (via BotFather), each topic
* in a group becomes an isolated conversation thread. This service:
*
* 1. Maps well-known topic names to purpose categories
* 2. Routes notifications (heartbeat, cron, alerts) to the right topic
* 3. Maintains topic ID ↔ purpose mapping per chat
* 4. Provides session isolation — each topic gets its own LLM context
*
* Topic IDs are Telegram message_thread_id values. The "General" topic
* has thread_id = undefined (or 1 in some API versions).
*
* State is persisted to disk on shutdown and restored on startup.
*
* Usage:
*   const topics = getForumTopics();
*   topics.registerTopic(chatId, threadId, 'trading');
*   const threadId = topics.getTopicForPurpose(chatId, 'alerts');
*/
const NAME_TO_PURPOSE = {
	"trading": "trading",
	"trades": "trading",
	"swap": "trading",
	"swaps": "trading",
	"defi": "trading",
	"transactions": "trading",
	"portfolio": "portfolio",
	"balances": "portfolio",
	"positions": "portfolio",
	"wallet": "portfolio",
	"holdings": "portfolio",
	"research": "research",
	"analysis": "research",
	"prices": "research",
	"market": "research",
	"charts": "research",
	"intel": "research",
	"alerts": "alerts",
	"notifications": "alerts",
	"heartbeat": "alerts",
	"monitor": "alerts",
	"watchlist": "alerts",
	"governance": "governance",
	"dao": "governance",
	"voting": "governance",
	"proposals": "governance",
	"social": "social",
	"farcaster": "social",
	"twitter": "social",
	"x": "social",
	"admin": "admin",
	"settings": "admin",
	"config": "admin",
	"setup": "admin",
	"bot": "admin"
};
var ForumTopicsService = class {
	chats = /* @__PURE__ */ new Map();
	/**
	* Register a topic for a chat. Auto-maps name to purpose.
	*/
	registerTopic(chatId, threadId, name, purpose) {
		const chat = this.getOrCreateChat(chatId);
		const resolvedPurpose = purpose ?? this.resolvePurpose(name);
		const config = {
			threadId,
			name,
			purpose: resolvedPurpose,
			receivesNotifications: resolvedPurpose === "alerts" || resolvedPurpose === "general"
		};
		chat.topics.set(threadId, config);
		chat.purposeMap.set(resolvedPurpose, threadId);
		chat.forumEnabled = true;
		return config;
	}
	/**
	* Remove a topic registration.
	*/
	unregisterTopic(chatId, threadId) {
		const chat = this.chats.get(chatId);
		if (!chat) return false;
		const config = chat.topics.get(threadId);
		if (!config) return false;
		chat.topics.delete(threadId);
		if (chat.purposeMap.get(config.purpose) === threadId) chat.purposeMap.delete(config.purpose);
		return true;
	}
	/**
	* Get the thread ID for a given purpose in a chat.
	* Returns undefined if no topic is mapped for that purpose.
	*/
	getTopicForPurpose(chatId, purpose) {
		return this.chats.get(chatId)?.purposeMap.get(purpose);
	}
	/**
	* Get the thread ID for routing a notification.
	* Falls back: alerts topic → general topic → undefined.
	*/
	getNotificationTopic(chatId) {
		const chat = this.chats.get(chatId);
		if (!chat?.forumEnabled) return void 0;
		return chat.purposeMap.get("alerts") ?? chat.purposeMap.get("general");
	}
	/**
	* Get the purpose for a specific thread in a chat.
	*/
	getTopicPurpose(chatId, threadId) {
		return this.chats.get(chatId)?.topics.get(threadId)?.purpose;
	}
	/**
	* Generate a session key suffix for a topic, enabling isolated LLM sessions.
	* Returns '-topic-{threadId}' or empty string if no forum mode.
	*/
	getSessionKeySuffix(chatId, threadId) {
		if (!threadId) return "";
		if (!this.chats.get(chatId)?.forumEnabled) return "";
		return `-topic-${threadId}`;
	}
	/**
	* Check if forum mode is enabled for a chat.
	*/
	isForumEnabled(chatId) {
		return this.chats.get(chatId)?.forumEnabled ?? false;
	}
	/**
	* Enable/disable forum mode for a chat.
	*/
	setForumEnabled(chatId, enabled) {
		const chat = this.getOrCreateChat(chatId);
		chat.forumEnabled = enabled;
	}
	/**
	* List all registered topics for a chat.
	*/
	listTopics(chatId) {
		const chat = this.chats.get(chatId);
		if (!chat) return [];
		return [...chat.topics.values()];
	}
	/**
	* List all chats with forum mode enabled.
	*/
	listForumChats() {
		return [...this.chats.entries()].filter(([, chat]) => chat.forumEnabled).map(([chatId]) => chatId);
	}
	/**
	* Get suggested topic structure for a new forum-enabled chat.
	*/
	getSuggestedTopics() {
		return [
			{
				name: "Trading",
				purpose: "trading",
				emoji: "📊"
			},
			{
				name: "Portfolio",
				purpose: "portfolio",
				emoji: "💰"
			},
			{
				name: "Research",
				purpose: "research",
				emoji: "🔍"
			},
			{
				name: "Alerts",
				purpose: "alerts",
				emoji: "🔔"
			},
			{
				name: "Governance",
				purpose: "governance",
				emoji: "🗳️"
			},
			{
				name: "Admin",
				purpose: "admin",
				emoji: "⚙️"
			}
		];
	}
	getOrCreateChat(chatId) {
		let chat = this.chats.get(chatId);
		if (!chat) {
			chat = {
				chatId,
				forumEnabled: false,
				topics: /* @__PURE__ */ new Map(),
				purposeMap: /* @__PURE__ */ new Map()
			};
			this.chats.set(chatId, chat);
		}
		return chat;
	}
	resolvePurpose(name) {
		return NAME_TO_PURPOSE[name.toLowerCase().trim()] ?? "general";
	}
};
let _instance = null;
function getForumTopics() {
	if (!_instance) _instance = new ForumTopicsService();
	return _instance;
}
function resetForumTopics() {
	_instance = null;
}
function getForumStateDir() {
	return process.env.OPENCLAWNCH_TX_DIR ? join(process.env.OPENCLAWNCH_TX_DIR, "..", "forum-topics") : join(process.env.HOME ?? "/tmp", ".openclawnch", "forum-topics");
}
function getForumStatePath() {
	return join(getForumStateDir(), "topics.json");
}
/** Persist all forum topic state to disk. Called on graceful shutdown. */
function persistForumTopics() {
	if (!_instance) return;
	const svc = _instance;
	const chats = [];
	for (const chatId of svc.listForumChats()) {
		const topics = svc.listTopics(chatId);
		if (topics.length > 0) chats.push({
			chatId,
			forumEnabled: true,
			topics: topics.map((t) => ({
				threadId: t.threadId,
				name: t.name,
				purpose: t.purpose,
				receivesNotifications: t.receivesNotifications
			}))
		});
	}
	if (chats.length === 0) return;
	const dir = getForumStateDir();
	if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
	writeFileSync(getForumStatePath(), JSON.stringify(chats, null, 2), "utf8");
}
/** Restore forum topic state from disk. Called on startup. */
function restoreForumTopics() {
	const path = getForumStatePath();
	try {
		if (!existsSync(path)) return;
		const data = JSON.parse(readFileSync(path, "utf8"));
		const svc = getForumTopics();
		for (const chat of data) for (const topic of chat.topics) svc.registerTopic(chat.chatId, topic.threadId, topic.name, topic.purpose);
	} catch {}
}
//#endregion
export { ForumTopicsService, getForumTopics, persistForumTopics, resetForumTopics, restoreForumTopics };

//# sourceMappingURL=forum-topics.mjs.map