import { createLogger } from "../../../src/utils/logger/index.js";
import { init_logger } from "../../../src/utils/logger.js";
import { TelegramConfigSchema, init_config_schema } from "./config-schema.js";
import { getWorkflowProgressBroker } from "../../../src/agent/workflow/progress-broker.js";
import "../../../src/agent/workflow/index.js";
import { transcribe } from "../../../src/voice/stt/transcribe-core.js";
import { isSTTAvailable } from "../../../src/voice/stt/availability.js";
import "../../../src/voice/stt/index.js";
import { generateSessionKey } from "../../../src/chat-commands/session-key.js";
import { submitClarifyChoiceFromChannel } from "../../../src/gateway/clarify-runtime.js";
import { resolveStandardPairingPath } from "../../../src/channels/pairing/paths.js";
import { issuePairingChallenge } from "../../../src/channels/pairing/pairing-challenge.js";
import { createStandardPairingAdapter } from "../../../src/channels/pairing/pairing-store-adapter.js";
import { createTimeoutAbortSignal } from "./timeout-abort.js";
import { createInboundDebouncer } from "../../../src/infra/debounce.js";
import { getChatChannelMeta } from "../../../src/channels/registry.js";
import { getMimeType } from "../../../src/channels/media.js";
import { TelegramAccountManager } from "./account-manager.js";
import { createOutboundSender } from "./outbound-sender.js";
import { createTelegramWorkflowProgressCapability } from "./workflow-progress.js";
import { createTelegramCommandHandler } from "./command-handler.js";
import { createInboundProcessor } from "./inbound-processor.js";
import { TELEGRAM_CHANNEL_DEFAULTS } from "./plugin-defaults.js";
import { createTelegramSetupWizard } from "./adapters/setup-wizard.js";
import { TELEGRAM_OUTBOUND_DEFAULTS, createTelegramOutboundSendMethods, telegramTextChunker } from "./adapters/outbound-adapter.js";
import { createTelegramInboundAccessControl } from "./adapters/inbound-access.js";
import { telegramDebouncerKeyPolicy } from "./adapters/debounce-keys.js";
import { createTelegramPluginAdapters } from "./adapters/index.js";
import { createTelegramGatewayAdapter } from "./adapters/gateway-adapter.js";
import { createTelegramStreamingAdapter } from "./adapters/streaming-adapter.js";
import { createTelegramCommandAdapter } from "./adapters/command-adapter.js";
import "./channel.js";
import { normalizeTelegramDeliveryChatId } from "./delivery-chat-id.js";
import { telegramConfigSurface } from "./adapters/config-surface.js";
import { telegramOnboardAdapter } from "./adapters/onboard-cli.js";
import { isDeepStrictEqual } from "node:util";
import { Bot } from "grammy";
import { run } from "@grammyjs/runner";
//#region extensions/telegram/src/plugin.ts
/**
* Telegram Channel Plugin - Implementation based on ChannelPlugin interface
*
* This plugin integrates Telegram with the ChannelPlugin architecture.
* It reuses components from this package's src tree where possible.
*/
init_logger();
init_config_schema();
/** Bound initial `getMe` so a bad `apiRoot` or unreachable API cannot block gateway startup for minutes. */
const TELEGRAM_GETME_TIMEOUT_MS = 2e4;
/** grammY per-request ceiling; must exceed long-poll `getUpdates` (~30s) but avoid multi-minute hangs on bad hosts. */
const TELEGRAM_CLIENT_TIMEOUT_SECONDS = 75;
function trimOptionalRootUrl(value) {
	const t = value?.trim();
	if (!t) return void 0;
	return t.replace(/\/$/, "");
}
const log = createLogger("TelegramPlugin");
const TELEGRAM_REGISTRY_META = getChatChannelMeta("telegram");
var TelegramChannelPlugin = class {
	id = "telegram";
	reload = { configPrefixes: ["channels.telegram"] };
	meta = {
		id: TELEGRAM_REGISTRY_META.id,
		label: TELEGRAM_REGISTRY_META.label,
		selectionLabel: "Telegram Bot",
		docsPath: "/channels/telegram",
		blurb: TELEGRAM_REGISTRY_META.description,
		order: 0,
		deferConnectUntilAfterListen: true
	};
	capabilities = TELEGRAM_REGISTRY_META.capabilities;
	defaults = {
		queue: { debounceMs: TELEGRAM_CHANNEL_DEFAULTS.queue.debounceMs },
		outbound: { textChunkLimit: TELEGRAM_CHANNEL_DEFAULTS.outbound.textChunkLimit }
	};
	setupWizard = createTelegramSetupWizard();
	configSchema = {
		schema: {},
		validate: (raw) => {
			const r = TelegramConfigSchema.safeParse(raw);
			return r.success ? { ok: true } : {
				ok: false,
				errors: [r.error.message]
			};
		}
	};
	cronDelivery = { async normalizeDeliveryTarget(to) {
		return { chatId: normalizeTelegramDeliveryChatId(to) };
	} };
	configSurface = telegramConfigSurface;
	onboard = telegramOnboardAdapter;
	pairing = createStandardPairingAdapter("telegram");
	bus;
	cfg;
	debouncer;
	accountManager;
	outboundSender;
	commandHandler;
	inboundProcessor;
	sessionModelHooks;
	/** Unregister fn for the workflow-progress capability registered against the global broker. */
	workflowProgressUnregister = null;
	config;
	security;
	status;
	outbound;
	gateway;
	streaming;
	commands;
	async init(options) {
		this.bus = options.bus;
		this.cfg = options.config;
		this.sessionModelHooks = options.sessionModel;
		this.accountManager = new TelegramAccountManager();
		this.loadAccounts();
		this.bindOutboundComponents();
		this.workflowProgressUnregister = getWorkflowProgressBroker().registerChannel(createTelegramWorkflowProgressCapability(this.accountManager));
		const debounceMs = this.defaults.queue?.debounceMs ?? TELEGRAM_CHANNEL_DEFAULTS.queue.debounceMs;
		const keyPolicy = telegramDebouncerKeyPolicy();
		this.debouncer = createInboundDebouncer({
			debounceMs,
			...keyPolicy,
			onFlush: async (items) => {
				await this.processMessages(items);
			},
			onError: (err, items) => {
				log.error({
					err,
					count: items.length
				}, "Debounced message processing failed");
			}
		});
		log.debug("Telegram plugin initialized");
	}
	bindOutboundComponents() {
		this.outboundSender = createOutboundSender({
			accountManager: this.accountManager,
			config: this.cfg
		});
		const sm = this.sessionModelHooks;
		this.commandHandler = createTelegramCommandHandler({
			bus: this.bus,
			config: this.cfg,
			accountManager: this.accountManager,
			getSessionModel: (sessionKey) => sm?.getModelForSession(sessionKey),
			setSessionModel: (sessionKey, modelId) => {
				if (!sm) return;
				sm.switchModelForSession(sessionKey, modelId);
			}
		});
		const adapters = createTelegramPluginAdapters({ accountManager: this.accountManager });
		this.config = adapters.config;
		this.security = adapters.security;
		this.status = adapters.status;
		const accessControl = createTelegramInboundAccessControl();
		this.inboundProcessor = createInboundProcessor({
			bus: this.bus,
			config: this.cfg,
			accountManager: this.accountManager,
			accessControl,
			sessionKeyService: { generateSessionKey: (opts) => generateSessionKey({
				source: "telegram",
				chatId: opts.chatId,
				senderId: opts.senderId,
				isGroup: opts.isGroup,
				threadId: opts.threadId,
				accountId: opts.accountId
			}) },
			sttService: {
				transcribe: async (buffer, config, options) => {
					return { text: (await transcribe(buffer, config, options)).text };
				},
				isSTTAvailable: (config) => isSTTAvailable(config)
			},
			mediaUtils: { getMimeType }
		});
		const sends = createTelegramOutboundSendMethods((opts) => this.outboundSender.send(opts));
		this.outbound = {
			deliveryMode: "direct",
			chunker: telegramTextChunker,
			chunkerMode: "text",
			textChunkLimit: TELEGRAM_OUTBOUND_DEFAULTS.textChunkLimit,
			...sends
		};
		this.gateway = createTelegramGatewayAdapter({
			startAccount: (account) => this.startAccount(account),
			stopAccount: (accountId) => this.accountManager.stopRunner(accountId)
		});
		this.streaming = createTelegramStreamingAdapter({ accountManager: this.accountManager });
		this.commands = createTelegramCommandAdapter();
	}
	async onConfigUpdated(cfg) {
		const prevTg = this.cfg.channels?.telegram;
		const nextTg = cfg.channels?.telegram;
		if (!nextTg || nextTg.enabled !== true) {
			this.cfg = cfg;
			await this.stop();
			this.accountManager.reset();
			this.bindOutboundComponents();
			return;
		}
		if (isDeepStrictEqual(prevTg, nextTg)) {
			this.cfg = cfg;
			this.bindOutboundComponents();
			if (!this.channelIsRunning(cfg)) await this.reapplyFromConfig(cfg);
			return;
		}
		await this.reapplyFromConfig(cfg);
	}
	async reapplyFromConfig(cfg) {
		this.cfg = cfg;
		await this.stop();
		this.accountManager.reset();
		const telegramCfg = cfg.channels?.telegram;
		if (!(telegramCfg != null && telegramCfg.enabled === true)) {
			this.bindOutboundComponents();
			return;
		}
		this.loadAccounts();
		this.bindOutboundComponents();
		await this.start();
	}
	loadAccounts() {
		const telegramCfg = this.cfg.channels?.telegram;
		if (!telegramCfg) return;
		if (telegramCfg.enabled !== true) return;
		const accounts = telegramCfg.accounts;
		if (!accounts || Object.keys(accounts).length === 0) return;
		const channelApiRoot = trimOptionalRootUrl(typeof telegramCfg.apiRoot === "string" ? telegramCfg.apiRoot : void 0);
		const channelProxy = typeof telegramCfg.proxy === "string" && telegramCfg.proxy.trim() ? telegramCfg.proxy.trim() : void 0;
		for (const [id, account] of Object.entries(accounts)) {
			const accApiRoot = trimOptionalRootUrl(typeof account.apiRoot === "string" ? account.apiRoot : void 0);
			const accProxy = typeof account.proxy === "string" && account.proxy.trim() ? account.proxy.trim() : void 0;
			this.accountManager.registerAccount({
				...account,
				accountId: id,
				...accApiRoot || channelApiRoot ? { apiRoot: accApiRoot || channelApiRoot } : {},
				...accProxy || channelProxy ? { proxy: accProxy || channelProxy } : {}
			});
		}
	}
	channelIsRunning(cfg) {
		return this.accountManager.getAllAccounts().some((a) => a.enabled !== false && !!a.botToken && this.accountManager.isRunning(a.accountId));
	}
	async start(options) {
		const section = this.cfg.channels?.telegram;
		if (!section || section.enabled !== true) return;
		const accountIds = options?.accountId ? [options.accountId] : this.config.listAccountIds(this.cfg);
		for (const accountId of accountIds) {
			const account = this.config.resolveAccount(this.cfg, accountId);
			if (!account.enabled || !account.botToken) continue;
			await this.startAccount(account);
		}
	}
	async stop(accountId) {
		if (!this.config) return;
		const ids = accountId ? [accountId] : this.config.listAccountIds(this.cfg);
		for (const id of ids) {
			await this.accountManager.stopRunner(id);
			log.info({ accountId: id }, "Telegram account stopped");
		}
		if (!accountId) {
			this.workflowProgressUnregister?.();
			this.workflowProgressUnregister = null;
		}
	}
	async startAccount(account) {
		if (this.accountManager.isRunning(account.accountId)) return;
		if (!account.botToken) return;
		this.accountManager.markStarting(account.accountId);
		try {
			const client = {
				timeoutSeconds: TELEGRAM_CLIENT_TIMEOUT_SECONDS,
				...account.apiRoot ? { apiRoot: account.apiRoot } : {}
			};
			const bot = new Bot(account.botToken, { client });
			const getMeSignal = createTimeoutAbortSignal(TELEGRAM_GETME_TIMEOUT_MS);
			let me;
			try {
				me = await bot.api.getMe(getMeSignal.signal);
			} finally {
				getMeSignal.dispose();
			}
			const runner = run(bot, { runner: {
				fetch: { timeout: 30 },
				silent: true,
				maxRetryTime: Number.POSITIVE_INFINITY,
				retryInterval: "exponential"
			} });
			this.accountManager.registerBot(account.accountId, bot);
			this.accountManager.registerRunner(account.accountId, runner);
			this.attachRunnerExitHandler(account.accountId, runner);
			this.accountManager.setBotUsername(account.accountId, me.username);
			this.accountManager.updateStatus({
				accountId: account.accountId,
				running: true,
				mode: "polling"
			});
			this.setupMessageHandler(account.accountId, bot);
			log.info({
				accountId: account.accountId,
				username: me.username
			}, "Telegram account started");
		} catch (err) {
			const em = err instanceof Error ? err.message : String(err);
			log.warn({
				accountId: account.accountId,
				apiRootConfigured: !!account.apiRoot,
				errorMessage: em
			}, `Telegram account not started (check token, network, or apiRoot): ${em}`);
			this.accountManager.updateStatus({
				accountId: account.accountId,
				running: false,
				mode: "stopped",
				lastError: em.slice(0, 400)
			});
		} finally {
			this.accountManager.markStartComplete(account.accountId);
		}
	}
	attachRunnerExitHandler(accountId, runner) {
		const task = runner.task();
		if (!task) return;
		task.catch((err) => {
			log.error({
				err,
				accountId
			}, "Telegram polling runner exited");
			this.accountManager.stopRunner(accountId).catch((e) => {
				log.error({
					err: e,
					accountId
				}, "Telegram runner cleanup failed");
			});
		});
	}
	setupMessageHandler(accountId, bot) {
		bot.on("message", async (ctx) => {
			try {
				const command = (ctx.message.text ?? ctx.message.caption ?? "").trim().split(" ")[0].split("@")[0].toLowerCase();
				if (command === "/models") {
					await this.commandHandler.handleModels(ctx);
					return;
				}
				if (command === "/start") {
					await this.commandHandler.handleStart(ctx);
					return;
				}
				if (command === "/cleanup") {
					await this.commandHandler.handleCleanup(ctx);
					return;
				}
				await this.debouncer.enqueue({
					ctx,
					accountId,
					message: ctx.message
				});
			} catch (err) {
				log.error({
					accountId,
					err
				}, "Message handler error");
			}
		});
		bot.on("callback_query:data", async (ctx) => {
			try {
				await this.handleCallbackQuery(ctx, accountId);
			} catch (err) {
				log.error({
					accountId,
					err
				}, "Callback query handler error");
				await ctx.answerCallbackQuery("An error occurred").catch(() => {});
			}
		});
	}
	async handleCallbackQuery(ctx, _accountId) {
		const data = ctx.callbackQuery?.data;
		if (!data) return;
		if (data.startsWith("provider:")) {
			const providerId = data.substring(9);
			await this.commandHandler.handleProviderSelect(ctx, providerId);
			return;
		}
		if (data.startsWith("model:")) {
			const modelId = data.substring(6);
			await this.commandHandler.handleModelSelect(ctx, modelId);
			return;
		}
		if (data === "cancel") {
			await this.commandHandler.handleCancel(ctx);
			return;
		}
		if (data === "providers") {
			await this.commandHandler.handleShowProviders(ctx);
			return;
		}
		if (data === "cleanup:confirm") {
			await this.commandHandler.handleCleanupConfirm(ctx);
			return;
		}
		if (data.startsWith("clarify:")) {
			const rest = data.slice(8);
			const lastColon = rest.lastIndexOf(":");
			if (lastColon > 0) {
				const requestId = rest.slice(0, lastColon);
				const idx = Number.parseInt(rest.slice(lastColon + 1), 10);
				if (requestId && Number.isFinite(idx) && submitClarifyChoiceFromChannel(requestId, idx)) {
					await ctx.answerCallbackQuery();
					return;
				}
			}
			await ctx.answerCallbackQuery("No pending question");
			return;
		}
		await ctx.answerCallbackQuery("Unknown action");
	}
	async processMessages(items) {
		if (items.length === 0) return;
		const tgSection = this.cfg.channels?.telegram;
		if (!tgSection || tgSection.enabled !== true) return;
		const last = items[items.length - 1];
		const ctx = last.ctx;
		const accountId = last.accountId;
		if (!this.accountManager.getAccount(accountId)) {
			log.warn({ accountId }, "Unknown account");
			return;
		}
		const account = this.config.resolveAccount(this.cfg, accountId);
		const isGroup = ctx.chat?.type === "group" || ctx.chat?.type === "supergroup";
		const senderId = ctx.from?.id?.toString() ?? "";
		const senderUsername = ctx.from?.username;
		const chatId = ctx.chat?.id?.toString() ?? "";
		const securityCtx = {
			accountId,
			chatId,
			senderId,
			senderName: senderUsername,
			isGroup
		};
		const accessResult = this.security.checkAccess?.(securityCtx, account, this.cfg);
		if (!accessResult?.allowed) {
			if (!isGroup && accessResult?.reason === "pairing-required") {
				const cid = ctx.chat?.id;
				const bot = cid != null ? this.accountManager.getBot(accountId) : void 0;
				if (bot && cid != null && senderId) issuePairingChallenge({
					channel: "telegram",
					pairingFilePath: resolveStandardPairingPath("telegram", accountId),
					accountId,
					senderId,
					senderIdLine: `Your Telegram user id: ${senderId}`,
					sendPairingReply: async (text) => {
						await bot.api.sendMessage(cid, text);
					},
					onCreated: ({ code }) => {
						log.info({
							accountId,
							senderId,
							code
						}, "Telegram DM pairing code issued");
					},
					onReplyError: (err) => {
						log.warn({
							err,
							accountId,
							chatId: String(cid)
						}, "Telegram pairing reply failed");
					}
				});
				return;
			}
			log.warn({
				accountId,
				chatId,
				senderId,
				isGroup,
				reason: accessResult?.reason,
				dmPolicy: account.dmPolicy,
				groupPolicy: account.groupPolicy
			}, "Telegram: message dropped by channel security (check dmPolicy/groupPolicy and allowFrom)");
			return;
		}
		await this.inboundProcessor(ctx, accountId);
	}
};
const telegramPlugin = new TelegramChannelPlugin();
//#endregion
export { TelegramChannelPlugin, telegramPlugin };

//# sourceMappingURL=plugin.js.map