/*
|--------------------------------------------------------------------------
| Provider File - node ace make:provider vinejsProvider
|--------------------------------------------------------------------------
|*/
import type { ApplicationService } from '@adonisjs/core/types';
import vine, { BaseLiteralType, Vine } from '@vinejs/vine';
import type { Validation, FieldContext, FieldOptions } from '@vinejs/vine/types';
// import type { MultipartFile, FileValidationOptions } from '@adonisjs/bodyparser/types';
import type { MultipartFile } from '@adonisjs/core/bodyparser';
import type { FileValidationOptions } from '@adonisjs/core/types/bodyparser';
import { Request, RequestValidator } from '@adonisjs/core/http';
import MimeType from '#models/mime_type';

/**
 * Validation options accepted by the "file" rule
 */
export type FileRuleValidationOptions = Partial<FileValidationOptions> | ((field: FieldContext) => Partial<FileValidationOptions>);
/**
 * Extend VineJS
 */
declare module '@vinejs/vine' {
    interface Vine {
        myfile(options?: FileRuleValidationOptions): VineMultipartFile;
    }
}
/**
 * Extend HTTP request class
 */
declare module '@adonisjs/core/http' {
  interface Request extends RequestValidator {
  }
}

/**
 * Checks if the value is an instance of multipart file
 * from bodyparser.
 */
export function isBodyParserFile(file: MultipartFile | unknown): boolean {
    return !!(file && typeof file === 'object' && 'isMultipartFile' in file);
}
async function getEnabledExtensions() {
    const enabledExtensions = await MimeType.query().select('file_extension').where('enabled', true).exec();
    const extensions = enabledExtensions
        .map((extension) => {
            return extension.file_extension.split('|');
        })
        .flat();

    return extensions;
};
/**
 * VineJS validation rule that validates the file to be an
 * instance of BodyParser MultipartFile class.
 */
const isMultipartFile = vine.createRule(async (file: MultipartFile | unknown, options: FileRuleValidationOptions, field: FieldContext) => {
    /**
     * Report error when value is not a field multipart
     * file object
     */
    if (!isBodyParserFile(file)) {
        field.report('The {{ field }} must be a file', 'file', field);
        return;
    }
    // At this point, you can use type assertion to explicitly tell TypeScript that file is of type MultipartFile
    const validatedFile = file as MultipartFile;
    const validationOptions = typeof options === 'function' ? options(field) : options;
    /**
     * Set size when it's defined in the options and missing
     * on the file instance
     */
    if (validatedFile.sizeLimit === undefined && validationOptions.size) {
        validatedFile.sizeLimit = validationOptions.size;
    }
    /**
     * Set extensions when it's defined in the options and missing
     * on the file instance
     */
    // if (validatedFile.allowedExtensions === undefined && validationOptions.extnames) {
    //     validatedFile.allowedExtensions = validationOptions.extnames;
    // }
    if (validatedFile.allowedExtensions === undefined && validationOptions.extnames) {
        validatedFile.allowedExtensions = await getEnabledExtensions();
    }
     /**
      * wieder löschen
     * Set extensions when it's defined in the options and missing
     * on the file instance
     */
    //  if (file.clientNameSizeLimit === undefined && validationOptions.clientNameSizeLimit) {
    //     file.clientNameSizeLimit = validationOptions.clientNameSizeLimit;
    // }
    /**
     * Validate file
     */
    validatedFile.validate();
    /**
     * Report errors
     */
    validatedFile.errors.forEach((error) => {
        field.report(error.message, `file.${error.type}`, field, validationOptions);
    });
});

export class VineMultipartFile extends BaseLiteralType<MultipartFile, MultipartFile, MultipartFile> {
    // #private;
    // constructor(validationOptions?: FileRuleValidationOptions, options?: FieldOptions, validations?: Validation<any>[]);
    // clone(): this;

    public validationOptions;
    // extnames: (18) ['gpkg', 'htm', 'html', 'csv', 'txt', 'asc', 'c', 'cc', 'h', 'srt', 'tiff', 'pdf', 'png', 'zip', 'jpg', 'jpeg', 'jpe', 'xlsx']
    // size: '512mb'

    public constructor(validationOptions?: FileRuleValidationOptions, options?: FieldOptions, validations?: Validation<any>[]) {
        // super(options, validations);
        super(options, [isMultipartFile(validationOptions || {})]);
        this.validationOptions = validationOptions;
    }

    public clone(): any {
        return new VineMultipartFile(this.validationOptions, this.cloneOptions(), this.cloneValidations());
    }
}

export default class VinejsProvider {
    protected app: ApplicationService;

    constructor(app: ApplicationService) {
        this.app = app;
        this.app.usingVineJS = true;
    }

    /**
     * Register bindings to the container
     */
    register() {}

    /**
     * The container bindings have booted
     */

    boot(): void {
        //   VineString.macro('translatedLanguage', function (this: VineString, options: Options) {
        //     return this.use(translatedLanguageRule(options));
        // });

        Vine.macro('myfile', function (this: Vine, options) {
            return new VineMultipartFile(options);
        });

        /**
         * The validate method can be used to validate the request
         * data for the current request using VineJS validators
         */
        Request.macro('validateUsing', function (...args) {
          return new RequestValidator(this.ctx).validateUsing(...args);
      });

    }

    /**
     * The application has been booted
     */
    async start() {}

    /**
     * The process has been started
     */
    async ready() {}

    /**
     * Preparing to shutdown the app
     */
    async shutdown() {}
}
