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, postJsonRequest } from "../../../media-shared/http/provider-http.js";
import "../../../media-shared/http/index.js";
import { registerMediaUnderstandingProvider } from "../../../media-understanding/registry.js";
//#region src/voice/stt/providers/alibaba-transcription.ts
/**
* Alibaba DashScope Paraformer STT — implements MediaUnderstandingProvider.transcribeAudio.
*
* DashScope's async API is a 4-step dance:
*   1. POST /api/v1/services/audio/asr/transcription with `X-DashScope-Async: enable`
*      and `input.file_urls = [data:audio/ogg;base64,...]` → returns task_id
*   2. Poll GET /api/v1/tasks/{task_id} until task_status === 'SUCCEEDED'
*   3. The success result contains `transcription_url` pointing at a JSON document
*   4. Fetch that JSON to extract the actual `transcripts[].text`
*
* All HTTP calls go through `fetchWithTimeoutGuarded` so the SSRF guard catches
* malicious config injection. The transcription_url is a public OSS URL
* (CDN-hosted); we keep `allowPrivateNetwork: false` so a malicious tenant
* cannot redirect to an internal address.
*
* Polling caps at 60s by default (60 * 1s). The runner's `timeoutMs` controls
* HTTP timeouts on each individual submit/poll/fetch call, not the overall
* poll loop ceiling.
*/
init_logger();
const log = createLogger("STT:Alibaba");
const DEFAULT_MODEL = "paraformer-v2";
const DEFAULT_BASE_URL = "https://dashscope.aliyuncs.com/api/v1/services/audio/asr/transcription";
const TASKS_BASE_URL = "https://dashscope.aliyuncs.com/api/v1/tasks";
const POLL_INTERVAL_MS = 1e3;
const MAX_POLL_MS = 6e4;
async function submitTask(params) {
	const data = await (await postJsonRequest(params.baseUrl, {
		timeoutMs: params.timeoutMs,
		label: "Alibaba STT submit",
		headers: {
			Authorization: `Bearer ${params.apiKey}`,
			"X-DashScope-Async": "enable"
		},
		body: {
			model: params.model,
			input: { file_urls: [params.audioDataUrl] },
			...params.language ? { parameters: { language_hint: params.language } } : {}
		}
	})).json();
	if (data.code) throw new Error(`Alibaba STT API error: ${data.code} - ${data.message}`);
	if (!data.output?.task_id) {
		log.error({ response: data }, "Alibaba STT API response missing task_id");
		throw new Error(`No task_id returned from Alibaba STT API: ${JSON.stringify(data)}`);
	}
	log.debug({ taskId: data.output.task_id }, "Task submitted");
	return data.output.task_id;
}
async function pollTask(params) {
	const startTime = Date.now();
	const url = `${TASKS_BASE_URL}/${params.taskId}`;
	while (Date.now() - startTime < MAX_POLL_MS) {
		const response = await fetchWithTimeoutGuarded(url, {
			timeoutMs: params.timeoutMs,
			label: "Alibaba STT poll",
			init: {
				method: "GET",
				headers: { Authorization: `Bearer ${params.apiKey}` }
			}
		});
		if (!response.ok) {
			const errorText = await response.text().catch(() => "");
			throw new ProviderHttpError({
				label: "Alibaba STT poll",
				status: response.status,
				detail: errorText.slice(0, 220) || response.statusText
			});
		}
		const data = await response.json();
		const status = data.output?.task_status;
		if (status === "SUCCEEDED") {
			const result = data.output?.results?.[0];
			if (!result) throw new Error("Alibaba STT task succeeded but no results found");
			return { text: (await fetchTranscription(result.transcription_url, params.timeoutMs)).transcripts.map((t) => t.text).join("\n") };
		}
		if (status === "FAILED") throw new Error("Alibaba STT task failed");
		await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
	}
	throw new Error(`Alibaba STT task did not complete within ${MAX_POLL_MS}ms`);
}
async function fetchTranscription(url, timeoutMs) {
	const response = await fetchWithTimeoutGuarded(url, {
		timeoutMs,
		label: "Alibaba STT transcription fetch"
	});
	if (!response.ok) throw new ProviderHttpError({
		label: "Alibaba STT transcription fetch",
		status: response.status,
		detail: response.statusText
	});
	return await response.json();
}
async function transcribeAudio(req) {
	const startTime = Date.now();
	const model = req.model ?? DEFAULT_MODEL;
	const baseUrl = req.baseUrl ?? DEFAULT_BASE_URL;
	const base64Audio = req.buffer.toString("base64");
	const audioDataUrl = `data:${req.mime ?? "audio/ogg"};base64,${base64Audio}`;
	log.debug({
		model,
		bufferSize: req.buffer.length,
		language: req.language,
		fileName: req.fileName
	}, "Sending to Alibaba Paraformer");
	try {
		const taskId = await submitTask({
			apiKey: req.apiKey,
			baseUrl,
			model,
			audioDataUrl,
			...req.language ? { language: req.language } : {},
			timeoutMs: req.timeoutMs
		});
		const result = await pollTask({
			apiKey: req.apiKey,
			taskId,
			timeoutMs: req.timeoutMs
		});
		const durationSeconds = (Date.now() - startTime) / 1e3;
		log.info({
			provider: "alibaba",
			durationSeconds,
			textLength: result.text.length
		}, "Transcription completed");
		return {
			text: result.text,
			model,
			...req.language ? { language: req.language } : {},
			durationSeconds
		};
	} catch (error) {
		const errorMsg = error instanceof Error ? error.message : String(error);
		log.error({
			err: error,
			bufferSize: req.buffer.length,
			model
		}, `Alibaba transcription failed: ${errorMsg}`);
		throw new Error(`Alibaba STT failed: ${errorMsg}`);
	}
}
const alibabaTranscriptionProvider = {
	id: "alibaba",
	aliases: ["dashscope", "paraformer"],
	capabilities: ["audio"],
	envKey: "DASHSCOPE_API_KEY",
	defaultModels: { audio: DEFAULT_MODEL },
	autoPriority: { audio: 10 },
	transcribeAudio
};
registerMediaUnderstandingProvider(alibabaTranscriptionProvider);
//#endregion
export { alibabaTranscriptionProvider };

//# sourceMappingURL=alibaba-transcription.js.map