/*
|--------------------------------------------------------------------------
| Preloaded File - node ace make:preload rules/fileScan
|--------------------------------------------------------------------------
|*/

import { FieldContext } from '@vinejs/vine/types';
import vine, { errors } from '@vinejs/vine';

// import { VineString } from '@vinejs/vine';
import { VineMultipartFile, isBodyParserFile } from '#providers/vinejs_provider';
import type { MultipartFile } from '@adonisjs/core/bodyparser';
import ClamScan from 'clamscan';

/**
 * Options accepted by the unique rule
 */
// type Options = {
//     mainLanguageField: string;
//     typeField: string;
// };
type Options = {
    // size: string | number;
    // extnames: string[];
    removeInfected: boolean;
    // debugMode?: boolean;
    // scanRecursively?: boolean;
    host?: string;
    port?: number;
    // clamdscan: {
    //     active: boolean;
    //     host: string;
    //     port: number;
    //     multiscan: boolean;
    // };
    // preference: string;
};

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.host, options.port); //, 'gitea.lan', 3310);
        // await this.scanFileForViruses("/tmp/testfile.txt");
    } 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, host?: string, port?: number): Promise<void> {
    // const clamscan = await (new ClamScan().init());
    const opts: ClamScan.Options = {
        removeInfected: true, // If true, removes infected files
        debugMode: false, // Whether or not to log info/debug/error msgs to the console
        scanRecursively: true, // If true, deep scan folders recursively
        clamdscan: {
            active: true, // If true, this module will consider using the clamdscan binary
            host,
            port,
            multiscan: true, // Scan using all available cores! Yay!
        },
        preference: 'clamdscan', // If clamdscan is found and active, it will be used by default
    };

    return new Promise(async (resolve, reject) => {
        try {
            const clamscan = await new ClamScan().init(opts);
            // You can re-use the `clamscan` object as many times as you want
            // const version = await clamscan.getVersion();
            // console.log(`ClamAV Version: ${version}`);
            const { file, isInfected, viruses } = await clamscan.isInfected(filePath);
            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!` }));
            } 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));
});
