import { a as normalizeLowercaseStringOrEmpty } from "./string-coerce-DKw2K5wM.js";
import { c as readErrorName, i as formatErrorMessage$1, r as extractErrorCode, t as collectErrorGraphCandidates } from "./errors-Cq_Pdh35.js";
import "./json-store-DWk93JH-.js";
import "./error-runtime-zzHx3voD.js";
import { r as makeProxyFetch } from "./proxy-fetch-Dh9myJ9s.js";
import "./number-runtime-CK87cK6O.js";
import "./string-coerce-runtime-_0MoDMYs.js";
import { i as normalizeTelegramApiRoot, r as resolveTelegramTransport } from "./fetch-DARO6Fsh.js";
import "./state-paths-y37W03wb.js";
import path from "node:path";
import fs from "node:fs/promises";
import "node:os";
import { randomUUID } from "node:crypto";
import { parentPort, workerData } from "node:worker_threads";
import { API_CONSTANTS } from "grammy";
//#region extensions/telegram/src/allowed-updates.ts
const DEFAULT_TELEGRAM_UPDATE_TYPES = API_CONSTANTS.DEFAULT_UPDATE_TYPES;
function resolveTelegramAllowedUpdates() {
	const updates = [...DEFAULT_TELEGRAM_UPDATE_TYPES];
	if (!updates.includes("message_reaction")) updates.push("message_reaction");
	if (!updates.includes("channel_post")) updates.push("channel_post");
	return updates;
}
//#endregion
//#region extensions/telegram/src/network-errors.ts
const RECOVERABLE_ERROR_CODES = new Set([
	"ECONNRESET",
	"ECONNREFUSED",
	"EPIPE",
	"ENETDOWN",
	"ETIMEDOUT",
	"ESOCKETTIMEDOUT",
	"ENETUNREACH",
	"EHOSTUNREACH",
	"ENOTFOUND",
	"EAI_AGAIN",
	"UND_ERR_CONNECT_TIMEOUT",
	"UND_ERR_HEADERS_TIMEOUT",
	"UND_ERR_BODY_TIMEOUT",
	"UND_ERR_SOCKET",
	"UND_ERR_ABORTED",
	"ECONNABORTED",
	"ERR_NETWORK"
]);
const RECOVERABLE_ERROR_NAMES = new Set([
	"AbortError",
	"TimeoutError",
	"ConnectTimeoutError",
	"HeadersTimeoutError",
	"BodyTimeoutError"
]);
const ALWAYS_RECOVERABLE_MESSAGES = new Set(["fetch failed", "typeerror: fetch failed"]);
const GRAMMY_NETWORK_REQUEST_FAILED_AFTER_RE = /^network request(?:\s+for\s+["']?[^"']+["']?)?\s+failed\s+after\b.*[!.]?$/i;
const RECOVERABLE_MESSAGE_SNIPPETS = [
	"undici",
	"network error",
	"network request",
	"client network socket disconnected",
	"socket hang up",
	"getaddrinfo",
	"timeout",
	"timed out"
];
function collectTelegramErrorCandidates(err) {
	return collectErrorGraphCandidates(err, (current) => {
		const nested = [current.cause, current.reason];
		if (Array.isArray(current.errors)) nested.push(...current.errors);
		if (readErrorName(current) === "HttpError") nested.push(current.error);
		return nested;
	});
}
function normalizeCode(code) {
	return code?.trim().toUpperCase() ?? "";
}
function getErrorCode(err) {
	const direct = extractErrorCode(err);
	if (direct) return direct;
	if (!err || typeof err !== "object") return;
	const errno = err.errno;
	if (typeof errno === "string") return errno;
	if (typeof errno === "number") return String(errno);
}
function isRecoverableTelegramNetworkError(err, options = {}) {
	if (!err) return false;
	const allowMessageMatch = typeof options.allowMessageMatch === "boolean" ? options.allowMessageMatch : options.context !== "send";
	for (const candidate of collectTelegramErrorCandidates(err)) {
		const code = normalizeCode(getErrorCode(candidate));
		if (code && RECOVERABLE_ERROR_CODES.has(code)) return true;
		const name = readErrorName(candidate);
		if (name && RECOVERABLE_ERROR_NAMES.has(name)) return true;
		const message = normalizeLowercaseStringOrEmpty(formatErrorMessage$1(candidate));
		if (message && ALWAYS_RECOVERABLE_MESSAGES.has(message)) return true;
		if (message && GRAMMY_NETWORK_REQUEST_FAILED_AFTER_RE.test(message)) return true;
		if (allowMessageMatch && message) {
			if (RECOVERABLE_MESSAGE_SNIPPETS.some((snippet) => message.includes(snippet))) return true;
		}
	}
	return false;
}
//#endregion
//#region extensions/telegram/src/request-timeouts.ts
const TELEGRAM_GET_UPDATES_REQUEST_TIMEOUT_MS = 45e3;
const TELEGRAM_DEFAULT_LONG_POLL_TIMEOUT_SECONDS = 30;
const TELEGRAM_LONG_POLL_ABORT_MARGIN_SECONDS = 5;
function resolveTelegramLongPollTimeoutSeconds(timeoutSeconds) {
	const maxLongPollTimeoutSeconds = Math.max(1, Math.floor(TELEGRAM_GET_UPDATES_REQUEST_TIMEOUT_MS / 1e3) - TELEGRAM_LONG_POLL_ABORT_MARGIN_SECONDS);
	return Math.min(typeof timeoutSeconds === "number" && Number.isFinite(timeoutSeconds) ? Math.max(1, Math.floor(timeoutSeconds)) : TELEGRAM_DEFAULT_LONG_POLL_TIMEOUT_SECONDS, maxLongPollTimeoutSeconds);
}
//#endregion
//#region extensions/telegram/src/telegram-ingress-spool.ts
const SPOOL_VERSION = 1;
`${process.pid}${randomUUID()}`;
function isValidUpdateId(value) {
	return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
}
function resolveTelegramUpdateId(update) {
	if (!update || typeof update !== "object") return null;
	const value = update.update_id;
	return isValidUpdateId(value) ? value : null;
}
function spoolFileName(updateId) {
	return `${String(updateId).padStart(16, "0")}.json`;
}
function processingFileName(updateId) {
	return `${spoolFileName(updateId)}.processing`;
}
function failedFileName(updateId) {
	return `${spoolFileName(updateId)}.failed`;
}
function processingPath(spoolDir, updateId) {
	return path.join(spoolDir, processingFileName(updateId));
}
function failedPath(spoolDir, updateId) {
	return path.join(spoolDir, failedFileName(updateId));
}
async function pathExists(filePath) {
	try {
		await fs.access(filePath);
		return true;
	} catch (err) {
		if (err.code === "ENOENT") return false;
		throw err;
	}
}
async function unlinkIfPresent(filePath) {
	try {
		await fs.unlink(filePath);
	} catch (err) {
		if (err.code === "ENOENT") return;
		throw err;
	}
}
async function writeTelegramSpooledUpdate(params) {
	const updateId = resolveTelegramUpdateId(params.update);
	if (updateId === null) throw new Error("Telegram update missing numeric update_id.");
	await fs.mkdir(params.spoolDir, { recursive: true });
	const targetPath = path.join(params.spoolDir, spoolFileName(updateId));
	const claimedPath = processingPath(params.spoolDir, updateId);
	const tombstonePath = failedPath(params.spoolDir, updateId);
	if (await pathExists(claimedPath) || await pathExists(tombstonePath)) return updateId;
	const tempPath = path.join(params.spoolDir, `${spoolFileName(updateId)}.${randomUUID()}.tmp`);
	const payload = {
		version: SPOOL_VERSION,
		updateId,
		receivedAt: params.now ?? Date.now(),
		update: params.update
	};
	await fs.writeFile(tempPath, `${JSON.stringify(payload)}\n`, { mode: 384 });
	if (await pathExists(claimedPath) || await pathExists(tombstonePath)) {
		await unlinkIfPresent(tempPath);
		return updateId;
	}
	await fs.rename(tempPath, targetPath);
	return updateId;
}
//#endregion
//#region extensions/telegram/src/telegram-ingress-worker.runtime.ts
const options = workerData;
const pollLimit = 100;
const retryInitialMs = 1e3;
const retryMaxMs = 3e4;
let stopped = false;
let activeController;
function post(message) {
	parentPort?.postMessage(message);
}
function sleep(ms) {
	return new Promise((resolve) => setTimeout(resolve, ms));
}
function formatErrorMessage(err) {
	if (err instanceof Error) return err.message || err.name;
	return String(err);
}
function resolveBackoff(attempt) {
	return Math.min(retryMaxMs, retryInitialMs * 2 ** Math.max(0, attempt - 1));
}
parentPort?.on("message", (message) => {
	if (message?.type !== "stop") return;
	stopped = true;
	activeController?.abort(/* @__PURE__ */ new Error("telegram ingress worker stopped"));
});
async function fetchJson(params) {
	const controller = new AbortController();
	activeController = controller;
	const timeout = setTimeout(() => {
		controller.abort(/* @__PURE__ */ new Error("Telegram getUpdates timed out"));
	}, TELEGRAM_GET_UPDATES_REQUEST_TIMEOUT_MS);
	timeout.unref?.();
	try {
		const response = await params.fetch(params.url, {
			method: "POST",
			headers: { "content-type": "application/json" },
			body: JSON.stringify(params.body),
			signal: controller.signal
		});
		const json = await response.json();
		if (!response.ok || json.ok !== true) throw new Error(typeof json.description === "string" ? json.description : `Telegram getUpdates failed with HTTP ${response.status}`);
		return json.result;
	} finally {
		clearTimeout(timeout);
		if (activeController === controller) activeController = void 0;
	}
}
async function main() {
	const transport = resolveTelegramTransport(options.proxy ? makeProxyFetch(options.proxy) : void 0, { network: options.network });
	const fetchImpl = transport.fetch ?? globalThis.fetch;
	const getUpdatesUrl = `${normalizeTelegramApiRoot(options.apiRoot ?? "https://api.telegram.org")}/bot${options.token}/getUpdates`;
	const pollTimeoutSeconds = resolveTelegramLongPollTimeoutSeconds(options.timeoutSeconds);
	let lastUpdateId = options.initialUpdateId;
	let failures = 0;
	try {
		for (;;) {
			if (stopped) break;
			const offset = lastUpdateId === null ? null : lastUpdateId + 1;
			post({
				type: "poll-start",
				offset,
				startedAt: Date.now()
			});
			try {
				const result = await fetchJson({
					fetch: fetchImpl,
					url: getUpdatesUrl,
					body: {
						timeout: pollTimeoutSeconds,
						limit: pollLimit,
						allowed_updates: resolveTelegramAllowedUpdates(),
						...offset === null ? {} : { offset }
					}
				});
				if (!Array.isArray(result)) throw new Error("Telegram getUpdates returned a non-array result.");
				for (const update of result) {
					if (stopped) break;
					const updateId = await writeTelegramSpooledUpdate({
						spoolDir: options.spoolDir,
						update
					});
					if (lastUpdateId === null || updateId > lastUpdateId) lastUpdateId = updateId;
					post({
						type: "spooled",
						updateId,
						queued: result.length
					});
				}
				failures = 0;
				post({
					type: "poll-success",
					offset,
					count: result.length,
					finishedAt: Date.now()
				});
			} catch (err) {
				if (stopped) break;
				failures += 1;
				post({
					type: "poll-error",
					message: formatErrorMessage(err),
					finishedAt: Date.now()
				});
				if (!isRecoverableTelegramNetworkError(err, { context: "polling" })) throw err;
				await sleep(resolveBackoff(failures));
			}
		}
	} finally {
		await transport.close();
	}
}
main().then(() => {
	parentPort?.close();
}).catch((err) => {
	post({
		type: "poll-error",
		message: formatErrorMessage(err),
		finishedAt: Date.now()
	});
	parentPort?.close();
	process.exitCode = stopped ? 0 : 1;
});
//#endregion
export {};
