// reeeper/cron/expire-announcements.js
// Copyright (C) 2022 DTP Technologies, LLC
// License: Apache-2.0

'use strict';

const path = require('path');
const moment = require('moment');

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

const { CronJob } = require('cron');

const { SiteWorkerProcess } = require(path.join(__dirname, '..', '..', '..', '..', 'lib', 'site-lib'));

/**
 * Announcements used to auto-expire from the MongoDB database after 21 days,
 * but then I added commenting. Now, an auto-expiring Announcement would orphan
 * all those comments. That's bad.
 * 
 * The solution, therefore, is to have a cron that wakes up daily and expires
 * all Announcements older than 21 days. Same policy, it just also cleans up
 * the comments and whatever else gets bolted onto an Announcement over time.
 * 
 * This is how you do that.
 */
class ExpiredAnnouncementsCron extends SiteWorkerProcess {

  static get COMPONENT ( ) {
    return {
      name: 'expiredAnnouncementsCron',
      slug: 'expired-announcements-cron',
    };
  }

  constructor (worker) {
    super(worker, ExpiredAnnouncementsCron.COMPONENT);
  }

  async start ( ) {
    await super.start();

    this.log.info('performing startup expiration of announcements');
    await this.expireAnnouncements();

    this.log.info('starting daily cron to expire announcements');
    this.job = new CronJob(
      '0 0 0 * * *', // at midnight every day
      this.expireAnnouncements.bind(this),
      null,
      true,
      process.env.DTP_CRON_TIMEZONE || 'America/New_York',
    );
  }

  async stop ( ) {
    if (this.job) {
      this.log.info('stopping announcement expire job');
      this.job.stop();
      delete this.job;
    }
    await super.stop();
  }

  async expireAnnouncements ( ) {
    const { announcement: announcementService } = this.dtp.services;

    const NOW = new Date();
    const OLDEST_DATE = moment(NOW).subtract(21, 'days').toDate();

    try {
      await Announcement
        .find({ created: { $lt: OLDEST_DATE } })
        .lean()
        .cursor()
        .eachAsync(async (announcement) => {
          try {
            await announcementService.remove(announcement);
          } catch (error) {
            this.log.error('failed to remove expired Announcement', {
              announcementId: announcement._id,
              error,
            });
            // fall through, we'll get it in a future run
          }
        });
    } catch (error) {
      this.log.error('failed to expire crashed hosts', { error });
    }
  }
}

module.exports = ExpiredAnnouncementsCron;