/*
|--------------------------------------------------------------------------
| Preloaded File - node ace make:preload rules/fileScan
|--------------------------------------------------------------------------
|*/
import { FieldContext } from '@vinejs/vine/types';
import vine, { errors } from '@vinejs/vine';
import { VineMultipartFile, isBodyParserFile } from '#providers/vinejs_provider';
import type { MultipartFile } from '@adonisjs/core/bodyparser';
import ClamScan from 'clamscan';

type Options = {
    removeInfected: boolean;
    host?: string;
    port?: number;
};

async function fileScan(file: VineMultipartFile | unknown, options: Options, field: FieldContext) {
    // if (typeof value !== 'string' && typeof value != 'number') {
    //     return;
    // }
    if (!isBodyParserFile(file)) {
        return;
    }
    const validatedFile = file as MultipartFile;

    try {
        await scanFileForViruses(validatedFile.tmpPath, options);
    } catch (error) {
        // If the file is infected or there's an error scanning the file, throw a validation exception
        // throw error;
        field.report(`Upload error. Code: ${error.code} message: ${error.messages.uploadError}`, 'fileScan', field);
    }
}

async function scanFileForViruses(filePath: string | undefined, options: Options): Promise<void> {
    if (!filePath) {
        throw new errors.E_VALIDATION_ERROR({ uploadError: 'File path is undefined!' });
    }
    const opts: ClamScan.Options = {
        removeInfected: options.removeInfected, // If true, removes infected files
        debugMode: false, // If true, deep scan folders recursively
        scanRecursively: true, // If true, deep scan folders recursively
        clamdscan: {
            active: true, // If true, this module will consider using the clamdscan binary
            host: options.host,
            port: options.port,
            multiscan: true, // Scan using all available cores! Yay!
        },
    };

    const clamscan = await new ClamScan().init(opts);
    return new Promise(async (resolve, reject) => {
        try {
            // You can re-use the `clamscan` object as many times as you want
            // const version = await clamscan.getVersion();
            // console.log(`ClamAV Version: ${version}`);
            const result = await clamscan.isInfected(filePath);
            if (!result || typeof result.isInfected === 'undefined') {
                reject(new errors.E_VALIDATION_ERROR({ uploadError: 'Unexpected response from virus scan!' }));
                return;
            }
            const { file, isInfected, viruses } = result;
            if (isInfected) {
                console.log(`${file} is infected with ${viruses}!`); // reject(new ValidationException(true, { 'upload error': `File ${file} is infected!` }));
                reject(new errors.E_VALIDATION_ERROR({ uploadError: `File ${file} is infected with ${viruses}!` }));
            } else {
                resolve();
            }
        } catch (error) {
            // If there's an error scanning the file, throw a validation exception
            // reject(new ValidationException(true, { 'upload error': `${error.message}` }));
            reject(new errors.E_VALIDATION_ERROR({ uploadError: `${error.message}!` }));
        }
    });
}

export const fileScanRule = vine.createRule(fileScan);

declare module '#providers/vinejs_provider' {
    interface VineMultipartFile {
        fileScan(options: Options): this;
    }
}

VineMultipartFile.macro('fileScan', function (this: VineMultipartFile, options: Options) {
    return this.use(fileScanRule(options));
});
