// dashboard.js
// Copyright (C) 2021 Digital Telepresence, LLC
// License: Apache-2.0

'use strict';

const mongoose = require('mongoose');

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

const moment = require('moment');

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

class DashboardService extends SiteService {

  static get CACHE_ENABLED ( ) { return process.env.LINKS_DASHBOARD_CACHE === 'enabled'; }

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

  async getResourceVisitStats (resourceType, resourceId) {
    if (!resourceId) {
      throw new Error('Invalid resource');
    }

    // this will throw if not a valid ObjectId (or able to become one)
    resourceId = mongoose.Types.ObjectId(resourceId);

    const { cache: cacheService } = this.dtp.services;
    let stats;

    const cacheKey = `stats:${resourceType.toLowerCase()}:${resourceId.toString()}:visit`;
    if (DashboardService.CACHE_ENABLED) {
      stats = await cacheService.getObject(cacheKey);
      if (stats) {
        return stats;
      }
    }

    this.log.info('generating resource visit stats report', { resourceId });

    const END_DATE = new Date();
    const START_DATE = moment(END_DATE).subtract(3, 'day').toDate();

    stats = await ResourceVisit.aggregate([
      {
        $match: {
          resource: resourceId,
          $and: [
            { created: { $gt: START_DATE } },
            { created: { $lt: END_DATE } },
          ],
        },
      },
      {
        $group: {
          _id: {
            year: { $year: '$created' },
            month: { $month: '$created' },
            day: { $dayOfMonth: '$created' },
            hour: { $hour: '$created' },
          },
          count: { $sum: 1 },
        },
      },
      {
        $project: {
          _id: false,
          date: {
            $dateFromParts: {
              year: '$_id.year',
              month: '$_id.month',
              day: '$_id.day',
              hour: '$_id.hour',
            },
          },
          count: '$count',
        },
      },
      {
        $sort: { date: 1 },
      },
    ]);

    const response = { start: START_DATE, end: END_DATE, stats };
    await cacheService.setObjectEx(cacheKey, 60 * 5, response);

    return response;
  }

  /*
   *
   * RESOURCE COUNTRY STATS
   * 
   */
  async getResourceCountryStats (resourceType, resourceId) {
    const { cache: cacheService } = this.dtp.services;
    let stats;

    // this will throw if not a valid ObjectId (or able to become one)
    resourceId = mongoose.Types.ObjectId(resourceId);

    const cacheKey = `stats:${resourceType.toLowerCase()}:${resourceId.toString()}:country`;
    if (DashboardService.CACHE_ENABLED) {
      stats = await cacheService.getObject(cacheKey);
      if (stats) {
        return stats;
      }
    }

    this.log.info('generating resource country stats report', { resourceId });

    const END_DATE = new Date();
    const START_DATE = moment(END_DATE).subtract(3, 'day').toDate();

    stats = await ResourceVisit.aggregate([
      {
        $match: {
          resource: resourceId,
          $and: [
            { created: { $gt: START_DATE } },
            { created: { $lt: END_DATE } },
          ],
        },
      },
      {
        $group: {
          _id: {
            country: '$geoip.country',
          },
          count: { $sum: 1 },
        },
      },
      {
        $project: {
          _id: false,
          country: '$_id.country',
          count: '$count',
        },
      },
      {
        $sort: { count: -1, country: 1 },
      },
      {
        $limit: 10,
      },
    ]);

    const response = { start: START_DATE, end: END_DATE, stats };
    await cacheService.setObjectEx(cacheKey, 60 * 5, response);

    return response;
  }

  /*
   *
   * RESOURCE CITY STATS
   * 
   */
  async getResourceCityStats (resourceType, resourceId) {
    const { cache: cacheService } = this.dtp.services;
    let stats;

    // this will throw if not a valid ObjectId (or able to become one)
    resourceId = mongoose.Types.ObjectId(resourceId);

    const cacheKey = `stats:${resourceType.toLowerCase()}:${resourceId.toString()}:city`;
    if (DashboardService.CACHE_ENABLED) {
      stats = await cacheService.getObject(cacheKey);
      if (stats) {
        return stats;
      }
    }

    this.log.info('generating resource city stats report', { resourceId });

    const END_DATE = new Date();
    const START_DATE = moment(END_DATE).subtract(3, 'day').toDate();

    stats = await ResourceVisit.aggregate([
      {
        $match: {
          resource: resourceId,
          $and: [
            { created: { $gt: START_DATE } },
            { created: { $lt: END_DATE } },
          ],
        },
      },
      {
        $group: {
          _id: {
            country: '$geoip.country',
            region: '$geoip.region',
            city: '$geoip.city',
          },
          count: { $sum: 1 },
        },
      },
      {
        $project: {
          _id: false,
          country: '$_id.country',
          region: '$_id.region',
          city: '$_id.city',
          count: '$count',
        },
      },
      {
        $sort: { count: -1, city: 1, region: 1, country: 1 },
      },
      {
        $limit: 10,
      },
    ]);

    const response = { start: START_DATE, end: END_DATE, stats };
    await cacheService.setObjectEx(cacheKey, 60 * 5, response);

    return response;
  }

  async getUserSignupsPerHour ( ) {
    this.log.info('generating user signup stats report');

    const start = moment().subtract(7, 'day').toDate();
    start.setHours(0, 0, 0, 0);

    const stats = await User.aggregate([
      {
        $match: {
          created: { $gt: start },
        },
      },
      {
        $group: {
          _id: {
            year: { $year: '$created' },
            month: { $month: '$created' },
            day: { $dayOfMonth: '$created' },
            hour: { $hour: '$created' },
          },
          count: { $sum: 1 },
        },
      },
      {
        $project: {
          _id: false,
          date: {
            $dateFromParts: {
              year: '$_id.year',
              month: '$_id.month',
              day: '$_id.day',
              hour: '$_id.hour',
            },
          },
          count: '$count',
        },
      },
      {
        $sort: { date: 1 },
      },
    ]);

    return stats;
  }
}

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