import { flattenHeaders, getContext, getTestId, isErrorResponse, isImageResponse, 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";
import { extractBoundary, extractFormField } from "./transcription.js";

//#region src/images.ts
function buildSyntheticRequest(model, prompt, context) {
	return {
		model,
		messages: [{
			role: "user",
			content: prompt
		}],
		_endpointType: "image",
		...context !== void 0 && { _context: context }
	};
}
async function handleImages(req, res, raw, fixtures, journal, defaults, setCorsHeaders, format = "openai", geminiModel) {
	setCorsHeaders(res);
	const path = req.url ?? "/v1/images/generations";
	const method = req.method ?? "POST";
	let model;
	let prompt;
	try {
		const body = JSON.parse(raw);
		if (format === "gemini") {
			prompt = body.instances?.[0]?.prompt ?? "";
			model = geminiModel ?? "imagen";
		} else {
			const openaiReq = body;
			prompt = openaiReq.prompt ?? "";
			model = openaiReq.model ?? "dall-e-3";
		}
	} catch (parseErr) {
		const detail = parseErr instanceof Error ? parseErr.message : "unknown";
		journal.add({
			method,
			path,
			headers: flattenHeaders(req.headers),
			body: null,
			response: {
				status: 400,
				fixture: null
			}
		});
		writeErrorResponse(res, 400, JSON.stringify({ error: {
			message: `Malformed JSON: ${detail}`,
			type: "invalid_request_error",
			code: "invalid_json"
		} }));
		return;
	}
	if (!prompt) {
		journal.add({
			method,
			path,
			headers: flattenHeaders(req.headers),
			body: null,
			response: {
				status: 400,
				fixture: null
			}
		});
		writeErrorResponse(res, 400, JSON.stringify({ error: {
			message: "Missing required parameter: 'prompt'",
			type: "invalid_request_error"
		} }));
		return;
	}
	const syntheticReq = buildSyntheticRequest(model, prompt, 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, format === "gemini" ? "gemini" : "openai", req.url ?? "/v1/images/generations", 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 (!isImageResponse(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 an image type",
			type: "server_error"
		} }));
		return;
	}
	journal.add({
		method,
		path,
		headers: flattenHeaders(req.headers),
		body: syntheticReq,
		response: {
			status: 200,
			fixture
		}
	});
	const items = response.images ?? (response.image ? [response.image] : []);
	if (format === "gemini") {
		const predictions = items.map((item) => ({
			bytesBase64Encoded: item.b64Json ?? "",
			mimeType: "image/png"
		}));
		res.writeHead(200, { "Content-Type": "application/json" });
		res.end(JSON.stringify({ predictions }));
	} else serializeOpenAIImageResponse(res, items);
}
/**
* Write the standard OpenAI image response envelope (`{ created, data }`).
* Shared by generations, edit, and variations endpoints.
*/
function serializeOpenAIImageResponse(res, items) {
	const data = items.map((item) => {
		const entry = {};
		if (item.url) entry.url = item.url;
		if (item.b64Json) entry.b64_json = item.b64Json;
		if (item.revisedPrompt) entry.revised_prompt = item.revisedPrompt;
		return entry;
	});
	res.writeHead(200, { "Content-Type": "application/json" });
	res.end(JSON.stringify({
		created: Math.floor(Date.now() / 1e3),
		data
	}));
}
/**
* Handle POST /v1/images/edits — OpenAI Image Edit API.
*
* Request uses multipart/form-data. We extract text fields (`prompt`, `model`,
* `n`, `size`, `response_format`) and ignore binary fields (`image`, `mask`)
* since aimock doesn't process actual image data.
*
* The response envelope is identical to /v1/images/generations.
*/
async function handleImageEdit(req, res, raw, fixtures, journal, defaults, setCorsHeaders) {
	setCorsHeaders(res);
	const path = req.url ?? "/v1/images/edits";
	const method = req.method ?? "POST";
	const boundary = extractBoundary(Array.isArray(req.headers["content-type"]) ? req.headers["content-type"][0] : req.headers["content-type"]);
	const prompt = extractFormField(raw, "prompt", boundary) ?? "";
	const model = extractFormField(raw, "model", boundary) ?? "dall-e-2";
	if (!prompt) {
		journal.add({
			method,
			path,
			headers: flattenHeaders(req.headers),
			body: null,
			response: {
				status: 400,
				fixture: null
			}
		});
		writeErrorResponse(res, 400, JSON.stringify({ error: {
			message: "Missing required parameter: 'prompt'",
			type: "invalid_request_error"
		} }));
		return;
	}
	const syntheticReq = buildSyntheticRequest(model, prompt, 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 ?? "/v1/images/edits", 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 (!isImageResponse(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 an image type",
			type: "server_error"
		} }));
		return;
	}
	journal.add({
		method,
		path,
		headers: flattenHeaders(req.headers),
		body: syntheticReq,
		response: {
			status: 200,
			fixture
		}
	});
	serializeOpenAIImageResponse(res, response.images ?? (response.image ? [response.image] : []));
}
/**
* Handle POST /v1/images/variations — OpenAI Image Variations API.
*
* Request uses multipart/form-data. We extract text fields (`model`, `n`,
* `size`, `response_format`) and ignore the binary `image` field.
* Unlike edit, no `prompt` field is required.
*
* The response envelope is identical to /v1/images/generations.
*/
async function handleImageVariations(req, res, raw, fixtures, journal, defaults, setCorsHeaders) {
	setCorsHeaders(res);
	const path = req.url ?? "/v1/images/variations";
	const method = req.method ?? "POST";
	const syntheticReq = buildSyntheticRequest(extractFormField(raw, "model", extractBoundary(Array.isArray(req.headers["content-type"]) ? req.headers["content-type"][0] : req.headers["content-type"])) ?? "dall-e-2", "[variation]", 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 ?? "/v1/images/variations", 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 (!isImageResponse(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 an image type",
			type: "server_error"
		} }));
		return;
	}
	journal.add({
		method,
		path,
		headers: flattenHeaders(req.headers),
		body: syntheticReq,
		response: {
			status: 200,
			fixture
		}
	});
	serializeOpenAIImageResponse(res, response.images ?? (response.image ? [response.image] : []));
}

//#endregion
export { handleImageEdit, handleImageVariations, handleImages };
//# sourceMappingURL=images.js.map