text: import {
    get,
    isBoolean,
    isNumber,
    isNull,
    isObject,
    isArray,
    isEmpty,
    isNaN,
    toNumber,
  } from 'lodash';
  import * as yup from 'yup';
  import { translatedErrors as errorsTrads } from 'strapi-helper-plugin';
  
  yup.addMethod(yup.mixed, 'defined', function() {
    return this.test('defined', errorsTrads.required, value => value !== undefined);
  });
  
  yup.addMethod(yup.array, 'notEmptyMin', function(min) {
    return this.test('notEmptyMin', errorsTrads.min, value => {
      if (isEmpty(value)) {
        return true;
      }
  
      return value.length >= min;
    });
  });
  
  yup.addMethod(yup.string, 'isInferior', function(message, max) {
    return this.test('isInferior', message, function(value) {
      if (!value) {
        return true;
      }
  
      if (Number.isNaN(toNumber(value))) {
        return true;
      }
  
      return toNumber(max) >= toNumber(value);
    });
  });
  
  yup.addMethod(yup.string, 'isSuperior', function(message, min) {
    return this.test('isSuperior', message, function(value) {
      if (!value) {
        return true;
      }
  
      if (Number.isNaN(toNumber(value))) {
        return true;
      }
  
      return toNumber(value) >= toNumber(min);
    });
  });
  
  const getAttributes = data => get(data, ['schema', 'attributes'], {});
  
  const createYupSchema = (model, { components }) => {
    const attributes = getAttributes(model);
  
    return yup.object().shape(
      Object.keys(attributes).reduce((acc, current) => {
        const attribute = attributes[current];
  
        if (
          attribute.type !== 'relation' &&
          attribute.type !== 'component' &&
          attribute.type !== 'dynamiczone'
        ) {
          const formatted = createYupSchemaAttribute(attribute.type, attribute);
          acc[current] = formatted;
        }
  
        if (attribute.type === 'relation') {
          acc[current] = [
            'oneWay',
            'oneToOne',
            'manyToOne',
            'oneToManyMorph',
            'oneToOneMorph',
          ].includes(attribute.relationType)
            ? yup.object().nullable()
            : yup.array().nullable();
        }
  
        if (attribute.type === 'component') {
          const componentFieldSchema = createYupSchema(components[attribute.component], {
            components,
          });
  
          if (attribute.repeatable === true) {
            const { min, max, required } = attribute;
            let componentSchema = yup.lazy(value => {
              let baseSchema = yup.array().of(componentFieldSchema);
  
              if (min) {
                if (required) {
                  baseSchema = baseSchema.min(min, errorsTrads.min);
                } else if (required !== true && isEmpty(value)) {
                  baseSchema = baseSchema.nullable();
                } else {
                  baseSchema = baseSchema.min(min, errorsTrads.min);
                }
              }
  
              if (max) {
                baseSchema = baseSchema.max(max, errorsTrads.max);
              }
  
              return baseSchema;
            });
  
            acc[current] = componentSchema;
  
            return acc;
          }
          const componentSchema = yup.lazy(obj => {
            if (obj !== undefined) {
              return attribute.required === true
                ? componentFieldSchema.defined()
                : componentFieldSchema.nullable();
            }
  
            return attribute.required === true ? yup.object().defined() : yup.object().nullable();
          });
  
          acc[current] = componentSchema;
  
          return acc;
        }
  
        if (attribute.type === 'dynamiczone') {
          let dynamicZoneSchema = yup.array().of(
            yup.lazy(({ __component }) => {
              return createYupSchema(components[__component], { components });
            })
          );
  
          const { max, min } = attribute;
  
          if (attribute.required) {
            dynamicZoneSchema = dynamicZoneSchema.required();
  
            if (min) {
              dynamicZoneSchema = dynamicZoneSchema
                .min(min, errorsTrads.min)
                .required(errorsTrads.required);
            }
          } else {
            // eslint-disable-next-line no-lonely-if
            if (min) {
              dynamicZoneSchema = dynamicZoneSchema.notEmptyMin(min);
            }
          }
  
          if (max) {
            dynamicZoneSchema = dynamicZoneSchema.max(max, errorsTrads.max);
          }
  
          acc[current] = dynamicZoneSchema;
        }
  
        return acc;
      }, {})
    );
  };
  
  const createYupSchemaAttribute = (type, validations) => {
    let schema = yup.mixed();
  
    let regex = get(validations, 'regex', null);
    delete validations.regex;
  
    if (regex) {
      validations.regex = new RegExp(regex);
    }
  
    if (['string', 'uid', 'text', 'richtext', 'email', 'password', 'enumeration'].includes(type)) {
      schema = yup.string();
    }
  
    if (type === 'json') {
      schema = yup
        .mixed(errorsTrads.json)
        .test('isJSON', errorsTrads.json, value => {
          if (value === undefined) {
            return true;
          }
  
          if (isNumber(value) || isNull(value) || isObject(value) || isArray(value)) {
            return true;
          }
  
          try {
            JSON.parse(value);
  
            return true;
          } catch (err) {
            return false;
          }
        })
        .nullable();
    }
  
    if (type === 'email') {
      schema = schema.email(errorsTrads.email);
    }
  
    if (['number', 'integer', 'biginteger', 'float', 'decimal'].includes(type)) {
      schema = yup
        .number()
        .transform(cv => (isNaN(cv) ? undefined : cv))
        .typeError();
    }
  
    if (['date', 'datetime'].includes(type)) {
      schema = yup.date();
    }
  
    if (type === 'biginteger') {
      schema = yup.string().matches(/^\d*$/);
    }
  
    Object.keys(validations).forEach(validation => {
      const validationValue = validations[validation];
  
      if (
        !!validationValue ||
        (!isBoolean(validationValue) && Number.isInteger(Math.floor(validationValue))) ||
        validationValue === 0
      ) {
        switch (validation) {
          case 'required':
            schema = schema.required(errorsTrads.required);
            break;
          case 'max': {
            if (type === 'biginteger') {
              schema = schema.isInferior(errorsTrads.max, validationValue);
            } else {
              schema = schema.max(validationValue, errorsTrads.max);
            }
            break;
          }
          case 'maxLength':
            schema = schema.max(validationValue, errorsTrads.maxLength);
            break;
          case 'min': {
            if (type === 'biginteger') {
              schema = schema.isSuperior(errorsTrads.min, validationValue);
            } else {
              schema = schema.min(validationValue, errorsTrads.min);
            }
            break;
          }
          case 'minLength':
            schema = schema.min(validationValue, errorsTrads.minLength);
            break;
          case 'regex':
            schema = schema.matches(validationValue, errorsTrads.regex);
            break;
          case 'lowercase':
            if (['text', 'textarea', 'email', 'string'].includes(type)) {
              schema = schema.strict().lowercase();
            }
            break;
          case 'uppercase':
            if (['text', 'textarea', 'email', 'string'].includes(type)) {
              schema = schema.strict().uppercase();
            }
            break;
          case 'positive':
            if (['number', 'integer', 'bigint', 'float', 'decimal'].includes(type)) {
              schema = schema.positive();
            }
            break;
          case 'negative':
            if (['number', 'integer', 'bigint', 'float', 'decimal'].includes(type)) {
              schema = schema.negative();
            }
            break;
          default:
            schema = schema.nullable();
        }
      }
    });
  
    return schema;
  };
  
  export default createYupSchema;
  