import fs from 'fs';
import multer from 'multer';
import path from 'path';
import { Req } from '#interfaces/IApi';
import { Request } from 'express';
import { sanitizeFilename, isBlockedExtension, validateMimeExtensionMatch } from '#utils/fileSecurityUtils';
import Config from '#config';

// Extend Express Request with upload-specific properties set by middleware
declare global {
	namespace Express {
		interface Request {
			/** Sub-folder path within the upload root, set before calling multer */
			imagePath?: string;
			/** Extra suffix appended to the filename before the extension */
			extendName?: string;
		}
	}
}

const fileTypeFn = (type = 'DEFAULT') => {
	switch (type) {
		case 'IMAGE':
			return 'images';
		case 'VIDEO':
			return 'videos';
		case 'DOCUMENT':
			return 'documents';
		default:
			return 'files';
	}
};

const DOCUMENT_MIME_TYPES = new Set([
	'application/pdf',
	'application/msword',
	'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
	'application/vnd.ms-excel',
	'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
]);

/**
 * Determine file type based on mimetype
 * @param mimetype - The mimetype of the file
 * @returns File type: "IMAGE", "VIDEO", "DOCUMENT", or "DEFAULT"
 */
export const getFileTypeFromMimetype = (mimetype: string): 'IMAGE' | 'VIDEO' | 'DOCUMENT' | 'DEFAULT' => {
	if (mimetype.startsWith('image/')) return 'IMAGE';
	if (mimetype.startsWith('video/')) return 'VIDEO';
	if (DOCUMENT_MIME_TYPES.has(mimetype)) return 'DOCUMENT';
	return 'DEFAULT';
};
const storage = multer.diskStorage({
	destination: function (req: Request, file: Express.Multer.File, cb: (arg0: null, arg1: string) => void) {
		const customReq = req as Req;
		const fileType = getFileTypeFromMimetype(file.mimetype);
		const storagePath = path.resolve(Config.uploads.path, fileTypeFn(fileType), <string>customReq.imagePath ?? '');
		fs.mkdirSync(storagePath, { recursive: true });
		cb(null, storagePath);
	},
	filename: function (
		req: Request,
		file: { fieldname: string; originalname: string },
		cb: (arg0: null, arg1: string) => void,
	) {
		const customReq = req as Req;
		const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
		// Decode latin1→utf8 (multer encoding), then sanitize to prevent path traversal
		const decodedName = Buffer.from(file.originalname, 'latin1').toString('utf8');
		const safeExt = path.extname(sanitizeFilename(decodedName)).toLowerCase();
		cb(null, `${file.fieldname}-${uniqueSuffix}${customReq.extendName ?? ''}${safeExt}`);
	},
});
export const ALLOWED_MIME_TYPES: readonly string[] = [
	'image/jpeg',
	'image/jpg',
	'image/png',
	'image/webp',
	'video/mp4',
	'video/quicktime',
	'video/x-msvideo',
	'video/x-ms-wmv',
	'video/x-matroska',
	'video/webm',
	'video/mpeg',
	'video/3gpp',
	'video/3gpp2',
	'video/ogg',
	'application/msword',
	'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
	'application/vnd.ms-excel',
	'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
	'application/pdf',
	'application/zip',
	'application/x-zip-compressed',
	'application/x-rar-compressed',
	'application/vnd.rar',
];

const fileFilter = (req: Request, file: Express.Multer.File, cb: multer.FileFilterCallback) => {
	// Block dangerous extensions first (before MIME check)
	if (isBlockedExtension(file.originalname)) {
		return cb(new Error(`Loại file không được phép: ${path.extname(file.originalname)}`));
	}

	// Cross-validate declared MIME type against file extension
	if (!validateMimeExtensionMatch(file.originalname, file.mimetype)) {
		return cb(
			new Error(
				`Extension file không khớp với định dạng khai báo: ${path.extname(file.originalname)} / ${file.mimetype}`,
			),
		);
	}

	if (ALLOWED_MIME_TYPES.includes(file.mimetype)) {
		cb(null, true);
	} else {
		cb(new Error(`Định dạng file không được hỗ trợ: ${file.mimetype}`));
	}
};

const MAX_FILE_SIZE = Config.uploads.maxSize;

export const upload = multer({
	storage: storage,
	fileFilter: fileFilter,
	limits: {
		fileSize: MAX_FILE_SIZE,
		files: 1,
		fields: 20,
	},
}).single('file');
export const uploadMultiple = multer({
	storage: storage,
	fileFilter: fileFilter,
	limits: {
		fileSize: MAX_FILE_SIZE,
		files: 10,
		fields: 30,
	},
}).array('files', 10);
