import path from 'path';
import fs from 'fs/promises';
import { getFileTypeFromMimetype } from '#services/file-system/uploadService';
import { compressImageFile, getRelativePath } from '#services/data/dataHelperService';
import deleteFileService from '#services/file-system/deleteService';
import { verifyMagicBytes, sanitizeFilename, calcChecksum } from '#utils/fileSecurityUtils';
import { scanFile } from '#services/file-system/virusScanService';
import { getStorageAdapter } from '#services/storage/adapters/StorageFactory';
import { Errors } from '#interfaces/error';

export interface ProcessedUploadResult {
	storagePath: string;
	storageProvider: 'local' | 'minio' | 's3';
	publicUrl: string | null;
	compressInfo: Record<string, string> | null;
	checksum: string;
	safeFileName: string;
	decodedName: string;
	fileType: string;
}

/**
 * Shared file upload pipeline: magic-byte check → virus scan →
 * compress (images) → checksum → storage upload → compressed-variant upload.
 *
 * On validation failure the temp file is removed before throwing.
 * On storage failure the already-uploaded main file is rolled back before throwing.
 */
export async function processAndUpload(
	file: Express.Multer.File,
	storageAdapter: ReturnType<typeof getStorageAdapter>,
): Promise<ProcessedUploadResult> {
	// 1. Magic-bytes check — prevent MIME-type spoofing
	const magicOk = await verifyMagicBytes(file.path, file.mimetype);
	if (!magicOk) {
		await deleteFileService({ filePath: file.path, compressInfo: null });
		throw Errors.validation([
			{
				field: 'file',
				code: 'INVALID_FORMAT',
				message: {
					vi: 'File không hợp lệ: nội dung không khớp với định dạng khai báo',
					en: 'Invalid file: content does not match declared format',
				},
			},
		]);
	}

	// 2. Virus scan (no-op when VIRUS_SCAN_ENABLED != true)
	const scanResult = await scanFile(file.path);
	if (!scanResult.isClean) {
		await deleteFileService({ filePath: file.path, compressInfo: null });
		throw Errors.validation([
			{
				field: 'file',
				code: 'MALWARE_DETECTED',
				message: {
					vi: `File bị từ chối: phát hiện mã độc (${scanResult.virusName})`,
					en: `File rejected: malware detected (${scanResult.virusName})`,
				},
			},
		]);
	}

	// 3. Compute metadata
	const fileType = getFileTypeFromMimetype(file.mimetype);
	let compressInfo = fileType === 'IMAGE' ? await compressImageFile(file) : null;
	const checksum = await calcChecksum(file.path);
	const decodedName = Buffer.from(file.originalname, 'latin1').toString('utf8');
	const safeFileName = sanitizeFilename(decodedName);
	const destKey = getRelativePath(file);

	// 4. Upload main file via storage adapter
	const storageResult = await storageAdapter.upload(file.path, destKey, file.mimetype);

	// 5. For object storage: upload compressed variants to bucket
	if (storageResult.storageProvider !== 'local' && compressInfo) {
		const updatedCompressInfo: Record<string, string> = {};
		for (const [size, localPath] of Object.entries(compressInfo)) {
			const diskPath = path.join(file.destination, path.basename(localPath));
			const compressKey = localPath.replace(/^\/+/, '');
			try {
				const cr = await storageAdapter.upload(diskPath, compressKey, 'image/webp');
				updatedCompressInfo[size] = cr.storagePath;
			} catch {
				// P1-3: log warning but don't roll back main file — compressed variants are optional
				// Skip failed variants instead of using invalid local path for object storage
				console.warn(`[FileUploadPipeline] Failed to upload compressed variant ${size}, skipping`);
			}
			await fs.unlink(diskPath).catch(() => {});
		}
		compressInfo = Object.keys(updatedCompressInfo).length > 0 ? updatedCompressInfo : null;
	}

	return {
		storagePath: storageResult.storagePath,
		storageProvider: storageResult.storageProvider,
		publicUrl: storageResult.publicUrl,
		compressInfo,
		checksum,
		safeFileName,
		decodedName,
		fileType,
	};
}
