# src/lib/utils/seeder.js

    /**
     * Base class for seeders to extend.
     *
     * Seeder is an Abstract base class
     * in order to use Seeder you need to
     * extend Seeder into your own class and implement async run() method
     *
     * @example
     * import { Seeder } from 'mongoose-data-seed';
     * import { User } from '../server/models';
     *
     * const data = [
     *   {
     *     email: 'user1@gmail.com',
     *     password: '123123',
     *     passwordConfirmation: '123123',
     *     isAdmin: true,
     *   },
     *   {
     *     email: 'user2@gmail.com',
     *     password: '123123',
     *     passwordConfirmation: '123123',
     *     isAdmin: false,
     *   },
     * ];
     *
     * class UsersSeeder extends Seeder {
     *   async shouldRun() {
     *     const count = await User.countDocuments().exec();
     *
     *     return count === 0;
     *   }
     *
     *   async run() {
     *     return User.create(data);
     *   }
     * }
     *
     * export default UsersSeeder;
     */
    class Seeder {
      /**
       * Abstract class can not be constructed.
       * Seeder class should be extended.
       * @abstract
       * @throws {TypeError} when creating an instance of the abstract class.
       * @throws {TypeError} when the run method is not implemented.
       */
      constructor() {
        if (this.constructor === Seeder) {
          throw new TypeError('Can not construct abstract class.');
        }
        if (this.run === Seeder.prototype.run) {
          throw new TypeError('Please implement abstract method run.');
        }
      }

      /**
       * Seed the data.
       * @return {Promise} Stats about the save.
       */
      async seed() {
        await this.beforeRun();

        let results = null;

        if (await this.shouldRun()) {
          results = await this.run();
        }

        return this.getStats(results);
      }

      /**
       * Should run
       * @return {Promise<boolean>} Indication if should run
       * @abstract
       */
      async shouldRun() {
        return true;
      }

      /**
       * To perform before run.
       * @return {Promise}
       * @abstract
       */
      async beforeRun() {}

      /**
       * Run the seeder.
       * @abstract
       */
      async run() {
        throw new TypeError(
          `Need to implement ${this.constructor.name} async run() function`
        );
      }

      /**
       * Get stats from seed results.
       * @param  {Array} [results] Seed results.
       * @return {Object}
       * @property {boolean} run      Did the seeder run?
       * @property {number}  created  Amount of records created by the seeder.
       */
      getStats(results) {
        if (Array.isArray(results)) {
          return { run: true, created: results.length };
        }

        return { run: false, created: 0 };
      }

      /**
       * Creates a new seeder by extending the base seeder.
       * Useful when not using old javascript
       * @param  {Object} [userSeederMethods={}]  Object with the seeders method
       *                                          (e.g. run, shouldRun, beforeRun ...)
       * @return {Seeder}
       *
       * @example
       * Seeder.extends({
       *   shouldRun: function() {
       *     return User.countDocuments()
       *       .exec()
       *       .then(function(count) {
       *         return count === 0;
       *       });
       *   },
       *   run: function() {
       *     return User.create(data);
       *   }
       * });
       */
      static extend(userSeederMethods = {}) {
        class UserSeeder extends Seeder {}

        // Add methods to the user seeder
        Object.keys(userSeederMethods).forEach(key => {
          UserSeeder.prototype[key] = userSeederMethods[key];
        });

        return UserSeeder;
      }
    }

    export default Seeder;