Swimlanes = new Mongo.Collection('swimlanes');

/**
 * A swimlane is an line in the kaban board.
 */
Swimlanes.attachSchema(
  new SimpleSchema({
    title: {
      /**
       * the title of the swimlane
       */
      type: String,
    },
    archived: {
      /**
       * is the swimlane archived?
       */
      type: Boolean,
      // eslint-disable-next-line consistent-return
      autoValue() {
        if (this.isInsert && !this.isSet) {
          return false;
        }
      },
    },
    boardId: {
      /**
       * the ID of the board the swimlane is attached to
       */
      type: String,
    },
    createdAt: {
      /**
       * creation date of the swimlane
       */
      type: Date,
      // eslint-disable-next-line consistent-return
      autoValue() {
        if (this.isInsert) {
          return new Date();
        } else if (this.isUpsert) {
          return { $setOnInsert: new Date() };
        } else {
          this.unset();
        }
      },
    },
    sort: {
      /**
       * the sort value of the swimlane
       */
      type: Number,
      decimal: true,
      // XXX We should probably provide a default
      optional: true,
    },
    color: {
      /**
       * the color of the swimlane
       */
      type: String,
      optional: true,
      // silver is the default, so it is left out
      allowedValues: [
        'white',
        'green',
        'yellow',
        'orange',
        'red',
        'purple',
        'blue',
        'sky',
        'lime',
        'pink',
        'black',
        'peachpuff',
        'crimson',
        'plum',
        'darkgreen',
        'slateblue',
        'magenta',
        'gold',
        'navy',
        'gray',
        'saddlebrown',
        'paleturquoise',
        'mistyrose',
        'indigo',
      ],
    },
    updatedAt: {
      /**
       * when was the swimlane last edited
       */
      type: Date,
      optional: true,
      // eslint-disable-next-line consistent-return
      autoValue() {
        if (this.isUpdate || this.isUpsert || this.isInsert) {
          return new Date();
        } else {
          this.unset();
        }
      },
    },
    modifiedAt: {
      type: Date,
      denyUpdate: false,
      // eslint-disable-next-line consistent-return
      autoValue() {
        if (this.isInsert || this.isUpsert || this.isUpdate) {
          return new Date();
        } else {
          this.unset();
        }
      },
    },
    type: {
      /**
       * The type of swimlane
       */
      type: String,
      defaultValue: 'swimlane',
    },
  }),
);

Swimlanes.allow({
  insert(userId, doc) {
    return allowIsBoardMemberCommentOnly(userId, Boards.findOne(doc.boardId));
  },
  update(userId, doc) {
    return allowIsBoardMemberCommentOnly(userId, Boards.findOne(doc.boardId));
  },
  remove(userId, doc) {
    return allowIsBoardMemberCommentOnly(userId, Boards.findOne(doc.boardId));
  },
  fetch: ['boardId'],
});

Swimlanes.helpers({
  copy(boardId) {
    const oldId = this._id;
    const oldBoardId = this.boardId;
    this.boardId = boardId;
    delete this._id;
    const _id = Swimlanes.insert(this);

    const query = {
      swimlaneId: { $in: [oldId, ''] },
      archived: false,
    };
    if (oldBoardId) {
      query.boardId = oldBoardId;
    }

    // Copy all lists in swimlane
    Lists.find(query).forEach(list => {
      list.type = 'list';
      list.swimlaneId = oldId;
      list.boardId = boardId;
      list.copy(boardId, _id);
    });
  },

  cards() {
    return Cards.find(
      Filter.mongoSelector({
        swimlaneId: this._id,
        archived: false,
      }),
      { sort: ['sort'] },
    );
  },

  lists() {
    //currentUser = Meteor.user();
    //if (currentUser) {
    //  enabled = Meteor.user().hasSortBy();
    //}
    //return enabled ? this.newestLists() : this.draggableLists();
    return this.draggableLists();
  },
  newestLists() {
    // sorted lists from newest to the oldest, by its creation date or its cards' last modification date
    return Lists.find(
      {
        boardId: this.boardId,
        swimlaneId: { $in: [this._id, ''] },
        archived: false,
      },
      { sort: { modifiedAt: -1 } },
    );
  },
  draggableLists() {
    return Lists.find(
      {
        boardId: this.boardId,
        swimlaneId: { $in: [this._id, ''] },
        archived: false,
      },
      { sort: ['sort'] },
    );
  },

  myLists() {
    return Lists.find({ swimlaneId: this._id });
  },

  allCards() {
    return Cards.find({ swimlaneId: this._id });
  },

  board() {
    return Boards.findOne(this.boardId);
  },

  colorClass() {
    if (this.color) return this.color;
    return '';
  },

  isTemplateSwimlane() {
    return this.type === 'template-swimlane';
  },

  isTemplateContainer() {
    return this.type === 'template-container';
  },

  isListTemplatesSwimlane() {
    const user = Users.findOne(Meteor.userId());
    return (user.profile || {}).listTemplatesSwimlaneId === this._id;
  },

  isCardTemplatesSwimlane() {
    const user = Users.findOne(Meteor.userId());
    return (user.profile || {}).cardTemplatesSwimlaneId === this._id;
  },

  isBoardTemplatesSwimlane() {
    const user = Users.findOne(Meteor.userId());
    return (user.profile || {}).boardTemplatesSwimlaneId === this._id;
  },

  remove() {
    Swimlanes.remove({ _id: this._id });
  },
});

