#!/usr/bin/env node
'use strict';

const { MongoClient } = require('../..');
const visualizeMonitoringEvents = require('./utils').visualizeMonitoringEvents;
const { now, calculateDurationInMs } = require('../../lib/utils');
const chalk = require('chalk');
const argv = require('yargs')
  .usage('Usage: $0 [options] <connection string>')
  .demandCommand(1)
  .help('h')
  .describe('workload', 'Simulate a read workload')
  .describe('writeWorkload', 'Simulate a write workload')
  .describe('writeWorkloadInterval', 'Time interval between write workload write attempts')
  .describe('writeWorkloadSampleSize', 'Sample size between status display for write workload')
  .describe('legacy', 'Use the legacy topology types')
  .alias('l', 'legacy')
  .alias('w', 'workload')
  .alias('h', 'help').argv;

function print(msg) {
  console.log(`${chalk.white(new Date().toISOString())} ${msg}`);
}

const uri = argv._[0];
const client = new MongoClient(uri, {
  useNewUrlParser: true,
  useUnifiedTopology: !argv.legacy
});

async function run() {
  print(
    `connecting to: ${chalk.bold(uri)} using ${chalk.bold(
      argv.legacy ? 'legacy' : 'unified'
    )} topology`
  );

  visualizeMonitoringEvents(client);
  await client.connect();

  if (argv.workload) {
    scheduleWorkload(client);
  }

  if (argv.writeWorkload) {
    scheduleWriteWorkload(client);
  }
}

let workloadTimer;
let workloadCounter = 0;
let workloadInterrupt = false;
async function scheduleWorkload(client) {
  if (!workloadInterrupt) {
    // immediately reschedule work
    workloadTimer = setTimeout(() => scheduleWorkload(client), 7000);
  }

  const currentWorkload = workloadCounter++;

  try {
    print(`${chalk.yellow(`workload#${currentWorkload}`)} issuing find...`);
    const result = await client
      .db('test')
      .collection('test')
      .find({}, { socketTimeout: 2000 })
      .limit(1)
      .toArray();

    print(
      `${chalk.yellow(`workload#${currentWorkload}`)} find completed: ${JSON.stringify(result)}`
    );
  } catch (e) {
    print(`${chalk.yellow(`workload#${currentWorkload}`)} find failed: ${e.message}`);
  }
}

let writeWorkloadTimer;
let writeWorkloadCounter = 0;
let averageWriteMS = 0;
let completedWriteWorkloads = 0;
const writeWorkloadSampleSize = argv.writeWorkloadSampleSize || 100;
const writeWorkloadInterval = argv.writeWorkloadInterval || 100;
async function scheduleWriteWorkload(client) {
  if (!workloadInterrupt) {
    // immediately reschedule work
    writeWorkloadTimer = setTimeout(() => scheduleWriteWorkload(client), writeWorkloadInterval);
  }

  const currentWriteWorkload = writeWorkloadCounter++;

  try {
    const start = now();
    const result = await client.db('test').collection('test').insertOne({ a: 42 });
    averageWriteMS = 0.2 * calculateDurationInMs(start) + 0.8 * averageWriteMS;

    completedWriteWorkloads++;
    if (completedWriteWorkloads % writeWorkloadSampleSize === 0) {
      print(`${chalk.yellow(`workload#${currentWriteWorkload}`)} completed ${completedWriteWorkloads} writes with average time: ${averageWriteMS}`);
    }

  } catch (e) {
    print(`${chalk.yellow(`workload#${currentWriteWorkload}`)} write failed: ${e.message}`);
  }
}

let exitRequestCount = 0;
process.on('SIGINT', async function() {
  exitRequestCount++;
  if (exitRequestCount > 3) {
    console.log('force quitting...');
    process.exit(1);
  }

  workloadInterrupt = true;
  clearTimeout(workloadTimer);
  clearTimeout(writeWorkloadTimer);
  await client.close();
});

run().catch(error => console.log('Caught', error));
