import { createLogger } from "../../../utils/logger/index.js";
import { init_logger } from "../../../utils/logger.js";
import { ProviderHttpError } from "../../../media-shared/http/provider-http-errors.js";
import { fetchWithTimeoutGuarded } from "../../../media-shared/http/provider-http.js";
import "../../../media-shared/http/index.js";
import { registerSpeechProvider } from "../speech-registry.js";
//#region src/voice/tts/providers/minimax-speech.ts
/**
* MiniMax TTS provider — async task model (submit → poll → download).
*
* MiniMax's t2a_async_v2 endpoint does not match the OpenAI POST /audio/speech
* shape (it returns task_id, then we poll, then we fetch the file by file_id),
* so we implement the contract directly rather than using
* createOpenAiCompatibleSpeechProvider.
*
* Implementation notes (per docs/voice-rearchitecture.md §8.4.4):
*   - Default per-call timeout: 150s. Submit + poll + download can legitimately
*     take 60-120s on long inputs, so we enforce `Math.max(timeoutMs, 150000)`.
*   - `synthesizeStream` is intentionally not implemented. MiniMax has a
*     separate WebSocket streaming endpoint that does not match the
*     buffer-then-wrap pattern; speak-core falls back to wrapBufferAsStream.
*   - Poll interval = 2s, max 60 attempts (2 min cap). Aligns with MiniMax docs.
*   - `groupId` is read from config but not passed into the request body —
*     MiniMax embeds groupId in the API key for this account tier. Kept as a
*     forward-compat slot for the enterprise account tier.
*/
init_logger();
const log = createLogger("SpeechProvider:MiniMax");
const DEFAULT_BASE_URL = "https://api.minimaxi.com/v1";
const DEFAULT_MODEL = "speech-2.8-hd";
const DEFAULT_VOICE = "male-qn-qingse";
const ENV_KEY = "MINIMAX_API_KEY";
const POLL_INTERVAL_MS = 2e3;
const MAX_POLL_ATTEMPTS = 60;
const DEFAULT_TIMEOUT_MS = 15e4;
const MINIMAX_TTS_MODELS = [
	"speech-2.8-hd",
	"speech-2.8-turbo",
	"speech-2.6-hd",
	"speech-2.6-turbo",
	"speech-02-hd",
	"speech-02-turbo"
];
const MINIMAX_TTS_VOICES = [
	"male-qn-qingse",
	"male-qn-jingying",
	"male-qn-badao",
	"male-qn-daxuesheng",
	"female-shaonv",
	"female-yujie",
	"female-chengshu",
	"female-tianmei",
	"audiobook_male_1",
	"audiobook_male_2",
	"audiobook_female_1",
	"audiobook_female_2",
	"presenter_male",
	"presenter_female"
];
function trimToUndefined(value) {
	if (typeof value !== "string") return void 0;
	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 normalizeConfig(rawConfig) {
	const raw = asObject(rawConfig.minimax) ?? rawConfig;
	return {
		apiKey: trimToUndefined(raw.apiKey),
		baseUrl: (trimToUndefined(raw.baseUrl) ?? DEFAULT_BASE_URL).replace(/\/+$/u, ""),
		model: trimToUndefined(raw.model ?? raw.modelId) ?? DEFAULT_MODEL,
		voice: trimToUndefined(raw.voice ?? raw.voiceId) ?? DEFAULT_VOICE,
		groupId: trimToUndefined(raw.groupId)
	};
}
function readProviderConfig(config) {
	return {
		apiKey: trimToUndefined(config.apiKey),
		baseUrl: (trimToUndefined(config.baseUrl) ?? DEFAULT_BASE_URL).replace(/\/+$/u, ""),
		model: trimToUndefined(config.model ?? config.modelId) ?? DEFAULT_MODEL,
		voice: trimToUndefined(config.voice ?? config.voiceId) ?? DEFAULT_VOICE,
		groupId: trimToUndefined(config.groupId)
	};
}
function resolveApiKey(config) {
	return config.apiKey ?? trimToUndefined(process.env[ENV_KEY]);
}
function abortError() {
	const err = /* @__PURE__ */ new Error("The operation was aborted");
	err.name = "AbortError";
	return err;
}
function sleep(ms, signal) {
	return new Promise((resolve, reject) => {
		if (signal?.aborted) {
			reject(abortError());
			return;
		}
		const id = setTimeout(() => {
			signal?.removeEventListener("abort", onAbort);
			resolve();
		}, ms);
		const onAbort = () => {
			clearTimeout(id);
			signal?.removeEventListener("abort", onAbort);
			reject(abortError());
		};
		signal?.addEventListener("abort", onAbort, { once: true });
	});
}
async function submitTask(params) {
	const response = await fetchWithTimeoutGuarded(`${params.baseUrl}/t2a_async_v2`, {
		timeoutMs: params.timeoutMs,
		label: "MiniMax TTS submit",
		signal: params.signal,
		init: {
			method: "POST",
			headers: {
				Authorization: `Bearer ${params.apiKey}`,
				"content-type": "application/json"
			},
			body: JSON.stringify({
				model: params.model,
				text: params.text,
				voice_setting: {
					voice_id: params.voiceId,
					speed: 1,
					vol: 10,
					pitch: 0
				},
				audio_setting: {
					audio_sample_rate: 32e3,
					bitrate: 128e3,
					format: "mp3",
					channel: 1
				}
			})
		}
	});
	const raw = await response.text();
	if (!response.ok) throw new ProviderHttpError({
		label: "MiniMax TTS submit",
		status: response.status,
		detail: raw.slice(0, 220) || response.statusText
	});
	let data;
	try {
		data = JSON.parse(raw);
	} catch {
		throw new Error(`MiniMax TTS submit returned non-JSON: ${raw.slice(0, 240)}`);
	}
	const code = data.base_resp?.status_code;
	if (code !== void 0 && code !== 0) throw new Error(`MiniMax TTS submit error: ${code} ${data.base_resp?.status_msg ?? ""}`.trim());
	const taskId = data.task_id?.trim();
	if (!taskId) throw new Error("MiniMax TTS submit returned no task_id");
	return taskId;
}
async function pollTask(params) {
	for (let attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt++) {
		if (params.signal?.aborted) throw abortError();
		const response = await fetchWithTimeoutGuarded(`${params.baseUrl}/query/t2a_async_query_v2?task_id=${encodeURIComponent(params.taskId)}`, {
			timeoutMs: params.timeoutMs,
			label: "MiniMax TTS query",
			signal: params.signal,
			init: {
				method: "GET",
				headers: { Authorization: `Bearer ${params.apiKey}` }
			}
		});
		const raw = await response.text();
		if (!response.ok) throw new ProviderHttpError({
			label: "MiniMax TTS query",
			status: response.status,
			detail: raw.slice(0, 220) || response.statusText
		});
		let data;
		try {
			data = JSON.parse(raw);
		} catch {
			throw new Error(`MiniMax TTS query returned non-JSON: ${raw.slice(0, 240)}`);
		}
		const code = data.base_resp?.status_code;
		if (code !== void 0 && code !== 0) throw new Error(`MiniMax TTS query error: ${code} ${data.base_resp?.status_msg ?? ""}`.trim());
		const status = (data.status || "").trim();
		if (status === "Success") {
			const fileId = data.file_id?.trim();
			if (!fileId) throw new Error("MiniMax TTS completed but file_id is missing");
			return fileId;
		}
		if (status === "Failed" || status === "Fail") throw new Error("MiniMax TTS task failed");
		await sleep(POLL_INTERVAL_MS, params.signal);
	}
	throw new Error(`MiniMax TTS timed out after ${MAX_POLL_ATTEMPTS} polls`);
}
async function downloadAudio(params) {
	const response = await fetchWithTimeoutGuarded(`${params.baseUrl}/files/retrieve_content?file_id=${encodeURIComponent(params.fileId)}`, {
		timeoutMs: params.timeoutMs,
		label: "MiniMax TTS download",
		signal: params.signal,
		init: {
			method: "GET",
			headers: { Authorization: `Bearer ${params.apiKey}` }
		}
	});
	if (!response.ok) {
		const detail = await response.text().catch(() => "");
		throw new ProviderHttpError({
			label: "MiniMax TTS download",
			status: response.status,
			detail: detail.slice(0, 220) || response.statusText
		});
	}
	return Buffer.from(await response.arrayBuffer());
}
function parseDirectiveTokenInternal(ctx) {
	switch (ctx.key) {
		case "voice":
		case "voice_id":
		case "voiceid":
		case "minimax_voice":
		case "minimaxvoice":
			if (!ctx.policy.allowVoice) return { handled: true };
			return {
				handled: true,
				overrides: { voice: ctx.value }
			};
		case "model":
		case "model_id":
		case "modelid":
		case "minimax_model":
		case "minimaxmodel":
			if (!ctx.policy.allowModelId) return { handled: true };
			return {
				handled: true,
				overrides: { model: ctx.value }
			};
		default: return { handled: false };
	}
}
const minimaxSpeechProvider = {
	id: "minimax",
	autoSelectOrder: 40,
	resolveConfig: (ctx) => normalizeConfig(ctx.rawConfig),
	isConfigured: (ctx) => Boolean(resolveApiKey(readProviderConfig(ctx.providerConfig))),
	parseDirectiveToken: parseDirectiveTokenInternal,
	listVoices: async () => MINIMAX_TTS_VOICES.map((id) => ({
		id,
		name: id
	})),
	synthesize: async (req) => {
		const config = readProviderConfig(req.providerConfig);
		const apiKey = resolveApiKey(config);
		if (!apiKey) throw new Error(`MiniMax TTS API key missing (set ${ENV_KEY} or messages.tts.providers.minimax.apiKey)`);
		const overrides = req.providerOverrides ?? {};
		const model = trimToUndefined(overrides.model ?? overrides.modelId) ?? config.model;
		const voice = trimToUndefined(overrides.voice ?? overrides.voiceId) ?? config.voice;
		const callTimeoutMs = Math.max(req.timeoutMs, DEFAULT_TIMEOUT_MS);
		log.debug({
			model,
			voice,
			textLength: req.text.length
		}, "MiniMax TTS submit");
		const taskId = await submitTask({
			baseUrl: config.baseUrl,
			apiKey,
			model,
			voiceId: voice,
			text: req.text,
			timeoutMs: callTimeoutMs
		});
		const fileId = await pollTask({
			baseUrl: config.baseUrl,
			apiKey,
			taskId,
			timeoutMs: callTimeoutMs
		});
		const audioBuffer = await downloadAudio({
			baseUrl: config.baseUrl,
			apiKey,
			fileId,
			timeoutMs: callTimeoutMs
		});
		log.debug({
			size: audioBuffer.length,
			taskId
		}, "MiniMax TTS completed");
		return {
			audioBuffer,
			outputFormat: "mp3",
			fileExtension: "mp3",
			voiceCompatible: false
		};
	}
};
registerSpeechProvider(minimaxSpeechProvider);
//#endregion
export { MINIMAX_TTS_MODELS, MINIMAX_TTS_VOICES, minimaxSpeechProvider };

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