import vine, { SimpleMessagesProvider } from '@vinejs/vine';
import { TitleTypes, DescriptionTypes, ContributorTypes, ReferenceIdentifierTypes, RelationTypes } from '#contracts/enums';
import dayjs from 'dayjs';
import MimeType from '#models/mime_type';

const enabledExtensions = await MimeType.query().select('file_extension').where('enabled', true).exec();
const extensions = enabledExtensions
    .map((extension) => {
        return extension.file_extension.split('|');
    })
    .flat();

/**
 * Validates the dataset's creation action
 * node ace make:validator dataset
 */
export const createDatasetValidator = vine.compile(
    vine.object({
        // first step
        language: vine
            .string()
            .trim()
            .regex(/^[a-zA-Z0-9]+$/),
        licenses: vine.array(vine.number()).minLength(1), // define at least one license for the new dataset
        rights: vine.string().in(['true']),
        // second step
        type: vine.string().trim().minLength(3).maxLength(255),
        creating_corporation: vine.string().trim().minLength(3).maxLength(255),
        titles: vine
            .array(
                vine.object({
                    value: vine.string().trim().minLength(3).maxLength(255),
                    type: vine.enum(Object.values(TitleTypes)),
                    language: vine
                        .string()
                        .trim()
                        .minLength(2)
                        .maxLength(255)
                        .translatedLanguage({ mainLanguageField: 'language', typeField: 'type' }),
                }),
            )
            .minLength(1),
        descriptions: vine
            .array(
                vine.object({
                    value: vine.string().trim().minLength(3).maxLength(2500),
                    type: vine.enum(Object.values(DescriptionTypes)),
                    language: vine
                        .string()
                        .trim()
                        .minLength(2)
                        .maxLength(255)
                        .translatedLanguage({ mainLanguageField: 'language', typeField: 'type' }),
                }),
            )
            .minLength(1),
        authors: vine
            .array(
                vine.object({
                    email: vine
                        .string()
                        .trim()
                        .maxLength(255)
                        .email()
                        .normalizeEmail()
                        .isUniquePerson({ table: 'persons', column: 'email', idField: 'id' }),
                    first_name: vine.string().trim().minLength(3).maxLength(255),
                    last_name: vine.string().trim().minLength(3).maxLength(255),
                }),
            )
            .minLength(1)
            .distinct('email'),
        contributors: vine
            .array(
                vine.object({
                    email: vine
                        .string()
                        .trim()
                        .maxLength(255)
                        .email()
                        .normalizeEmail()
                        .isUniquePerson({ table: 'persons', column: 'email', idField: 'id' }),
                    first_name: vine.string().trim().minLength(3).maxLength(255),
                    last_name: vine.string().trim().minLength(3).maxLength(255),
                    pivot_contributor_type: vine.enum(Object.keys(ContributorTypes)),
                }),
            )
            .distinct('email')
            .optional(),
        // third step
        project_id: vine.number().optional(),
        // embargo_date: schema.date.optional({ format: 'yyyy-MM-dd' }, [rules.after(10, 'days')]),
        embargo_date: vine
            .date({
                formats: ['YYYY-MM-DD'],
            })
            .afterOrEqual((_field) => {
                return dayjs().add(10, 'day').format('YYYY-MM-DD');
            })
            .optional(),
        coverage: vine.object({
            x_min: vine.number(),
            x_max: vine.number(),
            y_min: vine.number(),
            y_max: vine.number(),
            elevation_absolut: vine.number().positive().optional(),
            elevation_min: vine.number().positive().optional().requiredIfExists('elevation_max'),
            elevation_max: vine.number().positive().optional().requiredIfExists('elevation_min'),
            // type: vine.enum(Object.values(DescriptionTypes)),
            depth_absolut: vine.number().negative().optional(),
            depth_min: vine.number().negative().optional().requiredIfExists('depth_max'),
            depth_max: vine.number().negative().optional().requiredIfExists('depth_min'),
        }),
        references: vine
            .array(
                vine.object({
                    value: vine.string().trim().minLength(3).maxLength(255),
                    type: vine.enum(Object.values(ReferenceIdentifierTypes)),
                    relation: vine.enum(Object.values(RelationTypes)),
                    label: vine.string().trim().minLength(2).maxLength(255),
                }),
            )
            .optional(),
        subjects: vine
            .array(
                vine.object({
                    value: vine.string().trim().minLength(3).maxLength(255),
                    // pivot_contributor_type: vine.enum(Object.keys(ContributorTypes)),
                    language: vine.string().trim().minLength(2).maxLength(255),
                }),
            )
            .minLength(3)
            .distinct('value'),
        // last step
        files: vine
            .array(
                vine
                    .myfile({
                        size: '512mb',
                        extnames: extensions,
                    })
                    .filenameLength({ clientNameSizeLimit: 100 })
                    .fileScan({ removeInfected: true }),
            )
            .minLength(1),
    }),
);

