import { BlockedExternalUnsupportedBytesError, BlockedInlineFileExceedingError, BlockedInlineUnsupportedFileBytesError, InvalidInlineFileMalformedBase64Error } from "./file-security-errors.js";
import { decodeTextDocumentBytes, isTextDocumentMimeType } from "../../utils/text-document-attachments.js";
import { ALLOWED_EXTERNAL_IMAGE_MIME_TYPES, MAX_FILE_BYTES, TEXT_DOCUMENT_MAX_BYTES } from "./file-security-constants.js";
import { fileTypeFromBuffer } from "file-type";

//#region src/domains/run/services/blob-storage/file-content-security.ts
async function normalizeInlineImageBytes(file) {
	const normalized = await normalizeInlineFileBytes(file);
	if (normalized.mimeType.startsWith("image/")) return normalized;
	throw new BlockedInlineUnsupportedFileBytesError(file.mimeType || "unknown");
}
async function normalizeInlineFileBytes(file) {
	const data = decodeBase64Bytes(file.bytes);
	const requestedMimeType = file.mimeType?.split(";")[0]?.trim().toLowerCase();
	validateInlineFileSize(data, requestedMimeType);
	const sniffedMime = await sniffAllowedInlineFileMimeType(data, requestedMimeType);
	if (sniffedMime) return {
		data,
		mimeType: sniffedMime
	};
	throw new BlockedInlineUnsupportedFileBytesError(file.mimeType || "unknown");
}
async function resolveDownloadedFileMimeType(data, headerContentType, expectedMimeType) {
	const expected = expectedMimeType?.split(";")[0]?.trim().toLowerCase();
	if (expected === "application/pdf") {
		if (looksLikePdf(data)) return "application/pdf";
		throw new BlockedExternalUnsupportedBytesError(headerContentType || expected || "unknown");
	}
	const sniffedMime = await sniffAllowedImageMimeType(data);
	if (sniffedMime) return sniffedMime;
	throw new BlockedExternalUnsupportedBytesError(headerContentType || expected || "unknown");
}
function validateInlineFileSize(data, requestedMimeType) {
	const maxBytes = isTextDocumentMimeType(requestedMimeType) ? TEXT_DOCUMENT_MAX_BYTES : MAX_FILE_BYTES;
	if (data.length > maxBytes) throw new BlockedInlineFileExceedingError(maxBytes);
}
async function sniffAllowedImageMimeType(data) {
	const sniffedMime = (await fileTypeFromBuffer(data))?.mime?.toLowerCase();
	if (sniffedMime && ALLOWED_EXTERNAL_IMAGE_MIME_TYPES.has(sniffedMime)) return sniffedMime;
	return null;
}
async function sniffAllowedInlineFileMimeType(data, requestedMimeType) {
	if (requestedMimeType === "application/pdf") {
		if (!looksLikePdf(data)) throw new BlockedInlineUnsupportedFileBytesError(requestedMimeType);
		return "application/pdf";
	}
	if (isTextDocumentMimeType(requestedMimeType)) try {
		decodeTextDocumentBytes(data);
		return requestedMimeType;
	} catch {
		throw new BlockedInlineUnsupportedFileBytesError(requestedMimeType);
	}
	return await sniffAllowedImageMimeType(data);
}
function decodeBase64Bytes(base64Bytes) {
	const normalized = base64Bytes.replace(/\s+/g, "");
	if (normalized.length === 0 || normalized.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(normalized)) throw new InvalidInlineFileMalformedBase64Error();
	const decoded = Buffer.from(normalized, "base64");
	if (decoded.length === 0 || decoded.toString("base64") !== normalized) throw new InvalidInlineFileMalformedBase64Error();
	return Uint8Array.from(decoded);
}
function looksLikePdf(data) {
	if (data.length < 5) return false;
	return data[0] === 37 && data[1] === 80 && data[2] === 68 && data[3] === 70 && data[4] === 45;
}

//#endregion
export { normalizeInlineFileBytes, normalizeInlineImageBytes, resolveDownloadedFileMimeType };