import { createLogger } from "../../utils/logger/index.js";
import { init_logger } from "../../utils/logger.js";
import { listSpeechProviders } from "./speech-registry.js";
import "./providers/index.js";
//#region src/voice/tts/directives.ts
init_logger();
const log = createLogger("TTS:Directives");
const VALID_PROVIDERS = [
	"openai",
	"alibaba",
	"edge",
	"minimax"
];
function isValidProvider(value) {
	return VALID_PROVIDERS.includes(value);
}
function parseNumber(value) {
	const parsed = Number.parseFloat(value);
	return Number.isFinite(parsed) ? parsed : void 0;
}
/**
* Apply a provider-handled override into the bucket for `providerId`. Mutates
* `overrides` in place. Maps the SpeechProviderOverrides keys (model/voice/speed)
* onto the per-provider bucket in TtsDirectiveOverrides.
*/
function recordProviderOverride(overrides, providerId, patch) {
	if (!isValidProvider(providerId)) return;
	const bucket = overrides[providerId] ?? {};
	if (patch.voice !== void 0) bucket.voice = patch.voice;
	if (patch.model !== void 0) bucket.model = patch.model;
	if (patch.speed !== void 0) bucket.speed = patch.speed;
	overrides[providerId] = bucket;
}
function parseTtsDirectives(text, policy = { enabled: true }) {
	if (!policy.enabled) return {
		cleanedText: text,
		hasDirective: false,
		overrides: {},
		warnings: []
	};
	const overrides = {};
	const warnings = [];
	let cleanedText = text;
	let hasDirective = false;
	cleanedText = cleanedText.replace(/\[\[tts:text\]\]([\s\S]*?)\[\[\/tts:text\]\]/gi, (_match, inner) => {
		hasDirective = true;
		if (policy.allowText && overrides.ttsText === void 0) overrides.ttsText = inner.trim();
		return "";
	});
	const providers = listSpeechProviders();
	cleanedText = cleanedText.replace(/\[\[tts:([^\]]+)\]\]/gi, (_match, body) => {
		hasDirective = true;
		const tokens = body.split(/\s+/).filter(Boolean);
		for (const token of tokens) {
			const eqIndex = token.indexOf("=");
			if (eqIndex === -1) continue;
			const key = token.slice(0, eqIndex).toLowerCase().trim();
			const value = token.slice(eqIndex + 1).trim();
			if (!key || !value) continue;
			if (key === "provider") {
				if (!policy.allowProvider) continue;
				if (isValidProvider(value)) overrides.provider = value;
				else warnings.push(`Invalid provider "${value}"`);
				continue;
			}
			if (key === "speed") {
				if (!policy.allowVoiceSettings) continue;
				const speed = parseNumber(value);
				if (speed !== void 0 && speed >= .25 && speed <= 4) overrides.speed = speed;
				else warnings.push(`Invalid speed "${value}" (must be 0.25-4.0)`);
				continue;
			}
			let handled = false;
			for (const plugin of providers) {
				if (typeof plugin.parseDirectiveToken !== "function") continue;
				const result = plugin.parseDirectiveToken({
					key,
					value,
					policy: {
						enabled: policy.enabled ?? true,
						allowText: policy.allowText ?? false,
						allowProvider: policy.allowProvider ?? false,
						allowVoice: policy.allowVoice ?? false,
						allowModelId: policy.allowModelId ?? false,
						allowVoiceSettings: policy.allowVoiceSettings ?? false,
						allowNormalization: policy.allowNormalization ?? false,
						allowSeed: policy.allowSeed ?? false
					}
				});
				if (result.handled) {
					handled = true;
					if (result.warnings) warnings.push(...result.warnings);
					if (result.overrides) recordProviderOverride(overrides, plugin.id, result.overrides);
					break;
				}
			}
			if (!handled) warnings.push(`Unknown TTS directive key "${key}"`);
		}
		return "";
	});
	cleanedText = cleanedText.replace(/[ \t]+/g, " ").replace(/\n{3,}/g, "\n\n").trim();
	if (warnings.length > 0) log.debug({ warnings }, "TTS directive warnings");
	return {
		cleanedText,
		ttsText: overrides.ttsText,
		hasDirective,
		overrides,
		warnings
	};
}
function hasTtsDirectives(text) {
	return /\[\[tts:/i.test(text);
}
function stripTtsDirectives(text) {
	return text.replace(/\[\[tts:text\]\][\s\S]*?\[\[\/tts:text\]\]/gi, "").replace(/\[\[tts:[^\]]+\]\]/gi, "").replace(/[ \t]+/g, " ").replace(/\n{3,}/g, "\n\n").trim();
}
function buildTtsSystemPromptHint(config) {
	if (!config.enabled) return;
	const hints = ["Voice (TTS) is enabled."];
	switch (config.trigger) {
		case "inbound":
			hints.push("Only use TTS when the user's last message includes audio/voice.");
			break;
		case "tagged":
			hints.push("Only use TTS when you include [[tts]] or [[tts:text]] tags.");
			break;
		case "always":
			hints.push("You can use TTS for any message by including [[tts]] tag.");
			break;
		case "off": return;
	}
	const maxLength = config.maxTextLength || 4096;
	hints.push(`Keep spoken text ≤${maxLength} chars.`);
	if (config.channel && (config.channelAudioFormat || config.channelVoiceBubble !== void 0)) {
		const fmt = config.channelAudioFormat ?? "mp3";
		const bubble = config.channelVoiceBubble === true ? "voice-note compatible" : config.channelVoiceBubble === false ? "attachment / inline audio only" : "channel default";
		hints.push(`Channel ${config.channel}: prefer ${fmt}; ${bubble}.`);
	}
	if (config.textToSpeechTool) hints.push("You may call `text_to_speech` to generate speech when the user asks you to read aloud or voice fits better than text. Do not use it on every reply.");
	if (config.modelOverrides?.enabled) {
		const allowed = [];
		if (config.modelOverrides.allowText) allowed.push("[[tts:text]]...[[/tts:text]]");
		if (config.modelOverrides.allowVoice) allowed.push("[[tts:voice=...]]");
		if (config.modelOverrides.allowModelId) allowed.push("[[tts:model=...]]");
		if (config.modelOverrides.allowProvider) allowed.push("[[tts:provider=...]]");
		if (allowed.length > 0) hints.push(`Use ${allowed.join(", ")} to control voice output.`);
	}
	return hints.join("\n");
}
//#endregion
export { buildTtsSystemPromptHint, hasTtsDirectives, parseTtsDirectives, stripTtsDirectives };

//# sourceMappingURL=directives.js.map