/**
 * Validates the dataset's update action
 */
export const updateDatasetValidator = vine.compile(
    vine.object({
        // first step
        language: vine
            .string()
            .trim()
            .regex(/^[a-zA-Z0-9]+$/),
        licenses: vine.array(vine.number()).minLength(1), // define at least one license for the new dataset
        rights: vine.string().in(['true']),
        // second step
        type: vine.string().trim().minLength(3).maxLength(255),
        creating_corporation: vine.string().trim().minLength(3).maxLength(255),
        titles: vine
            .array(
                vine.object({
                    value: vine.string().trim().minLength(3).maxLength(2500),
                    type: vine.enum(Object.values(TitleTypes)),
                    language: vine
                        .string()
                        .trim()
                        .minLength(2)
                        .maxLength(255)
                        .translatedLanguage({ mainLanguageField: 'language', typeField: 'type' }),
                }),
            )
            .minLength(1),
        descriptions: vine
            .array(
                vine.object({
                    value: vine.string().trim().minLength(3).maxLength(255),
                    type: vine.enum(Object.values(DescriptionTypes)),
                    language: vine
                        .string()
                        .trim()
                        .minLength(2)
                        .maxLength(255)
                        .translatedLanguage({ mainLanguageField: 'language', typeField: 'type' }),
                }),
            )
            .minLength(1),
        authors: vine
            .array(
                vine.object({
                    email: vine
                        .string()
                        .trim()
                        .maxLength(255)
                        .email()
                        .normalizeEmail()
                        .isUniquePerson({ table: 'persons', column: 'email', idField: 'id' }),
                    first_name: vine.string().trim().minLength(3).maxLength(255),
                    last_name: vine.string().trim().minLength(3).maxLength(255),
                }),
            )
            .minLength(1)
            .distinct('email'),
        contributors: vine
            .array(
                vine.object({
                    email: vine
                        .string()
                        .trim()
                        .maxLength(255)
                        .email()
                        .normalizeEmail()
                        .isUniquePerson({ table: 'persons', column: 'email', idField: 'id' }),
                    first_name: vine.string().trim().minLength(3).maxLength(255),
                    last_name: vine.string().trim().minLength(3).maxLength(255),
                    pivot_contributor_type: vine.enum(Object.keys(ContributorTypes)),
                }),
            )
            .distinct('email')
            .optional(),
        // third step
        project_id: vine.number().optional(),
        // embargo_date: schema.date.optional({ format: 'yyyy-MM-dd' }, [rules.after(10, 'days')]),
        embargo_date: vine
            .date({
                formats: ['YYYY-MM-DD'],
            })
            .afterOrEqual((_field) => {
                return dayjs().add(10, 'day').format('YYYY-MM-DD');
            })
            .optional(),
        coverage: vine.object({
            x_min: vine.number(),
            x_max: vine.number(),
            y_min: vine.number(),
            y_max: vine.number(),
            elevation_absolut: vine.number().positive().optional(),
            elevation_min: vine.number().positive().optional().requiredIfExists('elevation_max'),
            elevation_max: vine.number().positive().optional().requiredIfExists('elevation_min'),
            // type: vine.enum(Object.values(DescriptionTypes)),
            depth_absolut: vine.number().negative().optional(),
            depth_min: vine.number().negative().optional().requiredIfExists('depth_max'),
            depth_max: vine.number().negative().optional().requiredIfExists('depth_min'),
        }),
        references: vine
            .array(
                vine.object({
                    value: vine.string().trim().minLength(3).maxLength(255),
                    type: vine.enum(Object.values(ReferenceIdentifierTypes)),
                    relation: vine.enum(Object.values(RelationTypes)),
                    label: vine.string().trim().minLength(2).maxLength(255),
                }),
            )
            .optional(),
        subjects: vine
            .array(
                vine.object({
                    value: vine.string().trim().minLength(3).maxLength(255),
                    // pivot_contributor_type: vine.enum(Object.keys(ContributorTypes)),
                    language: vine.string().trim().minLength(2).maxLength(255),
                }),
            )
            .minLength(3)
            .distinct('value'),
        // last step
        files: vine.array(
            vine.myfile({
                size: '512mb',
                extnames: extensions,
            }),
        ),
        // .minLength(1),
    }),
);

