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

'use strict';

const util = require('util');
const moment = require('moment');
const rfs = require('rotating-file-stream');

const color = require('ansicolor');

var LogModel, LogStream;

if (process.env.DTP_LOG_FILE === 'enabled') {
  LogStream = rfs.createStream(process.env.DTP_LOG_FILE_NAME_APP || 'site-app.log', {
    path: process.env.DTP_LOG_FILE_PATH || '/tmp',
    size: '10M',
    interval: '1d',
    compress: 'gzip',
  });
}

class SiteLog {

  /**
   * Sets the Mongoose model to be used for writing log entries to the database.
   * This is managed like this so we don't have to connect to the database and
   * load the db models *before* we can write log entries to the console.
   * @param {Model} model the Mongoose model to be used for writing logs.
   */
  static setModel (model) { LogModel = model; }

  constructor (dtp, component) {
    if (!dtp) {
      throw new Error('Must specify DTP module');
    }
    this.dtp = dtp;

    if (!component || !component.slug || !component.name) {
      throw new Error('Must specify DTP component');
    }
    this.component = component;
  }

  async debug (message, metadata) {
    if (process.env.DTP_LOG_DEBUG !== 'enabled') {
      return;
    }
    return this.writeLog('debug', message, metadata);
  }

  async info (message, metadata) {
    if (process.env.DTP_LOG_INFO !== 'enabled') {
      return;
    }
    return this.writeLog('info', message, metadata);
  }

  async warn (message, metadata) {
    if (process.env.DTP_LOG_WARN !== 'enabled') {
      return;
    }
    return this.writeLog('warn', message, metadata);
  }

  async alert (message, metadata) {
    return this.writeLog('alert', message, metadata);
  }

  async error (message, metadata) {
    return this.writeLog('error', message, metadata);
  }
  
  async crit (message, metadata) {
    return this.writeLog('crit', message, metadata);
  }

  async fatal (message, metadata) {
    this.writeLog('fatal', message, metadata);
  }

  async writeLog (level, message, metadata) {
    const NOW = new Date();
    const ctimestamp = color.black(moment(NOW).format('YYYY-MM-DD HH:mm:ss.SSS'));
    const ccomponentSlug = color.cyan(this.component.slug);
    const cmessage = color.darkGray(message);

    let clevel = level.padEnd(5);
    switch (level) {
      case 'debug':
        clevel = color.black(clevel);
        break;
      case 'info':
        clevel = color.green(clevel);
        break;
      case 'warn':
        clevel = color.yellow(clevel);
        break;
      case 'alert':
        clevel = color.red(clevel);
        break;
      case 'error':
        clevel = color.bgRed.white(clevel);
        break;
      case 'crit':
        clevel = color.bgRed.yellow(clevel);
        break;
      case 'fatal':
        clevel = color.bgRed.black(clevel);
        break;
    }

    if (process.env.DTP_LOG_CONSOLE === 'enabled') {
      if (metadata) {
        console.log(`${ctimestamp} ${clevel} ${ccomponentSlug} ${cmessage}`, util.inspect(metadata, false, Infinity, true));
      } else {
        console.log(`${ctimestamp} ${clevel} ${ccomponentSlug} ${cmessage}`);
      }
    }

    if (LogModel && (process.env.DTP_LOG_MONGODB === 'enabled')) {
      try {
        const component = { name: this.component.name, slug: this.component.slug };
        await LogModel.create({ created: NOW, level, component, message, metadata });
      } catch (error) {
        console.log(
          'failed to write log entry to MongoDB',
          `${ctimestamp} ${clevel} ${ccomponentSlug} ${cmessage}`,
          { error },
        );
        // fall through
      }
    }

    if (LogStream && (process.env.DTP_LOG_FILE === 'enabled')) {
      const logEntry = {
        t: NOW, c: this.component.slug, l: level, m: message, d: metadata,
      };
      LogStream.write(`${JSON.stringify(logEntry)}\n`);
    }
  }
}

module.exports.SiteLog = SiteLog;
