/*
 * Copyright (c) 2022.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */

import {AuthUserImageType} from "@chamesu/common/domains/auth/user/type";
import {Request} from "express";
import fs from "fs";
import path from "path";
import {v4} from "uuid";
import {
    createQueueMessageTemplate,
    publishMessageQueue
} from "../../../../modules/message-queue";
import {ImageCropCoordinates, ImageCropResizeConfig} from "@chamesu/common/domains/image/crop";

export async function uploadUserImageWithRequest(request: Request, destPath: string, userId: number) : Promise<{
    type: string,
    path: string,
    original: string,
    coordinates: ImageCropCoordinates
}>{
    const { coordinatesWidth, coordinatesHeight, coordinatesLeft, coordinatesTop } = <Record<string,any>> request.body;

    const width : number = parseInt(coordinatesWidth);
    const height : number = parseInt(coordinatesHeight);
    const left : number = parseInt(coordinatesLeft);
    const top : number = parseInt(coordinatesTop);

    if(Number.isNaN(width) || Number.isNaN(height) || Number.isNaN(left) || Number.isNaN(top)) {
        throw new Error('Coordinaten des Bildes sind nicht gültig.');
    }

    const coordinates : ImageCropCoordinates = {width, height, left, top}

    // @ts-ignore
    const type : string | undefined = !!request.files.cover ? 'cover' : (!!request.files.avatar ? 'avatar' : undefined);
    if(typeof type === 'undefined') {
        throw new Error('Es wurde weder ein Avatar noch ein Cover zum Upload angegeben...');
    }

    // @ts-ignore
    const buffer : Buffer = request.files[type][0].buffer;

    const mimeTypesAllowed : string[] = [
        'image/jpeg',
        'image/png',
        'image/jpg',
        'image/svg+xml',
        'image/tiff',
        'image/gif'
    ];

    // @ts-ignore
    const mimeType : string = request.files[type][0].mimetype;
    if(mimeTypesAllowed.indexOf(mimeType) === -1) {
        throw new Error('Der mimetype '+mimeType+' ist nicht als Bildformat erlaubt.');
    }

    const upload = await uploadUserImage(destPath, userId, <AuthUserImageType> type, buffer, coordinates);

    let queueMessage = createQueueMessageTemplate();
    queueMessage.type = 'resize';
    queueMessage.data = {
        srcFilePath: upload.filePath,
        cropCoordinates: coordinates,
        cropResizeConfig: upload.cropResizeConfig,
        type: 'user-'+type,
        id: userId
    };

    publishMessageQueue('image.command', queueMessage).then(r => r);

    return {
        type,
        path: upload.urlPath,
        original: upload.fileName,
        coordinates
    }
}

export async function uploadUserImage(
    destPath: string,
    userId: number,
    type: AuthUserImageType,
    image: Buffer,
    coordinates : ImageCropCoordinates
) : Promise<{
    fileName: string,
    filePath: string,
    directoryPath: string,
    urlPath: string,
    cropResizeConfig: ImageCropResizeConfig
}> {
    const directoryPath : string = path.resolve(destPath, 'user', type, userId.toString());

    // clear existing avatar and sizes
    if(fs.existsSync(directoryPath)) {
        fs.rmdirSync(directoryPath, {recursive: true});
    }

    try {
        await fs.promises.stat(directoryPath);
        await fs.promises.chmod(directoryPath, 0o775);
    } catch (e) {
        await fs.promises.mkdir(directoryPath, {
            mode: 0o775
        });
    }

    let cropResizeConfig : ImageCropResizeConfig | undefined;

    switch (type) {
        case "avatar":
            cropResizeConfig = UserTypeConfigs.avatar;
            break;
        case "cover":
            cropResizeConfig = UserTypeConfigs.cover;
            break;
    }

    const ratio : string = (coordinates.width / coordinates.height).toFixed(1);
    if(ratio !== cropResizeConfig.ratio) {
        throw new Error('Das Verhältnis des auszuschneidenen Teils ist nicht gültig.');
    }

    const fileName : string = v4()+'.png';
    const filePath : string = path.resolve(directoryPath, fileName);

    fs.writeFileSync(filePath, image);

    return {
        fileName,
        filePath,
        directoryPath,
        urlPath: 'static/user/'+type+'/'+userId+'/',
        cropResizeConfig
    };
}

export const UserTypeConfigs : Record<AuthUserImageType, ImageCropResizeConfig> = {
    avatar: {
        ratio: '1.0',
        sizes: [
            {
                name: 'large',
                width: 400
            },
            {
                name: 'medium',
                width: 200
            },
            {
                name: 'small',
                width: 128
            },
            {
                name: 'tiny',
                width: 64
            }
        ]
    },
    cover: {
        ratio: '3.6',
        sizes: [
            {
                name: 'large',
                width: 3600
            },
            {
                name: 'small',
                width: 1800
            },
            {
                name: 'tiny',
                width: 900
            }
        ]
    }
}
