import loggerFactory from '~/utils/logger';
import { removeNullPropertyFromObject } from '~/utils/object-utils';

// eslint-disable-next-line no-unused-vars
const logger = loggerFactory('growi:routes:apiv3:notification-setting');

const express = require('express');

const router = express.Router();

const { body } = require('express-validator');

const ErrorV3 = require('../../models/vo/error-apiv3');

const validator = {
  slackConfiguration: [
    body('webhookUrl').if(value => value != null).isString().trim(),
    body('isIncomingWebhookPrioritized').isBoolean(),
    body('slackToken').if(value => value != null).isString().trim(),
  ],
  userNotification: [
    body('pathPattern').isString().trim(),
    body('channel').isString().trim(),
  ],
  globalNotification: [
    body('triggerPath').isString().trim().not()
      .isEmpty(),
    body('notifyToType').isString().trim().isIn(['mail', 'slack']),
    body('toEmail').trim().custom((value, { req }) => {
      return (req.body.notifyToType === 'mail') ? (!!value && value.match(/.+@.+\..+/)) : true;
    }),
    body('slackChannels').trim().custom((value, { req }) => {
      return (req.body.notifyToType === 'slack') ? !!value : true;
    }),
  ],
  notifyForPageGrant: [
    body('isNotificationForOwnerPageEnabled').if(value => value != null).isBoolean(),
    body('isNotificationForGroupPageEnabled').if(value => value != null).isBoolean(),
  ],
};

/**
 * @swagger
 *  tags:
 *    name: NotificationSetting
 */

/**
 * @swagger
 *
 *  components:
 *    schemas:
 *      SlackConfigurationParams:
 *        type: object
 *        properties:
 *          webhookUrl:
 *            type: string
 *            description: incoming webhooks url
 *          isIncomingWebhookPrioritized:
 *            type: boolean
 *            description: use incoming webhooks even if Slack App settings are enabled
 *          slackToken:
 *            type: string
 *            description: OAuth access token
 *      UserNotificationParams:
 *        type: object
 *        properties:
 *          pathPattern:
 *            type: string
 *            description: path name of wiki
 *          channel:
 *            type: string
 *            description: slack channel name without '#'
 *      NotifyForPageGrant:
 *        type: object
 *        properties:
 *          isNotificationForOwnerPageEnabled:
 *            type: string
 *            description: Whether to notify on owner page
 *          isNotificationForGroupPageEnabled:
 *            type: string
 *            description: Whether to notify on group page
 *      GlobalNotificationParams:
 *        type: object
 *        properties:
 *          notifyToType:
 *            type: string
 *            description: What is type for notify
 *          toEmail:
 *            type: string
 *            description: email for notify
 *          slackChannels:
 *            type: string
 *            description: channels for notify
 *          triggerPath:
 *            type: string
 *            description: trigger path for notify
 *          triggerEvents:
 *            type: array
 *            items:
 *              type: string
 *              description: trigger events for notify
 */
