text: 'use strict';
  
  const { has, isObject } = require('lodash/fp');
  
  const permissionModelUID = 'strapi::permission';
  
  const hasAttribute = attribute => has(`attributes.${attribute}`);
  const hasFieldsAttribute = hasAttribute('fields');
  const hasPropertiesAttribute = hasAttribute('properties');
  
  const shouldRunMigration = (definition, previousDefinition) => {
    const isAdminPermissionModel = definition.uid === permissionModelUID;
  
    const hadFieldsButNotProperties =
      hasFieldsAttribute(previousDefinition) && !hasPropertiesAttribute(previousDefinition);
  
    const hasPropertiesButNotFields =
      !hasFieldsAttribute(definition) && hasPropertiesAttribute(definition);
  
    const targetedFieldsHaveChanged = hadFieldsButNotProperties && hasPropertiesButNotFields;
  
    return isAdminPermissionModel && targetedFieldsHaveChanged;
  };
  
  const permissionsFinderByORM = {
    async bookshelf(model) {
      const permissions = await model.fetchAll();
  
      return permissions.toJSON().map(permission => {
        const fields = permission.fields;
  
        return {
          ...permission,
          fields: isObject(fields) ? fields : JSON.parse(fields),
        };
      });
    },
  
    async mongoose(model) {
      return model.find().lean();
    },
  };
  
  module.exports = {
    shouldRun: {
      before(options) {
        const { definition, previousDefinition } = options;
  
        return shouldRunMigration(definition, previousDefinition);
      },
  
      after(options, context) {
        const { definition, previousDefinition } = options;
        const { permissionsFieldsToProperties = {} } = context;
        const { permissions = [] } = permissionsFieldsToProperties;
  
        return shouldRunMigration(definition, previousDefinition) && permissions.length > 0;
      },
    },
  
    // Here we make a backup of the permission objects in the database, then we store it into the migration context
    async before(options, context) {
      const { model } = options;
  
      const permissions = await permissionsFinderByORM[model.orm](model);
  
      Object.assign(context, { permissionsFieldsToProperties: { permissions } });
    },
  
    // Based on the permissions sent in the context, we perform an update. { fields } => { properties: { fields } }
    async after(options, context) {
      const { model, ORM } = options;
      const { permissionsFieldsToProperties = {} } = context;
      const { permissions = [] } = permissionsFieldsToProperties;
  
      if (model.orm === 'bookshelf') {
        const update = async transacting => {
          for (const permission of permissions) {
            const { fields, ...rest } = permission;
  
            await model
              .forge({ id: rest.id })
              .save({ properties: { fields } }, { patch: true, transacting });
          }
        };
  
        await ORM.transaction(transacting => update(transacting));
      }
  
      if (model.orm === 'mongoose') {
        for (const permission of permissions) {
          const { fields, _id } = permission;
  
          await model.updateOne({ _id }, [
            { $set: { properties: { fields } } },
            { $unset: ['fields'] },
          ]);
        }
      }
    },
  };
  