// files: schema.array([rules.minLength(1)]).members(
//     schema.file({
//         size: '512mb',
//         extnames: ['jpg', 'gif', 'png', 'tif', 'pdf', 'zip', 'fgb', 'nc', 'qml', 'ovr', 'gpkg', 'gml', 'gpx', 'kml', 'kmz', 'json'],
//     }),
// ),

let messagesProvider = new SimpleMessagesProvider({
    'minLength': '{{ field }} must be at least {{ min }} characters long',
    'maxLength': '{{ field }} must be less then {{ max }} characters long',
    'required': '{{ field }} is required',
    'unique': '{{ field }} must be unique, and this value is already taken',
    // 'confirmed': '{{ field }} is not correct',
    'licenses.minLength': 'at least {{ min }} permission must be defined',
    'licenses.*.number': 'Define roles as valid numbers',
    'rights.in': 'you must agree to continue',

    'titles.0.value.minLength': 'Main Title  must be at least {{ min }} characters long',
    'titles.0.value.required': 'Main Title is required',
    'titles.*.value.required': 'Additional title is required, if defined',
    'titles.*.type.required': 'Additional title type is required',
    'titles.*.language.required': 'Additional title language is required',
    'titles.*.language.translatedLanguage': 'The language of the translated title must be different from the language of the dataset',

    'descriptions.0.value.minLength': 'Main Abstract  must be at least {{ min }} characters long',
    'descriptions.0.value.maxLength': 'Main Abstract  must be less than {{ max }} characters long',
    'descriptions.0.value.required': 'Main Abstract is required',
    'descriptions.*.value.required': 'Additional description is required, if defined',
    'descriptions.*.type.required': 'Additional description type is required',
    'descriptions.*.language.required': 'Additional description language is required',
    'descriptions.*.language.translatedLanguage':
        'The language of the translated description must be different from the language of the dataset',

    'authors.array.minLength': 'at least {{ min }} author must be defined',
    'authors.distinct': 'The {{ field }} array must have unique values based on the {{ fields }} attribute.',
    'authors.*.email.isUnique': 'the email of the new creator already exists in the database',
    'contributors.*.pivot_contributor_type.required': 'contributor type is required, if defined',
    'contributors.distinct': 'The {{ field }} array must have unique values based on the {{ fields }} attribute.',

    'after': `{{ field }} must be older than ${dayjs().add(10, 'day')}`,

    'subjects.array.minLength': 'at least {{ min }} keywords must be defined',
    'subjects.*.value.required': 'keyword value is required',
    'subjects.*.value.minLength': 'keyword value  must be at least {{ min }} characters long',
    'subjects.*.type.required': 'keyword type is required',
    'subjects.*.language.required': 'language of keyword is required',
    'subjects.distinct': 'The {{ field }} array must have unique values based on the {{ fields }} attribute.',

    'references.*.value.required': 'Additional reference value is required, if defined',
    'references.*.type.required': 'Additional reference identifier type is required',
    'references.*.relation.required': 'Additional reference relation type is required',
    'references.*.label.required': 'Additional reference label is required',

    'files.array.minLength': 'At least {{ min }}  file upload is required.',
    'files.*.size': 'file size is to big',
    'files.*.extnames': 'file extension is not supported',
});

createDatasetValidator.messagesProvider = messagesProvider;
updateDatasetValidator.messagesProvider = messagesProvider;
// export default createDatasetValidator;