module.exports = (crowi) => {
  const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
  const adminRequired = require('../../middlewares/admin-required')(crowi);
  const csrf = require('../../middlewares/csrf')(crowi);
  const apiV3FormValidator = require('../../middlewares/apiv3-form-validator')(crowi);

  const UpdatePost = crowi.model('UpdatePost');
  const GlobalNotificationSetting = crowi.model('GlobalNotificationSetting');

  const GlobalNotificationMailSetting = crowi.models.GlobalNotificationMailSetting;
  const GlobalNotificationSlackSetting = crowi.models.GlobalNotificationSlackSetting;

  /**
   * @swagger
   *
   *    /notification-setting/:
   *      get:
   *        tags: [NotificationSetting]
   *        description: Get notification paramators
   *        responses:
   *          200:
   *            description: params of notification
   *            content:
   *              application/json:
   *                schema:
   *                  properties:
   *                    notificationParams:
   *                      type: object
   *                      description: notification params
   */
  router.get('/', loginRequiredStrictly, adminRequired, async(req, res) => {

    const notificationParams = {
      webhookUrl: await crowi.configManager.getConfig('notification', 'slack:incomingWebhookUrl'),
      isIncomingWebhookPrioritized: await crowi.configManager.getConfig('notification', 'slack:isIncomingWebhookPrioritized'),
      slackToken: await crowi.configManager.getConfig('notification', 'slack:token'),
      userNotifications: await UpdatePost.findAll(),
      isNotificationForOwnerPageEnabled: await crowi.configManager.getConfig('notification', 'notification:owner-page:isEnabled'),
      isNotificationForGroupPageEnabled: await crowi.configManager.getConfig('notification', 'notification:group-page:isEnabled'),
      globalNotifications: await GlobalNotificationSetting.findAll(),
    };
    return res.apiv3({ notificationParams });
  });

  /**
   * @swagger
   *
   *    /notification-setting/slack-configuration:
   *      put:
   *        tags: [NotificationSetting]
   *        description: Update slack configuration setting
   *        requestBody:
   *          required: true
   *          content:
   *            application/json:
   *              schema:
   *                $ref: '#/components/schemas/SlackConfigurationParams'
   *        responses:
   *          200:
   *            description: Succeeded to update slack configuration setting
   *            content:
   *              application/json:
   *                schema:
   *                  $ref: '#/components/schemas/SlackConfigurationParams'
   */
  router.put('/slack-configuration', loginRequiredStrictly, adminRequired, csrf, validator.slackConfiguration, apiV3FormValidator, async(req, res) => {

    const requestParams = {
      'slack:incomingWebhookUrl': req.body.webhookUrl,
      'slack:isIncomingWebhookPrioritized': req.body.isIncomingWebhookPrioritized,
      'slack:token': req.body.slackToken,
    };

    try {
      await crowi.configManager.updateConfigsInTheSameNamespace('notification', requestParams);
      const responseParams = {
        webhookUrl: await crowi.configManager.getConfig('notification', 'slack:incomingWebhookUrl'),
        isIncomingWebhookPrioritized: await crowi.configManager.getConfig('notification', 'slack:isIncomingWebhookPrioritized'),
        slackToken: await crowi.configManager.getConfig('notification', 'slack:token'),
      };
      return res.apiv3({ responseParams });
    }
    catch (err) {
      const msg = 'Error occurred in updating slack configuration';
      logger.error('Error', err);
      return res.apiv3Err(new ErrorV3(msg, 'update-slackConfiguration-failed'));
    }

  });

  /**
  * @swagger
  *
  *    /notification-setting/user-notification:
  *      post:
  *        tags: [NotificationSetting]
  *        description: add user notification setting
  *        requestBody:
  *          required: true
  *          content:
  *            application/json:
  *              schema:
  *                $ref: '#/components/schemas/UserNotificationParams'
  *        responses:
  *          200:
  *            description: Succeeded to add user notification setting
  *            content:
  *              application/json:
  *                schema:
  *                  properties:
  *                    createdUser:
  *                      type: object
  *                      description: user who set notification
  *                    userNotifications:
  *                      type: object
  *                      description: user trigger notifications for updated
  */
  router.post('/user-notification', loginRequiredStrictly, adminRequired, csrf, validator.userNotification, apiV3FormValidator, async(req, res) => {
    const { pathPattern, channel } = req.body;
    const UpdatePost = crowi.model('UpdatePost');

    try {
      logger.info('notification.add', pathPattern, channel);
      const responseParams = {
        createdUser: await UpdatePost.create(pathPattern, channel, req.user),
        userNotifications: await UpdatePost.findAll(),
      };
      return res.apiv3({ responseParams }, 201);
    }
    catch (err) {
      const msg = 'Error occurred in updating user notification';
      logger.error('Error', err);
      return res.apiv3Err(new ErrorV3(msg, 'update-userNotification-failed'));
    }

  });

  /**
   * @swagger
   *
   *    /notification-setting/user-notification/{id}:
   *      delete:
   *        tags: [NotificationSetting]
   *        description: delete user trigger notification pattern
   *        parameters:
   *          - name: id
   *            in: path
   *            required: true
   *            description: id of user trigger notification
   *            schema:
   *              type: string
   *        responses:
   *          200:
   *            description: Succeeded to delete user trigger notification pattern
   *            content:
   *              application/json:
   *                schema:
   *                  properties:
   *                    deletedNotificaton:
   *                      type: object
   *                      description: deleted notification
   */
  router.delete('/user-notification/:id', loginRequiredStrictly, adminRequired, csrf, async(req, res) => {
    const { id } = req.params;

    try {
      const deletedNotificaton = await UpdatePost.remove(id);
      return res.apiv3(deletedNotificaton);
    }
    catch (err) {
      const msg = 'Error occurred in delete user trigger notification';
      logger.error('Error', err);
      return res.apiv3Err(new ErrorV3(msg, 'delete-userTriggerNotification-failed'));
    }


  });

  /**
   * @swagger
   *
   *    /notification-setting/global-notification:
   *      post:
   *        tags: [NotificationSetting]
   *        description: add global notification
   *        requestBody:
   *          required: true
   *          content:
   *            application/json:
   *              schema:
   *                $ref: '#/components/schemas/GlobalNotificationParams'
   *        responses:
   *          200:
   *            description: Succeeded to add global notification
   *            content:
   *              application/json:
   *                schema:
   *                  properties:
   *                    createdNotification:
   *                      type: object
   *                      description: notification param created
   */
  router.post('/global-notification', loginRequiredStrictly, adminRequired, csrf, validator.globalNotification, apiV3FormValidator, async(req, res) => {

    const {
      notifyToType, toEmail, slackChannels, triggerPath, triggerEvents,
    } = req.body;

    let notification;

    if (notifyToType === GlobalNotificationSetting.TYPE.MAIL) {
      notification = new GlobalNotificationMailSetting(crowi);
      notification.toEmail = toEmail;
    }
    if (notifyToType === GlobalNotificationSetting.TYPE.SLACK) {
      notification = new GlobalNotificationSlackSetting(crowi);
      notification.slackChannels = slackChannels;
    }

    notification.triggerPath = triggerPath;
    notification.triggerEvents = triggerEvents || [];

    try {
      const createdNotification = await notification.save();
      return res.apiv3({ createdNotification }, 201);
    }
    catch (err) {
      const msg = 'Error occurred in updating global notification';
      logger.error('Error', err);
      return res.apiv3Err(new ErrorV3(msg, 'post-globalNotification-failed'));
    }

  });

  /**
   * @swagger
   *
   *    /notification-setting/global-notification/{id}:
   *      put:
   *        tags: [NotificationSetting]
   *        description: update global notification
   *        parameters:
   *          - name: id
   *            in: path
   *            required: true
   *            description: global notification id for updated
   *            schema:
   *              type: string
   *        requestBody:
   *          required: true
   *          content:
   *            application/json:
   *              schema:
   *                $ref: '#/components/schemas/GlobalNotificationParams'
   *        responses:
   *          200:
   *            description: Succeeded to update global notification
   *            content:
   *              application/json:
   *                schema:
   *                  properties:
   *                    createdNotification:
   *                      type: object
   *                      description: notification param updated
   */
  router.put('/global-notification/:id', loginRequiredStrictly, adminRequired, csrf, validator.globalNotification, apiV3FormValidator, async(req, res) => {
    const { id } = req.params;
    const {
      notifyToType, toEmail, slackChannels, triggerPath, triggerEvents,
    } = req.body;

    const models = {
      [GlobalNotificationSetting.TYPE.MAIL]: GlobalNotificationMailSetting,
      [GlobalNotificationSetting.TYPE.SLACK]: GlobalNotificationSlackSetting,
    };

    try {
      let setting = await GlobalNotificationSetting.findOne({ _id: id });
      setting = setting.toObject();

      // when switching from one type to another,
      // remove toEmail from slack setting and slackChannels from mail setting
      if (setting.__t !== notifyToType) {
        setting = models[setting.__t].hydrate(setting);
        setting.toEmail = undefined;
        setting.slackChannels = undefined;
        await setting.save();
        setting = setting.toObject();
      }

      if (notifyToType === GlobalNotificationSetting.TYPE.MAIL) {
        setting = GlobalNotificationMailSetting.hydrate(setting);
        setting.toEmail = toEmail;
      }
      if (notifyToType === GlobalNotificationSetting.TYPE.SLACK) {
        setting = GlobalNotificationSlackSetting.hydrate(setting);
        setting.slackChannels = slackChannels;
      }

      setting.__t = notifyToType;
      setting.triggerPath = triggerPath;
      setting.triggerEvents = triggerEvents || [];

      const createdNotification = await setting.save();
      return res.apiv3({ createdNotification });
    }
    catch (err) {
      const msg = 'Error occurred in updating global notification';
      logger.error('Error', err);
      return res.apiv3Err(new ErrorV3(msg, 'post-globalNotification-failed'));
    }

  });


  /**
   * @swagger
   *
   *    /notification-setting/notify-for-page-grant:
   *      put:
   *        tags: [NotificationSetting]
   *        description: Update settings for notify for page grant
   *        requestBody:
   *          required: true
   *          content:
   *            application/json:
   *              schema:
   *                $ref: '#/components/schemas/NotifyForPageGrant'
   *        responses:
   *          200:
   *            description: Succeeded to settings for notify for page grant
   *            content:
   *              application/json:
   *                schema:
   *                  $ref: '#/components/schemas/NotifyForPageGrant'
   */
  router.put('/notify-for-page-grant', loginRequiredStrictly, adminRequired, csrf, validator.notifyForPageGrant, apiV3FormValidator, async(req, res) => {

    let requestParams = {
      'notification:owner-page:isEnabled': req.body.isNotificationForOwnerPageEnabled,
      'notification:group-page:isEnabled': req.body.isNotificationForGroupPageEnabled,
    };

    requestParams = removeNullPropertyFromObject(requestParams);

    try {
      await crowi.configManager.updateConfigsInTheSameNamespace('notification', requestParams);
      const responseParams = {
        isNotificationForOwnerPageEnabled: await crowi.configManager.getConfig('notification', 'notification:owner-page:isEnabled'),
        isNotificationForGroupPageEnabled: await crowi.configManager.getConfig('notification', 'notification:group-page:isEnabled'),
      };
      return res.apiv3({ responseParams });
    }
    catch (err) {
      const msg = 'Error occurred in updating notify for page grant';
      logger.error('Error', err);
      return res.apiv3Err(new ErrorV3(msg, 'update-notify-for-page-grant-failed'));
    }
  });
  /**
   * @swagger
   *
   *    /notification-setting/global-notification/{id}/enabled:
   *      put:
   *        tags: [NotificationSetting]
   *        description: toggle enabled global notification
   *        parameters:
   *          - name: id
   *            in: path
   *            required: true
   *            description: notification id for updated
   *            schema:
   *              type: string
   *        requestBody:
   *          required: true
   *          content:
   *            application/json:
   *              schema:
   *                properties:
   *                  isEnabled:
   *                    type: boolean
   *                    description: is notification enabled
   *        responses:
   *          200:
   *            description: Succeeded to delete global notification pattern
   *            content:
   *              application/json:
   *                schema:
   *                  properties:
   *                    deletedNotificaton:
   *                      type: object
   *                      description: notification id for updated
   */
  router.put('/global-notification/:id/enabled', loginRequiredStrictly, adminRequired, csrf, async(req, res) => {
    const { id } = req.params;
    const { isEnabled } = req.body;

    try {
      if (isEnabled) {
        await GlobalNotificationSetting.enable(id);
      }
      else {
        await GlobalNotificationSetting.disable(id);
      }

      return res.apiv3({ id });

    }
    catch (err) {
      const msg = 'Error occurred in toggle of global notification';
      logger.error('Error', err);
      return res.apiv3Err(new ErrorV3(msg, 'toggle-globalNotification-failed'));
    }

  });

  /**
  * @swagger
  *
  *    /notification-setting/global-notification/{id}:
  *      delete:
  *        tags: [NotificationSetting]
  *        description: delete global notification pattern
  *        parameters:
  *          - name: id
  *            in: path
  *            required: true
  *            description: id of global notification
  *            schema:
  *              type: string
  *        responses:
  *          200:
  *            description: Succeeded to delete global notification pattern
  *            content:
  *              application/json:
  *                schema:
  *                  properties:
  *                    deletedNotificaton:
  *                      type: object
  *                      description: deleted notification
  */
  router.delete('/global-notification/:id', loginRequiredStrictly, adminRequired, csrf, async(req, res) => {
    const { id } = req.params;

    try {
      const deletedNotificaton = await GlobalNotificationSetting.findOneAndRemove({ _id: id });
      return res.apiv3(deletedNotificaton);
    }
    catch (err) {
      const msg = 'Error occurred in delete global notification';
      logger.error('Error', err);
      return res.apiv3Err(new ErrorV3(msg, 'delete-globalNotification-failed'));
    }


  });

  return router;
};
