import { flattenHeaders, getContext, getTestId, isErrorResponse, isTranscriptionResponse, resolveResponse, resolveStrictMode, serializeErrorResponse, strictNoMatchLogLine, strictNoMatchMessage, strictOverrideField } from "./helpers.js";
import { matchFixtureDiagnostic } from "./router.js";
import { writeErrorResponse } from "./sse-writer.js";
import { applyChaos } from "./chaos.js";
import { proxyAndRecord } from "./recorder.js";

//#region src/transcription.ts
/**
* Extract the multipart boundary string from a Content-Type header.
*/
function extractBoundary(contentType) {
	if (!contentType) return void 0;
	return contentType.match(/boundary=([^\s;]+)/i)?.[1];
}
/**
* Extract a text field from multipart form data using boundary-based parsing.
* Splits the body by the multipart boundary so each part is isolated, then
* checks each part's Content-Disposition header for the target field name.
* This avoids false matches from binary audio data that might contain
* header-like byte sequences.
*/
function extractFormField(raw, fieldName, boundary) {
	if (!boundary) {
		console.warn("extractFormField: no multipart boundary found, using best-effort regex fallback");
		const escaped = fieldName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
		const pattern = new RegExp(`Content-Disposition:\\s*form-data;[^\\r\\n]*name="${escaped}"[^\\r\\n]*\\r\\n\\r\\n([^\\r\\n]*)`, "i");
		return raw.match(pattern)?.[1];
	}
	const delimiter = `--${boundary}`;
	const parts = raw.split(delimiter);
	for (const part of parts) {
		if (!part || part.trimStart().startsWith("--")) continue;
		const headerEnd = part.indexOf("\r\n\r\n");
		if (headerEnd === -1) continue;
		const headers = part.slice(0, headerEnd);
		const body = part.slice(headerEnd + 4);
		const cdMatch = headers.match(/Content-Disposition:\s*form-data;[^\r\n]*name="([^"]+)"/i);
		if (cdMatch && cdMatch[1] === fieldName) return body.replace(/\r\n$/, "");
	}
}
async function handleTranscription(req, res, raw, fixtures, journal, defaults, setCorsHeaders, endpointType = "transcription") {
	setCorsHeaders(res);
	const defaultPath = endpointType === "translation" ? "/v1/audio/translations" : "/v1/audio/transcriptions";
	const path = req.url ?? defaultPath;
	const method = req.method ?? "POST";
	const boundary = extractBoundary(Array.isArray(req.headers["content-type"]) ? req.headers["content-type"][0] : req.headers["content-type"]);
	const model = extractFormField(raw, "model", boundary) ?? "whisper-1";
	const responseFormat = extractFormField(raw, "response_format", boundary) ?? "json";
	const syntheticReq = {
		model,
		messages: [],
		_endpointType: endpointType,
		_context: getContext(req)
	};
	const testId = getTestId(req);
	const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(fixtures, syntheticReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
	if (fixture) {
		journal.incrementFixtureMatchCount(fixture, fixtures, testId);
		defaults.logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);
	} else defaults.logger.debug(`No fixture matched for request`);
	if (applyChaos(res, fixture, defaults.chaos, req.headers, journal, {
		method,
		path,
		headers: flattenHeaders(req.headers),
		body: syntheticReq
	}, fixture ? "fixture" : "proxy", defaults.registry, defaults.logger)) return;
	if (!fixture) {
		if (resolveStrictMode(defaults.strict, req.headers)) {
			const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);
			defaults.logger.error(strictNoMatchLogLine(method, path, skippedBySequenceOrTurn));
			journal.add({
				method,
				path,
				headers: flattenHeaders(req.headers),
				body: syntheticReq,
				response: {
					status: 503,
					fixture: null,
					...strictOverrideField(defaults.strict, req.headers)
				}
			});
			writeErrorResponse(res, 503, JSON.stringify({ error: {
				message: strictMessage,
				type: "invalid_request_error",
				code: "no_fixture_match"
			} }));
			return;
		}
		if (defaults.record) {
			const outcome = await proxyAndRecord(req, res, syntheticReq, "openai", req.url ?? defaultPath, fixtures, defaults, raw);
			if (outcome === "handled_by_hook") return;
			if (outcome !== "not_configured") {
				journal.add({
					method,
					path,
					headers: flattenHeaders(req.headers),
					body: syntheticReq,
					response: {
						status: res.statusCode ?? 200,
						fixture: null,
						source: "proxy"
					}
				});
				return;
			}
		}
		journal.add({
			method,
			path,
			headers: flattenHeaders(req.headers),
			body: syntheticReq,
			response: {
				status: 404,
				fixture: null,
				...strictOverrideField(defaults.strict, req.headers)
			}
		});
		writeErrorResponse(res, 404, JSON.stringify({ error: {
			message: "No fixture matched",
			type: "invalid_request_error",
			code: "no_fixture_match"
		} }));
		return;
	}
	const response = await resolveResponse(fixture, syntheticReq);
	if (isErrorResponse(response)) {
		const status = response.status ?? 500;
		journal.add({
			method,
			path,
			headers: flattenHeaders(req.headers),
			body: syntheticReq,
			response: {
				status,
				fixture
			}
		});
		writeErrorResponse(res, status, serializeErrorResponse(response), { retryAfter: response.retryAfter });
		return;
	}
	if (!isTranscriptionResponse(response)) {
		journal.add({
			method,
			path,
			headers: flattenHeaders(req.headers),
			body: syntheticReq,
			response: {
				status: 500,
				fixture
			}
		});
		writeErrorResponse(res, 500, JSON.stringify({ error: {
			message: "Fixture response is not a transcription type",
			type: "server_error"
		} }));
		return;
	}
	journal.add({
		method,
		path,
		headers: flattenHeaders(req.headers),
		body: syntheticReq,
		response: {
			status: 200,
			fixture
		}
	});
	const t = response.transcription;
	if (responseFormat === "verbose_json" || t.words != null || t.segments != null) {
		const verboseBody = {
			task: endpointType === "translation" ? "translate" : "transcribe",
			language: t.language ?? "english",
			duration: t.duration ?? 0,
			text: t.text
		};
		if (t.words && t.words.length > 0) verboseBody.words = t.words;
		if (t.segments && t.segments.length > 0) verboseBody.segments = t.segments;
		res.writeHead(200, { "Content-Type": "application/json" });
		res.end(JSON.stringify(verboseBody));
	} else {
		res.writeHead(200, { "Content-Type": "application/json" });
		res.end(JSON.stringify({ text: t.text }));
	}
}

//#endregion
export { extractBoundary, extractFormField, handleTranscription };
//# sourceMappingURL=transcription.js.map