import { existsSync } from 'fs';
import { fork } from 'child_process';
import { resolve as pathResolve } from 'path';
import Config from '#config';

export async function wrapCompression(
	/** Path to the compression module */
	modulePath: string,
	/** Size to be compressed, between: desktop | mobile | tablet */
	compressSize: string,
	/** Type of file, between: IMAGE | VIDEO */
	compressType: string,
	/** File collected from multer middleware */
	file: Express.Multer.File,
	/** Return options */
	fileReturnOptions: Record<string, string | undefined>,
): Promise<void> {
	if (!existsSync(modulePath)) {
		const { LoggingService } = await import('#services/file-system/logService.js');
		const logger = new LoggingService();
		await logger.logErrorAsync('wrapCompression', new Error('Module path not found'), null);
		throw new Error('Missing compression service');
	}

	return new Promise<void>((resolve, reject) => {
		const child = fork(modulePath);
		// P2-11: kill child process if it hangs
		const timeout = setTimeout(() => {
			child.kill();
			reject(new Error('Compression timed out'));
		}, Config.uploads.compressTimeoutMs);

		child.on('error', (err) => {
			clearTimeout(timeout);
			reject(new Error(err.message));
		});
		child.send({ compressType, file, compressSize });
		child.on('message', (message: { statusCode: number; text?: string; path: { [key: string]: string } | null }) => {
			clearTimeout(timeout);
			if (message.statusCode == 500) reject(new Error(<string>message.text));
			else {
				// Handle null path for unsupported compression types - skip if path is null
				if (message.path) {
					for (const [key, value] of Object.entries(message.path)) {
						if (value !== null) fileReturnOptions[key] = value;
					}
				}
				resolve();
			}
		});
	});
}

// P1-7: return a plain relative key (no /api/storage/uploads prefix)
// LocalStorageAdapter joins this with storageRoot; download handler strips legacy prefix if needed.
export const getRelativePath = (file: Express.Multer.File): string => {
	const subfolder = file.mimetype.startsWith('image/')
		? 'images'
		: file.mimetype.startsWith('video/')
			? 'videos'
			: 'files';
	return `${subfolder}/${file.filename}`;
};

/**
 * Compress an image file into multiple sizes
 * @param file - The uploaded file from multer
 * @returns Promise<Record<string, string>> - Object containing paths to compressed files
 */
export async function compressImageFile(file: Express.Multer.File): Promise<Record<string, string>> {
	const compressInfo: Record<string, string | undefined> = {};
	// H-10: Resolve path relative to __dirname so it works in both dev (ts-node) and prod (dist/)
	const tsPath = pathResolve(__dirname, '../file-system/compressService.ts');
	const jsPath = pathResolve(__dirname, '../file-system/compressService.js');
	const modulePath = existsSync(jsPath) ? jsPath : tsPath;
	const sizes = ['desktop', 'tablet', 'mobile', 'preload'];

	await Promise.all(sizes.map((size) => wrapCompression(modulePath, size, 'IMAGE', file, compressInfo)));

	// Filter out undefined values
	return Object.fromEntries(Object.entries(compressInfo).filter(([_, v]) => v !== undefined)) as Record<string, string>;
}

export default {
	wrapCompression,
	getRelativePath,
	compressImageFile,
};
