// user-notification.js
// Copyright (C) 2022 DTP Technologies, LLC
// License: Apache-2.0

'use strict';

const path = require('path');

const mongoose = require('mongoose');
const UserNotification = mongoose.model('UserNotification');

const pug = require('pug');

const { SiteService } = require('../../lib/site-lib');

class UserNotificationService extends SiteService {

  constructor (dtp) {
    super(dtp, module.exports);
    this.populateUserNotification = [
      {
        path: 'user',
        select: '_id username username_lc displayName picture',
      },
      {
        path: 'event',
        populate: [
          {
            path: 'attachment',
          },
        ],
      },
    ];
  }

  async start ( ) {
    await super.start();
    this.templates = { };
    this.templates.notification = pug.compileFile(path.join(this.dtp.config.root, 'app', 'views', 'notification', 'components', 'notification-standalone.pug'));
  }

  middleware (options) {
    options = Object.assign({
      withNotifications: false,
    }, options || { });
    return async (req, res, next) => {
      res.locals.middleware = res.locals.middleware || { };
      const data = res.locals.middleware.notifications = { };
      if (!req.user) {
        // requests with no user don't matter to this middleware
        return next();
      }
      try {
        data.newCount = await this.getNewCountForUser(req.user);
        if (options.withNotifications) {
          data.new = await this.getForUser(req.user, { skip: 0, cpp: 10 });
        }
        return next();
      } catch (error) {
        this.log.error('failed to populate route with notifications data', { error });
        return next(error);
      }
    };
  }

  async create (user, event) {
    const notification = new UserNotification();
    notification.created = event.created;
    notification.user = user._id;
    notification.status = 'new';
    notification.event = event._id;
    await notification.save();

    await this.dtp.redis.hincrby(`user:${user._id}:notification`, 'newCount', 1);

    return notification.toObject();
  }

  async getNewCountForUser (user) {
    const userKey = `user:${user._id}:notification`;
    let count;

    const value = await this.dtp.redis.hget(userKey, 'newCount');
    if (!value) {
      count = await UserNotification.countDocuments({ user: user._id, status: 'new' });
      await this.dtp.redis.hset(userKey, 'newCount', count);
      return count;
    }

    count = parseInt(value, 10);
    if (count < 0) {
      count = await UserNotification.countDocuments({ user: user._id, status: 'new' });
      await this.dtp.redis.hset(userKey, 'newCount', count);
    }

    return count;
  }

  async getForUser (user, pagination) {
    const notifications = await UserNotification
      .find({ user: user._id })
      .sort({ created: -1 })
      .skip(pagination.skip)
      .limit(pagination.cpp)
      .populate(this.populateUserNotification)
      .lean();

    const newNotifications = notifications.filter((notif) => notif.status === 'new');
    if (newNotifications.length > 0) {
      await UserNotification.updateMany(
        { _id: { $in: newNotifications.map((notif) => notif._id) } },
        { $set: { status: 'seen' } },
      );
      await this.dtp.redis.hincrby(
        `user:${user._id}:notification`,
        'newCount',
        -(newNotifications.length),
      );
    }

    return notifications;
  }

  async getById (notificationId) {
    const notification = await UserNotification
      .findById(notificationId)
      .populate(this.populateUserNotification)
      .lean();
    return notification;
  }
}

module.exports = {
  name: 'userNotification',
  slug: 'user-notification',
  create: (dtp) => { return new UserNotificationService(dtp); },
};