Swimlanes.mutations({
  rename(title) {
    return { $set: { title } };
  },

  archive() {
    if (this.isTemplateSwimlane()) {
      this.myLists().forEach(list => {
        return list.archive();
      });
    }
    return { $set: { archived: true } };
  },

  restore() {
    if (this.isTemplateSwimlane()) {
      this.myLists().forEach(list => {
        return list.restore();
      });
    }
    return { $set: { archived: false } };
  },

  setColor(newColor) {
    if (newColor === 'silver') {
      newColor = null;
    }
    return {
      $set: {
        color: newColor,
      },
    };
  },
});

Swimlanes.hookOptions.after.update = { fetchPrevious: false };

if (Meteor.isServer) {
  Meteor.startup(() => {
    Swimlanes._collection._ensureIndex({ modifiedAt: -1 });
    Swimlanes._collection._ensureIndex({ boardId: 1 });
  });

  Swimlanes.after.insert((userId, doc) => {
    Activities.insert({
      userId,
      type: 'swimlane',
      activityType: 'createSwimlane',
      boardId: doc.boardId,
      swimlaneId: doc._id,
    });
  });

  Swimlanes.before.remove(function(userId, doc) {
    const lists = Lists.find(
      {
        boardId: doc.boardId,
        swimlaneId: { $in: [doc._id, ''] },
        archived: false,
      },
      { sort: ['sort'] },
    );

    if (lists.count() < 2) {
      lists.forEach(list => {
        list.remove();
      });
    } else {
      Cards.remove({ swimlaneId: doc._id });
    }

    Activities.insert({
      userId,
      type: 'swimlane',
      activityType: 'removeSwimlane',
      boardId: doc.boardId,
      swimlaneId: doc._id,
      title: doc.title,
    });
  });

  Swimlanes.after.update((userId, doc) => {
    if (doc.archived) {
      Activities.insert({
        userId,
        type: 'swimlane',
        activityType: 'archivedSwimlane',
        swimlaneId: doc._id,
        boardId: doc.boardId,
      });
    }
  });
}

//SWIMLANE REST API
if (Meteor.isServer) {
  /**
   * @operation get_all_swimlanes
   *
   * @summary Get the list of swimlanes attached to a board
   *
   * @param {string} boardId the ID of the board
   * @return_type [{_id: string,
   *                title: string}]
   */
  JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes', function(req, res) {
    try {
      const paramBoardId = req.params.boardId;
      Authentication.checkBoardAccess(req.userId, paramBoardId);

      JsonRoutes.sendResult(res, {
        code: 200,
        data: Swimlanes.find({ boardId: paramBoardId, archived: false }).map(
          function(doc) {
            return {
              _id: doc._id,
              title: doc.title,
            };
          },
        ),
      });
    } catch (error) {
      JsonRoutes.sendResult(res, {
        code: 200,
        data: error,
      });
    }
  });

  /**
   * @operation get_swimlane
   *
   * @summary Get a swimlane
   *
   * @param {string} boardId the ID of the board
   * @param {string} swimlaneId the ID of the swimlane
   * @return_type Swimlanes
   */
  JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes/:swimlaneId', function(
    req,
    res,
  ) {
    try {
      const paramBoardId = req.params.boardId;
      const paramSwimlaneId = req.params.swimlaneId;
      Authentication.checkBoardAccess(req.userId, paramBoardId);
      JsonRoutes.sendResult(res, {
        code: 200,
        data: Swimlanes.findOne({
          _id: paramSwimlaneId,
          boardId: paramBoardId,
          archived: false,
        }),
      });
    } catch (error) {
      JsonRoutes.sendResult(res, {
        code: 200,
        data: error,
      });
    }
  });

  /**
   * @operation new_swimlane
   *
   * @summary Add a swimlane to a board
   *
   * @param {string} boardId the ID of the board
   * @param {string} title the new title of the swimlane
   * @return_type {_id: string}
   */
  JsonRoutes.add('POST', '/api/boards/:boardId/swimlanes', function(req, res) {
    try {
      Authentication.checkUserId(req.userId);
      const paramBoardId = req.params.boardId;
      const board = Boards.findOne(paramBoardId);
      const id = Swimlanes.insert({
        title: req.body.title,
        boardId: paramBoardId,
        sort: board.swimlanes().count(),
      });
      JsonRoutes.sendResult(res, {
        code: 200,
        data: {
          _id: id,
        },
      });
    } catch (error) {
      JsonRoutes.sendResult(res, {
        code: 200,
        data: error,
      });
    }
  });

  /**
   * @operation delete_swimlane
   *
   * @summary Delete a swimlane
   *
   * @description The swimlane will be deleted, not moved to the recycle bin
   *
   * @param {string} boardId the ID of the board
   * @param {string} swimlaneId the ID of the swimlane
   * @return_type {_id: string}
   */
  JsonRoutes.add(
    'DELETE',
    '/api/boards/:boardId/swimlanes/:swimlaneId',
    function(req, res) {
      try {
        Authentication.checkUserId(req.userId);
        const paramBoardId = req.params.boardId;
        const paramSwimlaneId = req.params.swimlaneId;
        Swimlanes.remove({ _id: paramSwimlaneId, boardId: paramBoardId });
        JsonRoutes.sendResult(res, {
          code: 200,
          data: {
            _id: paramSwimlaneId,
          },
        });
      } catch (error) {
        JsonRoutes.sendResult(res, {
          code: 200,
          data: error,
        });
      }
    },
  );
}

export default Swimlanes;
