import { createLogger } from "../../../../src/utils/logger/index.js";
import { init_logger } from "../../../../src/utils/logger.js";
import { resolveFeishuAccount } from "../state/accounts.js";
import { createFeishuClient } from "../transport/client/client.js";
import { getFeishuBindingByMessageId, recordFeishuMessageBinding } from "../state/message-bindings.js";
import { formatFeishuOutboundText } from "../format.js";
import { randomUUID } from "node:crypto";
//#region extensions/feishu/src/streaming/streaming-adapter.ts
init_logger();
const log = createLogger("FeishuStreaming");
/** Normalize CardKit `card.create` responses (SDK / OpenAPI envelope shapes differ). */
function extractFeishuCardKitCreateCardId(raw) {
	if (!raw || typeof raw !== "object") return void 0;
	const o = raw;
	const tryId = (v) => {
		if (typeof v !== "string") return void 0;
		const t = v.trim();
		return t.length > 0 ? t : void 0;
	};
	const fromData = (data) => {
		if (!data || typeof data !== "object") return void 0;
		const d = data;
		return tryId(d.card_id) ?? tryId(d.cardId);
	};
	return fromData(o.data) ?? (o.data && typeof o.data === "object" ? fromData(o.data.data) : void 0) ?? tryId(o.card_id);
}
function feishuOpenApiOk(res) {
	if (res === null || res === void 0) return false;
	if (typeof res !== "object") return true;
	const code = res.code;
	if (code === void 0 || code === null) return true;
	return Number(code) === 0;
}
function createFeishuStreamingAdapter(getConfig) {
	return { startStream(options) {
		const account = resolveFeishuAccount(getConfig(), options.accountId ?? "default");
		if (!account.configured) return null;
		if (account.streaming !== true) return null;
		const { api } = createFeishuClient(account);
		let messageId;
		let lastText = "";
		let timer = null;
		let aborted = false;
		let editedAtLeastOnce = false;
		let ready = null;
		let cardId;
		let cardSeq = 0;
		const cardElementId = `md_${randomUUID().replace(/-/g, "")}`;
		let fallbackSent = false;
		const renderMode = account.renderMode ?? "auto";
		const preferCard = renderMode === "card" || renderMode === "auto";
		const formatStreamText = (text) => {
			if (!text.trim()) return text;
			if (text.trim() === "Thinking…") return text;
			return formatFeishuOutboundText({
				text,
				renderMode,
				forCardMarkdown: Boolean(preferCard && cardId)
			});
		};
		const recordBindingIfReply = (childMessageId) => {
			if (!childMessageId) return;
			const parentId = options.replyToMessageId;
			if (!parentId) return;
			const parent = getFeishuBindingByMessageId(parentId);
			if (!parent) return;
			recordFeishuMessageBinding({
				...parent,
				messageId: childMessageId
			});
		};
		const edit = async (text) => {
			if (ready) await ready;
			if (!messageId) return;
			const outbound = formatStreamText(text);
			if (cardId && preferCard) {
				cardSeq += 1;
				await api.cardkit.v1.cardElement.content({
					path: {
						card_id: cardId,
						element_id: cardElementId
					},
					data: {
						content: outbound,
						sequence: cardSeq,
						uuid: `${cardId}:${cardSeq}`
					}
				});
			} else await api.im.v1.message.update({
				path: { message_id: messageId },
				data: {
					msg_type: "text",
					content: JSON.stringify({ text: outbound })
				}
			});
			editedAtLeastOnce = true;
		};
		const sendFallbackText = async (text) => {
			if (fallbackSent) return;
			fallbackSent = true;
			const receive_id_type = options.chatId.startsWith("ou_") || options.chatId.startsWith("on_") ? "open_id" : "chat_id";
			try {
				const outbound = formatStreamText(text);
				const res = options.replyToMessageId ? await api.im.message.reply({
					path: { message_id: options.replyToMessageId },
					data: {
						msg_type: "text",
						content: JSON.stringify({ text: outbound }),
						...options.threadId ? { reply_in_thread: true } : {}
					}
				}) : await api.im.message.create({
					params: { receive_id_type },
					data: {
						receive_id: options.chatId,
						msg_type: "text",
						content: JSON.stringify({ text: outbound })
					}
				});
				const mid = res?.data?.message_id ?? res?.message_id ?? void 0;
				if (mid) recordBindingIfReply(mid);
				editedAtLeastOnce = true;
				messageId = mid ?? messageId;
				cardId = void 0;
			} catch (err) {
				log.warn({
					err,
					accountId: account.accountId
				}, "Feishu streaming fallback send failed");
			}
		};
		const flush = async () => {
			if (aborted) return;
			if (ready) await ready;
			if (!messageId) return;
			const text = lastText;
			if (!text.trim()) return;
			try {
				await edit(text);
			} catch (err) {
				log.warn({
					err,
					accountId: account.accountId,
					messageId,
					mode: preferCard ? "card" : "text"
				}, "Feishu streaming edit failed");
				await sendFallbackText(text);
			}
		};
		const start = async () => {
			const receive_id_type = options.chatId.startsWith("ou_") || options.chatId.startsWith("on_") ? "open_id" : "chat_id";
			const thinking = "Thinking…";
			if (preferCard) {
				const cardSpec = {
					schema: "2.0",
					config: { update_multi: true },
					header: { title: {
						tag: "plain_text",
						content: "xopc"
					} },
					body: { elements: [{
						tag: "markdown",
						element_id: cardElementId,
						content: thinking
					}] }
				};
				let created;
				try {
					created = await api.cardkit.v1.card.create({ data: {
						type: "card_json",
						data: JSON.stringify(cardSpec)
					} });
				} catch (err) {
					log.warn({
						err,
						accountId: account.accountId
					}, "Feishu cardkit.card.create threw; falling back to text stream");
					created = null;
				}
				const apiOk = feishuOpenApiOk(created);
				cardId = apiOk ? extractFeishuCardKitCreateCardId(created) : void 0;
				if (!apiOk) log.warn({
					accountId: account.accountId,
					code: created?.code,
					msg: created?.msg
				}, "Feishu cardkit.card.create returned non-zero code; falling back to text stream");
				else if (!cardId) log.warn({
					accountId: account.accountId,
					responsePreview: JSON.stringify(created).slice(0, 400)
				}, "Feishu cardkit.card.create returned no card_id; falling back to text stream");
				if (cardId) {
					const res = options.replyToMessageId ? await api.im.message.reply({
						path: { message_id: options.replyToMessageId },
						data: {
							msg_type: "interactive",
							content: JSON.stringify({
								type: "card",
								data: { card_id: cardId }
							}),
							...options.threadId ? { reply_in_thread: true } : {}
						}
					}) : await api.im.message.create({
						params: { receive_id_type },
						data: {
							receive_id: options.chatId,
							msg_type: "interactive",
							content: JSON.stringify({
								type: "card",
								data: { card_id: cardId }
							})
						}
					});
					messageId = res?.data?.message_id ?? res?.message_id ?? void 0;
					if (messageId) recordBindingIfReply(messageId);
					log.info({
						accountId: account.accountId,
						chatId: options.chatId,
						messageId,
						mode: "card"
					}, "Feishu streaming started");
					return;
				}
				cardId = void 0;
			}
			const res = options.replyToMessageId ? await api.im.message.reply({
				path: { message_id: options.replyToMessageId },
				data: {
					msg_type: "text",
					content: JSON.stringify({ text: thinking }),
					...options.threadId ? { reply_in_thread: true } : {}
				}
			}) : await api.im.message.create({
				params: { receive_id_type },
				data: {
					receive_id: options.chatId,
					msg_type: "text",
					content: JSON.stringify({ text: thinking })
				}
			});
			messageId = res?.data?.message_id ?? res?.message_id ?? void 0;
			if (messageId) recordBindingIfReply(messageId);
			log.info({
				accountId: account.accountId,
				chatId: options.chatId,
				messageId,
				mode: "text"
			}, "Feishu streaming started");
		};
		ready = start().catch((err) => {
			log.warn({
				err,
				accountId: account.accountId
			}, "Feishu streaming start failed");
		});
		return {
			update: (text) => {
				lastText = text;
				if (timer) clearTimeout(timer);
				timer = setTimeout(() => {
					flush().catch((err) => log.warn({ err }, "Feishu streaming flush failed"));
				}, 800);
			},
			end: async () => {
				if (timer) clearTimeout(timer);
				await flush();
				log.debug({
					accountId: account.accountId,
					chatId: options.chatId,
					messageId,
					editedAtLeastOnce,
					lastTextLen: lastText.length,
					mode: preferCard ? "card" : "text"
				}, "Feishu streaming ended");
			},
			abort: async () => {
				aborted = true;
				if (timer) clearTimeout(timer);
			},
			messageId: () => void 0,
			skipFinalOutbound: () => Boolean(messageId) && editedAtLeastOnce,
			updateProgress: void 0,
			setProgress: void 0
		};
	} };
}
//#endregion
export { createFeishuStreamingAdapter };

//# sourceMappingURL=streaming-adapter.js.map