'use strict';

/**
 * Module dependencies
 */

// Public node modules.
const _ = require('lodash');
const pluralize = require('pluralize');
const { nameToSlug, nameToCollectionName } = require('strapi-utils');

/* eslint-disable prefer-template */

/**
 * This `before` function is run before generating targets.
 * Validate, configure defaults, get extra dependencies, etc.
 *
 * @param {Object} scope
 * @param {Function} cb
 */

module.exports = (scope, cb) => {
  if (!scope.rootPath || !scope.id) {
    return cb.invalid(
      'Usage: `$ strapi generate:model modelName --api apiName --plugin pluginName`'
    );
  }

  // Format `id`.
  const name = scope.name || nameToSlug(scope.id);

  // `scope.args` are the raw command line arguments.
  _.defaults(scope, {
    name,
    environment: process.env.NODE_ENV || 'development',
  });

  // Determine the destination path.
  let filePath;
  if (scope.args.api) {
    filePath = `./api/${scope.args.api}/models`;
  } else if (scope.args.plugin) {
    filePath = `./plugins/${scope.args.plugin}/models`;
  } else {
    filePath = `./api/${name}/models`;
  }

  // Take another pass to take advantage of the defaults absorbed in previous passes.
  _.defaults(scope, {
    rootPath: scope.rootPath,
    filePath,
    filename: `${name}.js`,
    filenameSettings: `${name}.settings.json`,
  });

  // Validate optional attribute arguments.
  const invalidAttributes = [];

  if (_.isPlainObject(scope.args.attributes)) {
    scope.attributes = scope.args.attributes;
  } else {
    // Map attributes and split them for CLI.
    scope.attributes = scope.args.attributes.map(attribute => {
      if (_.isString(attribute)) {
        const parts = attribute.split(':');

        parts[1] = parts[1] || 'string';

        // Handle invalid attributes.
        if (!parts[1] || !parts[0]) {
          invalidAttributes.push('Error: Invalid attribute notation `' + attribute + '`.');
          return;
        }

        return {
          name: _.trim(_.deburr(parts[0].toLowerCase())),
          params: {
            type: _.trim(_.deburr(parts[1].toLowerCase())),
          },
        };
      } else {
        return _.has(attribute, 'params.type') ? attribute : undefined;
      }
    });

    scope.attributes = _.compact(scope.attributes);

    // Handle invalid action arguments.
    // Send back invalidActions.
    if (invalidAttributes.length) {
      return cb.invalid(invalidAttributes);
    }

    // Make sure there aren't duplicates.
    if (
      _(scope.attributes.map(attribute => attribute.name))
        .uniq()
        .valueOf().length !== scope.attributes.length
    ) {
      return cb.invalid('Duplicate attributes not allowed!');
    }

    // Render some stringified code from the action template
    // and make it available in our scope for use later on.
    scope.attributes = scope.attributes.reduce((acc, attribute) => {
      acc[attribute.name] = attribute.params;
      return acc;
    }, {});
  }

  // Set collectionName
  scope.collectionName = _.has(scope.args, 'collectionName')
    ? scope.args.collectionName
    : nameToCollectionName(pluralize(scope.id));

  // Set description
  scope.description = _.has(scope.args, 'description') ? scope.args.description : undefined;

  // Set connection
  scope.connection = _.get(scope.args, 'connection', undefined);

  scope.schema = JSON.stringify(
    {
      kind: 'collectionType',
      connection: scope.connection,
      collectionName: scope.collectionName,
      info: {
        name: scope.id,
        description: scope.description,
      },
      options: {
        draftAndPublish: scope.args.draftAndPublish === 'true',
        timestamps: true,
        increments: true,
        comment: '',
      },
      attributes: scope.attributes,
    },
    null,
    2
  );

  // Trigger callback with no error to proceed.
  return cb();
};
