import type { MessageFile } from "$lib/types/Message";
import { z } from "zod";

export interface FileProcessorOptions<TMimeType extends string = string> {
	supportedMimeTypes: TMimeType[];
	maxSizeInMB: number;
}

export type ImageProcessor<TMimeType extends string = string> = (file: MessageFile) => Promise<{
	file: Buffer;
	mime: TMimeType;
}>;

export const createDocumentProcessorOptionsValidator = <TMimeType extends string = string>(
	defaults: FileProcessorOptions<TMimeType>
) => {
	return z
		.object({
			supportedMimeTypes: z
				.array(
					z.enum<string, [TMimeType, ...TMimeType[]]>([
						defaults.supportedMimeTypes[0],
						...defaults.supportedMimeTypes.slice(1),
					])
				)
				.default(defaults.supportedMimeTypes),
			maxSizeInMB: z.number().positive().default(defaults.maxSizeInMB),
		})
		.default(defaults);
};

export type DocumentProcessor<TMimeType extends string = string> = (file: MessageFile) => {
	file: Buffer;
	mime: TMimeType;
};

export type AsyncDocumentProcessor<TMimeType extends string = string> = (
	file: MessageFile
) => Promise<{
	file: Buffer;
	mime: TMimeType;
}>;

export function makeDocumentProcessor<TMimeType extends string = string>(
	options: FileProcessorOptions<TMimeType>
): AsyncDocumentProcessor<TMimeType> {
	return async (file) => {
		const { supportedMimeTypes, maxSizeInMB } = options;
		const { mime, value } = file;

		const buffer = Buffer.from(value, "base64");
		const tooLargeInBytes = buffer.byteLength > maxSizeInMB * 1000 * 1000;

		if (tooLargeInBytes) {
			throw Error("Document is too large");
		}

		const outputMime = validateMimeType(supportedMimeTypes, mime);
		return { file: buffer, mime: outputMime };
	};
}

const validateMimeType = <T extends readonly string[]>(
	supportedMimes: T,
	mime: string
): T[number] => {
	if (!supportedMimes.includes(mime)) {
		const supportedMimesStr = supportedMimes.join(", ");

		throw Error(`Mimetype "${mime}" not found in supported mimes: ${supportedMimesStr}`);
	}

	return mime;
};
