import { createLogger } from "../../../src/utils/logger/index.js";
import { init_logger } from "../../../src/utils/logger.js";
import { createRetryRunner, isRecoverableNetworkError } from "../../../src/infra/retry.js";
import { buildSendOptions, parseDataUrl, resolveMediaMethod } from "./send-options.js";
import { splitTelegramCaption } from "./caption.js";
import { markdownToTelegramChunks, renderTelegramHtmlText } from "./format.js";
import { sentMessageCache } from "./sent-cache.js";
import { isTelegramHtmlParseError } from "./errors.js";
import { InputFile } from "grammy";
//#region extensions/telegram/src/outbound-sender.ts
init_logger();
const log = createLogger("TelegramOutboundSender");
/** Maximum message chunk size */
const MESSAGE_CHUNK_SIZE = 3800;
/**
* Create outbound message sender
*
* Note: TTS is handled at the dispatch layer (ChannelManager),
* not here. If a message has TTS audio, it will be passed as
* mediaUrl with audioAsVoice=true.
*/
function createOutboundSender(deps) {
	const { accountManager } = deps;
	async function send(options) {
		const { chatId, content, type = "message", accountId = "default", threadId, replyToMessageId, mediaUrl, mediaType, silent, audioAsVoice } = options;
		log.debug({
			chatId,
			accountId,
			hasContent: !!content,
			hasMediaUrl: !!mediaUrl,
			audioAsVoice,
			mediaType
		}, "Sending outbound message");
		const bot = accountManager.getBot(accountId);
		if (!bot) {
			log.error({ accountId }, "Bot not found");
			return {
				messageId: "",
				chatId,
				success: false,
				error: "Bot not initialized"
			};
		}
		if (type === "typing_on") {
			try {
				await bot.api.sendChatAction(chatId, "typing");
			} catch (err) {
				log.warn({ err }, "Failed to send typing action");
			}
			return {
				messageId: "",
				chatId,
				success: true
			};
		}
		if (type === "typing_off") return {
			messageId: "",
			chatId,
			success: true
		};
		if ((!content || content.trim() === "") && !mediaUrl) {
			log.debug({ chatId }, "Skipping empty message");
			return {
				messageId: "",
				chatId,
				success: true
			};
		}
		try {
			let sentMessageId;
			if (mediaUrl && audioAsVoice) sentMessageId = await sendVoiceMessage(bot, chatId, mediaUrl, content, {
				threadId,
				replyToMessageId,
				silent
			});
			else if (mediaUrl && mediaUrl.startsWith("data:")) sentMessageId = await sendDataUrlMedia(bot, chatId, mediaUrl, content, {
				threadId,
				replyToMessageId,
				silent
			});
			else if (mediaUrl) sentMessageId = await sendRemoteUrlMedia(bot, chatId, mediaUrl, mediaType, content, {
				threadId,
				replyToMessageId,
				silent
			});
			else sentMessageId = await sendTextMessage(bot, chatId, content, {
				threadId,
				replyToMessageId,
				silent
			});
			if (sentMessageId) sentMessageCache.record(chatId, sentMessageId);
			return {
				messageId: String(sentMessageId),
				chatId,
				success: true
			};
		} catch (err) {
			log.error({
				chatId,
				err
			}, "Failed to send message");
			return {
				messageId: "",
				chatId,
				success: false,
				error: err instanceof Error ? err.message : String(err)
			};
		}
	}
	async function sendVoiceMessage(bot, chatId, mediaUrl, content, options) {
		const parsed = parseDataUrl(mediaUrl);
		if (!parsed) throw new Error("Invalid voice message data URL format");
		const { buffer } = parsed;
		const file = new InputFile(buffer, "voice.ogg");
		const caption = content?.trim();
		const htmlCaption = caption ? renderTelegramHtmlText(caption) : void 0;
		const sendOptions = buildSendOptions({
			threadId: options?.threadId,
			replyToMessageId: options?.replyToMessageId,
			silent: options?.silent,
			caption: htmlCaption
		});
		const result = await bot.api.sendVoice(chatId, file, sendOptions);
		log.info({
			chatId,
			messageId: result.message_id
		}, "Voice message sent");
		return result.message_id;
	}
	async function sendDataUrlMedia(bot, chatId, dataUrl, content, options) {
		const parsed = parseDataUrl(dataUrl);
		if (!parsed) throw new Error("Invalid data URL format");
		const { mimeType, buffer } = parsed;
		const file = new InputFile(buffer);
		const { caption, followUpText } = content ? splitTelegramCaption(content) : {
			caption: "",
			followUpText: void 0
		};
		const htmlCaption = caption ? renderTelegramHtmlText(caption) : void 0;
		const sendOptions = buildSendOptions({
			threadId: options?.threadId,
			replyToMessageId: options?.replyToMessageId,
			silent: options?.silent,
			caption: htmlCaption
		});
		const method = resolveMediaMethod(mimeType);
		let result;
		switch (method) {
			case "sendPhoto":
				result = await bot.api.sendPhoto(chatId, file, sendOptions);
				break;
			case "sendVideo":
				result = await bot.api.sendVideo(chatId, file, sendOptions);
				break;
			case "sendAudio":
				result = await bot.api.sendAudio(chatId, file, sendOptions);
				break;
			default: result = await bot.api.sendDocument(chatId, file, sendOptions);
		}
		if (followUpText) {
			const followHtml = renderTelegramHtmlText(followUpText);
			const followResult = await bot.api.sendMessage(chatId, followHtml, {
				parse_mode: "HTML",
				reply_to_message_id: result.message_id
			});
			sentMessageCache.record(chatId, followResult.message_id);
		}
		return result.message_id;
	}
	async function sendRemoteUrlMedia(bot, chatId, mediaUrl, mediaType, content, options) {
		const response = await fetch(mediaUrl);
		if (!response.ok) throw new Error(`Failed to fetch media: ${response.status}`);
		const buffer = await response.arrayBuffer();
		const file = new InputFile(Buffer.from(buffer));
		const { caption, followUpText } = content ? splitTelegramCaption(content) : {
			caption: "",
			followUpText: void 0
		};
		const htmlCaption = caption ? renderTelegramHtmlText(caption) : void 0;
		const sendOptions = buildSendOptions({
			threadId: options?.threadId,
			replyToMessageId: options?.replyToMessageId,
			silent: options?.silent,
			caption: htmlCaption
		});
		let result;
		switch (mediaType) {
			case "photo":
				result = await bot.api.sendPhoto(chatId, file, sendOptions);
				break;
			case "video":
				result = await bot.api.sendVideo(chatId, file, sendOptions);
				break;
			case "audio":
				result = await bot.api.sendAudio(chatId, file, sendOptions);
				break;
			default: result = await bot.api.sendDocument(chatId, file, sendOptions);
		}
		if (followUpText) {
			const followHtml = renderTelegramHtmlText(followUpText);
			await bot.api.sendMessage(chatId, followHtml, {
				parse_mode: "HTML",
				reply_to_message_id: result.message_id
			});
		}
		return result.message_id;
	}
	async function sendTextMessage(bot, chatId, content, options) {
		const chunks = markdownToTelegramChunks(content, MESSAGE_CHUNK_SIZE);
		const retryRunner = createRetryRunner({
			attempts: 3,
			minDelayMs: 300,
			maxDelayMs: 5e3,
			shouldRetry: (err) => isRecoverableNetworkError(err, "send")
		});
		const sendFirstChunk = async () => {
			const sendOptions = buildSendOptions({
				threadId: options?.threadId,
				replyToMessageId: options?.replyToMessageId,
				silent: options?.silent
			});
			try {
				return (await bot.api.sendMessage(chatId, chunks[0].html, {
					...sendOptions,
					parse_mode: "HTML"
				})).message_id;
			} catch (err) {
				if (isTelegramHtmlParseError(err)) {
					log.warn({
						chatId,
						err
					}, "HTML parse error, retrying with plain text");
					return (await bot.api.sendMessage(chatId, chunks[0].text, { reply_to_message_id: options?.replyToMessageId ? parseInt(options.replyToMessageId, 10) : void 0 })).message_id;
				}
				throw err;
			}
		};
		const firstMessageId = await retryRunner(sendFirstChunk, "sendMessage");
		for (let i = 1; i < chunks.length; i++) await retryRunner(async () => {
			await bot.api.sendMessage(chatId, chunks[i].html, {
				parse_mode: "HTML",
				reply_to_message_id: firstMessageId
			});
		}, `sendMessage-reply-${i}`);
		return firstMessageId;
	}
	return { send };
}
//#endregion
export { createOutboundSender };

//# sourceMappingURL=outbound-sender.js.map