ChecklistItems = new Mongo.Collection('checklistItems');

/**
 * An item in a checklist
 */
ChecklistItems.attachSchema(
  new SimpleSchema({
    title: {
      /**
       * the text of the item
       */
      type: String,
    },
    sort: {
      /**
       * the sorting field of the item
       */
      type: Number,
      decimal: true,
    },
    isFinished: {
      /**
       * Is the item checked?
       */
      type: Boolean,
      defaultValue: false,
    },
    checklistId: {
      /**
       * the checklist ID the item is attached to
       */
      type: String,
    },
    cardId: {
      /**
       * the card ID the item is attached to
       */
      type: String,
    },
    createdAt: {
      type: Date,
      optional: true,
      // eslint-disable-next-line consistent-return
      autoValue() {
        if (this.isInsert) {
          return new Date();
        } else if (this.isUpsert) {
          return { $setOnInsert: 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();
        }
      },
    },
  }),
);

ChecklistItems.allow({
  insert(userId, doc) {
    return allowIsBoardMemberByCard(userId, Cards.findOne(doc.cardId));
  },
  update(userId, doc) {
    return allowIsBoardMemberByCard(userId, Cards.findOne(doc.cardId));
  },
  remove(userId, doc) {
    return allowIsBoardMemberByCard(userId, Cards.findOne(doc.cardId));
  },
  fetch: ['userId', 'cardId'],
});

ChecklistItems.before.insert((userId, doc) => {
  if (!doc.userId) {
    doc.userId = userId;
  }
});

// Mutations
ChecklistItems.mutations({
  setTitle(title) {
    return { $set: { title } };
  },
  check() {
    return { $set: { isFinished: true } };
  },
  uncheck() {
    return { $set: { isFinished: false } };
  },
  toggleItem() {
    return { $set: { isFinished: !this.isFinished } };
  },
  move(checklistId, sortIndex) {
    const cardId = Checklists.findOne(checklistId).cardId;
    const mutatedFields = {
      cardId,
      checklistId,
      sort: sortIndex,
    };

    return { $set: mutatedFields };
  },
});

// Activities helper
function itemCreation(userId, doc) {
  const card = Cards.findOne(doc.cardId);
  const boardId = card.boardId;
  Activities.insert({
    userId,
    activityType: 'addChecklistItem',
    cardId: doc.cardId,
    boardId,
    checklistId: doc.checklistId,
    checklistItemId: doc._id,
    checklistItemName: doc.title,
    listId: card.listId,
    swimlaneId: card.swimlaneId,
  });
}

function itemRemover(userId, doc) {
  Activities.remove({
    checklistItemId: doc._id,
  });
}

function publishCheckActivity(userId, doc) {
  const card = Cards.findOne(doc.cardId);
  const boardId = card.boardId;
  let activityType;
  if (doc.isFinished) {
    activityType = 'checkedItem';
  } else {
    activityType = 'uncheckedItem';
  }
  const act = {
    userId,
    activityType,
    cardId: doc.cardId,
    boardId,
    checklistId: doc.checklistId,
    checklistItemId: doc._id,
    checklistItemName: doc.title,
    listId: card.listId,
    swimlaneId: card.swimlaneId,
  };
  Activities.insert(act);
}

function publishChekListCompleted(userId, doc) {
  const card = Cards.findOne(doc.cardId);
  const boardId = card.boardId;
  const checklistId = doc.checklistId;
  const checkList = Checklists.findOne({ _id: checklistId });
  if (checkList.isFinished()) {
    const act = {
      userId,
      activityType: 'completeChecklist',
      cardId: doc.cardId,
      boardId,
      checklistId: doc.checklistId,
      checklistName: checkList.title,
      listId: card.listId,
      swimlaneId: card.swimlaneId,
    };
    Activities.insert(act);
  }
}

function publishChekListUncompleted(userId, doc) {
  const card = Cards.findOne(doc.cardId);
  const boardId = card.boardId;
  const checklistId = doc.checklistId;
  const checkList = Checklists.findOne({ _id: checklistId });
  // BUGS in IFTTT Rules: https://github.com/wekan/wekan/issues/1972
  //       Currently in checklist all are set as uncompleted/not checked,
  //       IFTTT Rule does not move card to other list.
  //       If following line is negated/changed to:
  //         if(!checkList.isFinished()){
  //       then unchecking of any checkbox will move card to other list,
  //       even when all checkboxes are not yet unchecked.
  //       What is correct code for only moving when all in list is unchecked?
  // TIPS: Finding  files, ignoring some directories with grep -v:
  //         cd wekan
  //         find . | xargs grep 'count' -sl | grep -v .meteor | grep -v node_modules | grep -v .build
  //       Maybe something related here?
  //         wekan/client/components/rules/triggers/checklistTriggers.js
  if (checkList.isFinished()) {
    const act = {
      userId,
      activityType: 'uncompleteChecklist',
      cardId: doc.cardId,
      boardId,
      checklistId: doc.checklistId,
      checklistName: checkList.title,
      listId: card.listId,
      swimlaneId: card.swimlaneId,
    };
    Activities.insert(act);
  }
}

// Activities
if (Meteor.isServer) {
  Meteor.startup(() => {
    ChecklistItems._collection._ensureIndex({ modifiedAt: -1 });
    ChecklistItems._collection._ensureIndex({ checklistId: 1 });
    ChecklistItems._collection._ensureIndex({ cardId: 1 });
  });

  ChecklistItems.after.update((userId, doc, fieldNames) => {
    publishCheckActivity(userId, doc);
    publishChekListCompleted(userId, doc, fieldNames);
  });

  ChecklistItems.before.update((userId, doc, fieldNames) => {
    publishChekListUncompleted(userId, doc, fieldNames);
  });

  ChecklistItems.after.insert((userId, doc) => {
    itemCreation(userId, doc);
  });

  ChecklistItems.before.remove((userId, doc) => {
    itemRemover(userId, doc);
    const card = Cards.findOne(doc.cardId);
    const boardId = card.boardId;
    Activities.insert({
      userId,
      activityType: 'removedChecklistItem',
      cardId: doc.cardId,
      boardId,
      checklistId: doc.checklistId,
      checklistItemId: doc._id,
      checklistItemName: doc.title,
      listId: card.listId,
      swimlaneId: card.swimlaneId,
    });
  });
}

if (Meteor.isServer) {
  /**
   * @operation get_checklist_item
   * @tag Checklists
   * @summary Get a checklist item
   *
   * @param {string} boardId the board ID
   * @param {string} cardId the card ID
   * @param {string} checklistId the checklist ID
   * @param {string} itemId the ID of the item
   * @return_type ChecklistItems
   */
  JsonRoutes.add(
    'GET',
    '/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId',
    function(req, res) {
      Authentication.checkUserId(req.userId);
      const paramBoardId = req.params.boardId;
      const paramItemId = req.params.itemId;
      const checklistItem = ChecklistItems.findOne({ _id: paramItemId });
      if (checklistItem) {
        JsonRoutes.sendResult(res, {
          code: 200,
          data: checklistItem,
        });
      } else {
        JsonRoutes.sendResult(res, {
          code: 500,
        });
      }
    },
  );

  /**
   * @operation edit_checklist_item
   * @tag Checklists
   * @summary Edit a checklist item
   *
   * @param {string} boardId the board ID
   * @param {string} cardId the card ID
   * @param {string} checklistId the checklist ID
   * @param {string} itemId the ID of the item
   * @param {string} [isFinished] is the item checked?
   * @param {string} [title] the new text of the item
   * @return_type {_id: string}
   */
  JsonRoutes.add(
    'PUT',
    '/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId',
    function(req, res) {
      Authentication.checkUserId(req.userId);
      const paramBoardId = req.params.boardId;
      const paramItemId = req.params.itemId;

      function isTrue(data) {
        try {
          return data.toLowerCase() === 'true';
        } catch (error) {
          return data;
        }
      }

      if (req.body.hasOwnProperty('isFinished')) {
        ChecklistItems.direct.update(
          { _id: paramItemId },
          { $set: { isFinished: isTrue(req.body.isFinished) } },
        );
      }
      if (req.body.hasOwnProperty('title')) {
        ChecklistItems.direct.update(
          { _id: paramItemId },
          { $set: { title: req.body.title } },
        );
      }

      JsonRoutes.sendResult(res, {
        code: 200,
        data: {
          _id: paramItemId,
        },
      });
    },
  );

  /**
   * @operation delete_checklist_item
   * @tag Checklists
   * @summary Delete a checklist item
   *
   * @description Note: this operation can't be reverted.
   *
   * @param {string} boardId the board ID
   * @param {string} cardId the card ID
   * @param {string} checklistId the checklist ID
   * @param {string} itemId the ID of the item to be removed
   * @return_type {_id: string}
   */
  JsonRoutes.add(
    'DELETE',
    '/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId',
    function(req, res) {
      Authentication.checkUserId(req.userId);
      const paramBoardId = req.params.boardId;
      const paramItemId = req.params.itemId;
      ChecklistItems.direct.remove({ _id: paramItemId });
      JsonRoutes.sendResult(res, {
        code: 200,
        data: {
          _id: paramItemId,
        },
      });
    },
  );
}

export default ChecklistItems;
