import { authWithHeaders } from '../../middlewares/auth';
import {
  NotFound,
} from '../../libs/errors';
import { model as PushDevice } from '../../models/pushDevice';
import { sendNotification as sendPushNotification } from '../../libs/pushNotifications';

const api = {};

/**
 * @apiIgnore
 * @api {post} /api/v3/user/push-devices Add a push device to a user
 * @apiName UserAddPushDevice
 * @apiGroup User
 *
 * @apiParam (Body) {String} regId The id of the push device
 * @apiParam (Body) {String} type The type of push device
 *
 * @apiSuccess {Object} data List of push devices
 * @apiSuccess {String} message Success message
 */
api.addPushDevice = {
  method: 'POST',
  url: '/user/push-devices',
  middlewares: [authWithHeaders()],
  async handler (req, res) {
    const { user } = res.locals;

    req.checkBody('regId', res.t('regIdRequired')).notEmpty();
    req.checkBody('type', res.t('typeRequired')).notEmpty().isIn(['ios', 'android', 'unifiedpush']);

    const validationErrors = req.validationErrors();
    if (validationErrors) throw validationErrors;

    const { pushDevices } = user;

    const item = {
      regId: req.body.regId,
      type: req.body.type,
    };

    // When adding a duplicate push device, fail silently instead of throwing an error
    if (pushDevices.find(device => device.regId === item.regId)) {
      res.respond(200, user.pushDevices, res.t('pushDeviceAdded'));
      return;
    }

    // Concurrency safe update
    const pushDevice = (new PushDevice(item)).toJSON(); // Create a mongo doc
    await user.updateOne({
      $push: { pushDevices: pushDevice },
    }).exec();

    // Update the response
    user.pushDevices.push(pushDevice);

    res.respond(200, user.pushDevices, res.t('pushDeviceAdded'));
  },
};

/**
 * @apiIgnore
 * @api {post} /api/v3/user/push-devices/test Send a test push notification
 * @apiName UserSendTestPushNotification
 * @apiGroup User
 *
 * @apiParam (Body) {String} [regId] The id of a specific push device to target
 *
 * @apiSuccess {String} message Success message
 */
api.sendUnifiedPushTest = {
  method: 'POST',
  url: '/user/push-devices/test',
  middlewares: [authWithHeaders()],
  async handler (req, res) {
    const { user } = res.locals;

    const regId = req.body?.regId;
    const pushDevices = user.pushDevices?.toObject ? user.pushDevices.toObject() : user.pushDevices;
    let unifiedPushDevices = (pushDevices || []).filter(device => device?.type === 'unifiedpush');

    if (regId) {
      unifiedPushDevices = unifiedPushDevices.filter(device => device.regId === regId);
    }

    if (unifiedPushDevices.length === 0) {
      throw new NotFound(res.t('pushDeviceNotFound'));
    }

    // Use simple built-in defaults to avoid missing translation strings.
    const notificationTitle = req.body?.title || 'Test Successful';
    const notificationMessage = req.body?.message || 'This is a test UnifiedPush notification from Habitica.';
    const successMessage = 'UnifiedPush test notification sent.';

    const userForPush = user.toObject ? user.toObject() : { ...user };
    userForPush._id = user._id;
    userForPush.pushDevices = unifiedPushDevices;

    await sendPushNotification(userForPush, {
      identifier: 'unifiedPushTestNotification',
      title: notificationTitle,
      message: notificationMessage,
      payload: {
        message: notificationMessage,
      },
    });

    res.respond(200, null, successMessage);
  },
};

/**
 * @apiIgnore
 * @api {delete} /api/v3/user/push-devices/:regId remove a push device from a user
 * @apiName UserRemovePushDevice
 * @apiGroup User
 *
 * @apiParam (Path) {String} regId The id of the push device
 *
 * @apiSuccess {Object} data List of push devices
 * @apiSuccess {String} message Success message
 */
api.removePushDevice = {
  method: 'DELETE',
  url: '/user/push-devices/:regId',
  middlewares: [authWithHeaders()],
  async handler (req, res) {
    const { user } = res.locals;

    req.checkParams('regId', res.t('regIdRequired')).notEmpty();

    const validationErrors = req.validationErrors();
    if (validationErrors) throw validationErrors;

    const { regId } = req.params;

    const { pushDevices } = user;

    const indexOfPushDevice = pushDevices.findIndex(element => element.regId === regId);

    if (indexOfPushDevice === -1) {
      throw new NotFound(res.t('pushDeviceNotFound'));
    }

    // Concurrency safe update
    const pullQuery = { $pull: { pushDevices: { regId } } };
    await user.updateOne(pullQuery).exec();

    // Update the response
    pushDevices.splice(indexOfPushDevice, 1);

    res.respond(200, user.pushDevices, res.t('pushDeviceRemoved'));
  },
};

export default api;
