import { Mr as formatErrorMessage, ns as resolveRetryConfig, rs as retryAsync } from "./model-auth-DvR9nmjM.js";
import { T as createSubsystemLogger } from "./utils-BkOxPe01.js";
import { RateLimitError } from "@buape/carbon";
//#region src/polls.ts
function resolvePollMaxSelections(optionCount, allowMultiselect) {
	return allowMultiselect ? Math.max(2, optionCount) : 1;
}
function normalizePollInput(input, options = {}) {
	const question = input.question.trim();
	if (!question) throw new Error("Poll question is required");
	const cleaned = (input.options ?? []).map((option) => option.trim()).filter(Boolean);
	if (cleaned.length < 2) throw new Error("Poll requires at least 2 options");
	if (options.maxOptions !== void 0 && cleaned.length > options.maxOptions) throw new Error(`Poll supports at most ${options.maxOptions} options`);
	const maxSelectionsRaw = input.maxSelections;
	const maxSelections = typeof maxSelectionsRaw === "number" && Number.isFinite(maxSelectionsRaw) ? Math.floor(maxSelectionsRaw) : 1;
	if (maxSelections < 1) throw new Error("maxSelections must be at least 1");
	if (maxSelections > cleaned.length) throw new Error("maxSelections cannot exceed option count");
	const durationSecondsRaw = input.durationSeconds;
	const durationSeconds = typeof durationSecondsRaw === "number" && Number.isFinite(durationSecondsRaw) ? Math.floor(durationSecondsRaw) : void 0;
	if (durationSeconds !== void 0 && durationSeconds < 1) throw new Error("durationSeconds must be at least 1");
	const durationRaw = input.durationHours;
	const durationHours = typeof durationRaw === "number" && Number.isFinite(durationRaw) ? Math.floor(durationRaw) : void 0;
	if (durationHours !== void 0 && durationHours < 1) throw new Error("durationHours must be at least 1");
	if (durationSeconds !== void 0 && durationHours !== void 0) throw new Error("durationSeconds and durationHours are mutually exclusive");
	return {
		question,
		options: cleaned,
		maxSelections,
		durationSeconds,
		durationHours
	};
}
function normalizePollDurationHours(value, options) {
	const base = typeof value === "number" && Number.isFinite(value) ? Math.floor(value) : options.defaultHours;
	return Math.min(Math.max(base, 1), options.maxHours);
}
//#endregion
//#region src/infra/retry-policy.ts
const DISCORD_RETRY_DEFAULTS = {
	attempts: 3,
	minDelayMs: 500,
	maxDelayMs: 3e4,
	jitter: .1
};
const TELEGRAM_RETRY_DEFAULTS = {
	attempts: 3,
	minDelayMs: 400,
	maxDelayMs: 3e4,
	jitter: .1
};
const TELEGRAM_RETRY_RE = /429|timeout|connect|reset|closed|unavailable|temporarily/i;
const log = createSubsystemLogger("retry-policy");
function resolveTelegramShouldRetry(params) {
	if (!params.shouldRetry) return (err) => TELEGRAM_RETRY_RE.test(formatErrorMessage(err));
	if (params.strictShouldRetry) return params.shouldRetry;
	return (err) => params.shouldRetry?.(err) || TELEGRAM_RETRY_RE.test(formatErrorMessage(err));
}
function getTelegramRetryAfterMs(err) {
	if (!err || typeof err !== "object") return;
	const candidate = "parameters" in err && err.parameters && typeof err.parameters === "object" ? err.parameters.retry_after : "response" in err && err.response && typeof err.response === "object" && "parameters" in err.response ? err.response.parameters?.retry_after : "error" in err && err.error && typeof err.error === "object" && "parameters" in err.error ? err.error.parameters?.retry_after : void 0;
	return typeof candidate === "number" && Number.isFinite(candidate) ? candidate * 1e3 : void 0;
}
function createDiscordRetryRunner(params) {
	const retryConfig = resolveRetryConfig(DISCORD_RETRY_DEFAULTS, {
		...params.configRetry,
		...params.retry
	});
	return (fn, label) => retryAsync(fn, {
		...retryConfig,
		label,
		shouldRetry: (err) => err instanceof RateLimitError,
		retryAfterMs: (err) => err instanceof RateLimitError ? err.retryAfter * 1e3 : void 0,
		onRetry: params.verbose ? (info) => {
			const labelText = info.label ?? "request";
			const maxRetries = Math.max(1, info.maxAttempts - 1);
			log.warn(`discord ${labelText} rate limited, retry ${info.attempt}/${maxRetries} in ${info.delayMs}ms`);
		} : void 0
	});
}
function createTelegramRetryRunner(params) {
	const retryConfig = resolveRetryConfig(TELEGRAM_RETRY_DEFAULTS, {
		...params.configRetry,
		...params.retry
	});
	const shouldRetry = resolveTelegramShouldRetry(params);
	return (fn, label) => retryAsync(fn, {
		...retryConfig,
		label,
		shouldRetry,
		retryAfterMs: getTelegramRetryAfterMs,
		onRetry: params.verbose ? (info) => {
			const maxRetries = Math.max(1, info.maxAttempts - 1);
			log.warn(`telegram send retry ${info.attempt}/${maxRetries} for ${info.label ?? label ?? "request"} in ${info.delayMs}ms: ${formatErrorMessage(info.err)}`);
		} : void 0
	});
}
//#endregion
//#region src/infra/channel-activity.ts
const activity = /* @__PURE__ */ new Map();
function keyFor(channel, accountId) {
	return `${channel}:${accountId || "default"}`;
}
function ensureEntry(channel, accountId) {
	const key = keyFor(channel, accountId);
	const existing = activity.get(key);
	if (existing) return existing;
	const created = {
		inboundAt: null,
		outboundAt: null
	};
	activity.set(key, created);
	return created;
}
function recordChannelActivity(params) {
	const at = typeof params.at === "number" ? params.at : Date.now();
	const accountId = params.accountId?.trim() || "default";
	const entry = ensureEntry(params.channel, accountId);
	if (params.direction === "inbound") entry.inboundAt = at;
	if (params.direction === "outbound") entry.outboundAt = at;
}
function getChannelActivity(params) {
	const accountId = params.accountId?.trim() || "default";
	return activity.get(keyFor(params.channel, accountId)) ?? {
		inboundAt: null,
		outboundAt: null
	};
}
//#endregion
export { normalizePollDurationHours as a, createTelegramRetryRunner as i, recordChannelActivity as n, normalizePollInput as o, createDiscordRetryRunner as r, resolvePollMaxSelections as s, getChannelActivity as t };
