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

'use strict';

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

const ioEmitter = require('socket.io-emitter');

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

class ContentVoteService extends SiteService {

  constructor (dtp) {
    super(dtp, module.exports);
  }

  async start ( ) {
    await super.start();
    this.emitter = ioEmitter(this.dtp.redis);
  }

  async recordVote (user, resourceType, resource, vote) {
    const NOW = new Date();
    const ResourceModel = mongoose.model(resourceType);
    const updateOp = { $inc: { } };
    let message;

    const contentVote = await ContentVote.findOne({
      user: user._id,
      resource: resource._id,
    });

    this.log.debug('processing content vote', { resource: resource._id, user: user._id, vote, contentVote });

    if (!contentVote) {
      /*
       * It's a new vote from a new user
       */
      await ContentVote.create({
        created: NOW,
        resourceType,
        resource: resource._id,
        user: user._id,
        vote
      });
      if (vote === 'up') {
        updateOp.$inc['resourceStats.upvoteCount'] = 1;
        message = 'Comment upvote recorded';
      } else {
        updateOp.$inc['resourceStats.downvoteCount'] = 1;
        message = 'Comment downvote recorded';
      }
    } else {
      /*
       * If vote not changed, do no further work.
       */
      if (contentVote.vote === vote) {
        const updatedResource = await ResourceModel.findById(resource._id).select('resourceStats');
        return { message: "Comment vote unchanged", resourceStats: updatedResource.resourceStats };
      }

      /*
       * Update the user's existing vote
       */
      await ContentVote.updateOne(
        { _id: contentVote._id },
        { $set: { vote } }
      );

      /*
       * Adjust resource's stats based on the changed vote
       */
      if (vote === 'up') {
        updateOp.$inc['resourceStats.upvoteCount'] = 1;
        updateOp.$inc['resourceStats.downvoteCount'] = -1;
        message = 'Comment vote changed to upvote';
      } else {
        updateOp.$inc['resourceStats.upvoteCount'] = -1;
        updateOp.$inc['resourceStats.downvoteCount'] = 1;
        message = 'Comment vote changed to downvote';
      }
    }

    this.log.info('updating resource stats', { resourceType, resource, updateOp });
    await ResourceModel.updateOne({ _id: resource._id }, updateOp);

    const updatedResource = await ResourceModel.findById(resource._id).select('resourceStats');
    return { message, resourceStats: updatedResource.resourceStats };
  }

  async removeForResource (resource) {
    this.log.info('removing all votes for resource', { resourceId: resource._id });
    await ContentVote.deleteMany({ resource: resource._id });
  }
}

module.exports = {
  slug: 'content-vote',
  name: 'contentVote',
  create: (dtp) => { return new ContentVoteService(dtp); },
};