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

'use strict';

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

const geoip = require('geoip-lite');

const mongoose = require('mongoose');

const ResourceView = mongoose.model('ResourceView');
const ResourceVisit = mongoose.model('ResourceVisit');

class ResourceService extends SiteService {

  constructor (dtp) {
    super(dtp, module.exports);
    this.populateResourceView = [
      {
        path: 'resource',
      },
    ];
  }

  /**
   * Records 24-hour unique view counts for a given resource happening on a
   * given ExpressJS Request. Views are uniqued by stripping time from the
   * current Date, and upserting a tracking object in MongoDB.
   *
   * @param {Request} req 
   * @param {String} resourceType 'Post', 'Page', or 'Newsletter'
   * @param {mongoose.Types.ObjectId} resourceId The _id of the object for which
   * a view is being tracked.
   */
  async recordView (req, resourceType, resourceId) {
    const Model = mongoose.model(resourceType);
    const modelUpdate = { $inc: { } };
    
    const NOW = new Date();
    const CURRENT_DAY = new Date(NOW);
    CURRENT_DAY.setHours(0, 0, 0, 0);

    let uniqueKey = req.ip.toString().trim().toLowerCase();
    if (req.user) {
      uniqueKey += `:user:${req.user._id.toString()}`;
    }

    const response = await ResourceView.updateOne(
      {
        created: CURRENT_DAY,
        resourceType,
        resource: resourceId,
        uniqueKey,
      },
      {
        $inc: { 'stats.visitCount': 1 },
      },
      { upsert: true },
    );

    if (response.upsertedCount > 0) {
      modelUpdate.$inc['stats.uniqueViewCount'] = 1;
    }

    /*
     * Record the ResourceVisit
     */
    const visit = new ResourceVisit();

    visit.created = NOW;
    visit.resourceType = resourceType;
    visit.resource = resourceId;

    if (req.user) {
      visit.user = req.user._id;
    }

    /*
     * We geo-analyze (but do not store) the IP address.
     */
    const ipAddress = req.ip;
    const geo = geoip.lookup(ipAddress);
    if (geo) {
      visit.geoip = {
        country: geo.country,
        region: geo.region,
        eu: geo.eu,
        timezone: geo.timezone,
        city: geo.city,
      };
      if (Array.isArray(geo.ll) && (geo.ll.length === 2)) {
        visit.geoip.location = {
          type: 'Point',
          coordinates: geo.ll,
        };
      }
    }

    await visit.save();

    modelUpdate.$inc['stats.totalVisitCount'] = 1;
    await Model.updateOne({ _id: resourceId }, modelUpdate);
  }

  async remove (resourceType, resource) {
    this.log.debug('removing resource view records', { resourceType, resourceId: resource._id });
    await ResourceView.deleteMany({ resource: resource._id });

    this.log.debug('removing resource visit records', { resourceType, resourceId: resource._id });
    await ResourceVisit.deleteMany({ resource: resource._id });

    this.log.debug('removing resource', { resourceType, resourceId: resource._id });
    const Model = mongoose.model(resourceType);
    await Model.deleteOne({ _id: resource._id });
  }
}

module.exports = {
  slug: 'resource',
  name: 'resource',
  create: (dtp) => { return new ResourceService(dtp); },
};
