import { Readable } from 'stream';
import { NextFunction, Request } from 'express';
import multer from 'multer';
import { BadRequestError } from '../../business/errors/badRequestError';
import { ErrorCodes } from '../../business/errors/errorCodes';
import { HumandResponse } from '../types/response';

export const createBulkFilesMimeTypesRegex = new RegExp('application/zip-compressed|application/zip|application/x-zip-compressed');

export const getHumandUpload = (maxFileSize: number) => multer({ limits: { fileSize: maxFileSize }, storage: multer.memoryStorage() });

export const getFileMiddlewareByUploadType =
    (upload: multer.Multer) =>
    (paramName = 'file') =>
    (req: Request, res: HumandResponse, next: NextFunction) => {
        const uploadHandler = upload.single(paramName);
        uploadHandler(req, res, (err) => {
            if (err instanceof multer.MulterError && err.code === 'LIMIT_UNEXPECTED_FILE') {
                return next(new BadRequestError(`The file content should be contained on ${paramName} param`, ErrorCodes.INVALID_DATA));
            } else if (err) {
                return next(err);
            }
            return next();
        });
    };

export const getMimeTypesCheckerByRegex =
    (regex: RegExp) =>
    (req: Request, res: HumandResponse, next: NextFunction): void => {
        const { file } = req;
        if (!file || file.size === 0) return next(new BadRequestError('The provided file cannot be empty', ErrorCodes.INVALID_DATA));
        if (!regex.test(file.mimetype)) {
            return next(new BadRequestError('The provided file mimetype is not accepted', ErrorCodes.INVALID_DATA));
        }
        next();
    };

interface Dimensions {
    width: number;
    height: number;
}

export const AspectRatio = {
    '16:9': { height: 9, width: 16 } as Dimensions,
    '1:1': { height: 1, width: 1 } as Dimensions,
};

export const getBase64Middleware = (paramName = 'file') => {
    return (req: Request, _res: unknown, next: NextFunction) => {
        const body = req.body as Record<string, string | undefined>;
        const base64String = body[paramName] as string;
        if (!base64String) {
            return next(new BadRequestError(`The request must contain a ${paramName} field with a base64 encoded file`, ErrorCodes.INVALID_DATA));
        }

        const matches = base64String.match(/^data:(.+);base64,(.+)$/);
        if (!matches || matches.length !== 3) {
            return next(new BadRequestError('The provided base64 string is not properly formatted', ErrorCodes.INVALID_DATA));
        }

        const mimeType = matches[1];
        const base64Content = matches[2];
        if (!['image/jpeg', 'image/png'].includes(mimeType)) {
            return next(new BadRequestError('The provided image format is not supported. Only JPEG and PNG are allowed.', ErrorCodes.INVALID_DATA));
        }
        const extension = mimeType === 'image/jpeg' ? 'jpg' : 'png';
        const buffer = Buffer.from(base64Content, 'base64');

        const stream = new Readable();
        stream.push(buffer);
        stream.push(null);

        // Generate a file with base64 to use te same methods
        req.file = {
            buffer,
            destination: '',
            encoding: '7bit',
            fieldname: paramName,
            filename: '',
            mimetype: mimeType,
            originalname: `${paramName}-${Date.now()}.${extension}`,
            path: '',
            size: buffer.length,
            stream,
        };

        next();
    };
};
