import { createLogger } from "../../../utils/logger/index.js";
import { init_logger } from "../../../utils/logger.js";
import { registerSpeechProvider } from "../speech-registry.js";
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
import path from "node:path";
import { tmpdir } from "node:os";
//#region src/voice/tts/providers/edge-speech.ts
/**
* Edge TTS provider — wraps the `node-edge-tts` package. Requires no API key
* (Microsoft's Edge browser endpoint is unauthenticated).
*
* Implementation notes:
*   - Temp-file dance (mkdtemp → ttsPromise → readFileSync → rm) is required
*     because `node-edge-tts` does not expose a stream-to-buffer API.
*   - `synthesizeStream` is implemented as "synthesize + wrap as single-chunk
*     stream" so callers see uniform behavior. This is not a true network
*     stream; it's buffered-then-wrapped.
*   - `inferEdgeExtension` derives the file extension from Edge's outputFormat
*     string.
*/
init_logger();
const log = createLogger("SpeechProvider:Edge");
const DEFAULT_VOICE = "en-US-MichelleNeural";
const DEFAULT_LANG = "en-US";
const DEFAULT_OUTPUT_FORMAT = "audio-24khz-48kbitrate-mono-mp3";
function trimToUndefined(value) {
	if (typeof value !== "string") return;
	const trimmed = value.trim();
	return trimmed.length > 0 ? trimmed : void 0;
}
function asObject(value) {
	return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
}
function inferEdgeExtension(outputFormat) {
	const normalized = outputFormat.toLowerCase();
	if (normalized.includes("webm")) return {
		ext: "webm",
		format: "webm"
	};
	if (normalized.includes("opus")) return {
		ext: "opus",
		format: "opus"
	};
	if (normalized.includes("ogg")) return {
		ext: "ogg",
		format: "ogg"
	};
	if (normalized.includes("wav") || normalized.includes("riff") || normalized.includes("pcm")) return {
		ext: "wav",
		format: "wav"
	};
	return {
		ext: "mp3",
		format: "mp3"
	};
}
function normalizeConfig(rawConfig) {
	const raw = asObject(rawConfig.edge) ?? rawConfig;
	return {
		voice: trimToUndefined(raw.voice ?? raw.voiceId) ?? DEFAULT_VOICE,
		lang: trimToUndefined(raw.lang) ?? DEFAULT_LANG,
		outputFormat: trimToUndefined(raw.outputFormat) ?? DEFAULT_OUTPUT_FORMAT,
		pitch: trimToUndefined(raw.pitch),
		rate: trimToUndefined(raw.rate),
		volume: trimToUndefined(raw.volume),
		proxy: trimToUndefined(raw.proxy),
		enabled: raw.enabled !== false
	};
}
function readProviderConfig(config) {
	return {
		voice: trimToUndefined(config.voice ?? config.voiceId) ?? DEFAULT_VOICE,
		lang: trimToUndefined(config.lang) ?? DEFAULT_LANG,
		outputFormat: trimToUndefined(config.outputFormat) ?? DEFAULT_OUTPUT_FORMAT,
		pitch: trimToUndefined(config.pitch),
		rate: trimToUndefined(config.rate),
		volume: trimToUndefined(config.volume),
		proxy: trimToUndefined(config.proxy),
		enabled: config.enabled !== false
	};
}
function parseDirectiveTokenInternal(ctx) {
	switch (ctx.key) {
		case "voice":
		case "voice_id":
		case "voiceid":
		case "edge_voice":
		case "edgevoice":
			if (!ctx.policy.allowVoice) return { handled: true };
			return {
				handled: true,
				overrides: { voice: ctx.value }
			};
		default: return { handled: false };
	}
}
async function synthesizeToBuffer(text, config, voiceOverride, timeoutMs) {
	const { EdgeTTS } = await import("node-edge-tts");
	const voice = voiceOverride ?? config.voice;
	const tempDir = mkdtempSync(path.join(tmpdir(), "tts-edge-"));
	const { ext, format } = inferEdgeExtension(config.outputFormat);
	const outputPath = path.join(tempDir, `speech-${Date.now()}.${ext}`);
	try {
		log.debug({
			voice,
			outputFormat: config.outputFormat,
			textLength: text.length
		}, "Calling Edge TTS");
		await new EdgeTTS({
			voice,
			lang: config.lang,
			outputFormat: config.outputFormat,
			proxy: config.proxy,
			rate: config.rate,
			pitch: config.pitch,
			volume: config.volume,
			timeout: timeoutMs
		}).ttsPromise(text, outputPath);
		return {
			buffer: readFileSync(outputPath),
			outputFormat: format,
			ext
		};
	} finally {
		try {
			rmSync(tempDir, {
				recursive: true,
				force: true
			});
		} catch (cleanupError) {
			log.warn({
				err: cleanupError,
				tempDir
			}, "Failed to cleanup Edge TTS temp directory");
		}
	}
}
const edgeSpeechProvider = {
	id: "edge",
	autoSelectOrder: 100,
	resolveConfig: (ctx) => normalizeConfig(ctx.rawConfig),
	isConfigured: (ctx) => readProviderConfig(ctx.providerConfig).enabled,
	parseDirectiveToken: parseDirectiveTokenInternal,
	listVoices: async () => [{
		id: DEFAULT_VOICE,
		name: DEFAULT_VOICE,
		locale: DEFAULT_LANG
	}],
	synthesize: async (req) => {
		const config = readProviderConfig(req.providerConfig);
		if (!config.enabled) throw new Error("Edge TTS is disabled in config (messages.tts.providers.edge.enabled = false)");
		const overrides = req.providerOverrides ?? {};
		const voiceOverride = trimToUndefined(overrides.voice ?? overrides.voiceId);
		const { buffer, outputFormat, ext } = await synthesizeToBuffer(req.text, config, voiceOverride, req.timeoutMs);
		return {
			audioBuffer: buffer,
			outputFormat,
			fileExtension: ext,
			voiceCompatible: outputFormat === "opus" || outputFormat === "ogg"
		};
	},
	synthesizeStream: async (req) => {
		const config = readProviderConfig(req.providerConfig);
		if (!config.enabled) throw new Error("Edge TTS is disabled in config (messages.tts.providers.edge.enabled = false)");
		const overrides = req.providerOverrides ?? {};
		const voiceOverride = trimToUndefined(overrides.voice ?? overrides.voiceId);
		const { buffer, outputFormat, ext } = await synthesizeToBuffer(req.text, config, voiceOverride, req.timeoutMs);
		return {
			audioStream: new ReadableStream({ start(controller) {
				controller.enqueue(new Uint8Array(buffer));
				controller.close();
			} }),
			outputFormat,
			fileExtension: ext,
			voiceCompatible: outputFormat === "opus" || outputFormat === "ogg"
		};
	}
};
registerSpeechProvider(edgeSpeechProvider);
//#endregion
export { edgeSpeechProvider };

//# sourceMappingURL=edge-speech.js.map