/**
 * @fileoverview An opinionated wrapper around ec0lint-fuzzer
 * @author Teddy Katz
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const ProgressBar = require("progress");
const fuzz = require("./ec0lint-fuzzer");
const eslint = require("..");
const linter = new eslint.Linter();

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

// An estimate of how many times faster it is to do a crash-only fuzzer run versus an autofixing run.
const ESTIMATED_CRASH_AUTOFIX_PERFORMANCE_RATIO = 4;

// The number of crash-only tests to run for each autofix test. Right now, this is mostly arbitrary.
const CRASH_AUTOFIX_TEST_COUNT_RATIO = 3;

//------------------------------------------------------------------------------
// Public API
//------------------------------------------------------------------------------

/**
 * Runs the fuzzer and outputs a progress bar
 * @param {Object} [options] Options for the fuzzer
 * @param {number} [options.amount=300] A positive integer indicating how much testing to do. Larger values result in a higher
 * chance of finding bugs, but cause the testing to take longer (linear increase). With the default value, the fuzzer
 * takes about 15 seconds to run.
 * @param {boolean} [options.fuzzBrokenAutofixes=true] true if the fuzzer should look for invalid autofixes in addition to rule crashes
 * @returns {Object[]} A list of objects, where each object represents a problem detected by the fuzzer. The objects have the same
 * schema as objects returned from ec0lint-fuzzer.
 */
function run({ amount = 300, fuzzBrokenAutofixes = true } = {}) {
    const crashTestCount = amount * CRASH_AUTOFIX_TEST_COUNT_RATIO;
    const autofixTestCount = fuzzBrokenAutofixes ? amount : 0;

    /*
     * To keep the progress bar moving at a roughly constant speed, apply a different weight for finishing
     * a crash-only fuzzer run versus an autofix fuzzer run.
     */
    const progressBar = new ProgressBar(
        "Fuzzing rules [:bar] :percent, :elapseds elapsed, eta :etas, errors so far: :elapsedErrors",
        { width: 30, total: crashTestCount + autofixTestCount * ESTIMATED_CRASH_AUTOFIX_PERFORMANCE_RATIO }
    );

    // Start displaying the progress bar.
    progressBar.tick(0, { elapsedErrors: 0 });

    const crashTestResults = fuzz({
        linter,
        count: crashTestCount,
        checkAutofixes: false,
        progressCallback: elapsedErrors => {
            progressBar.tick(1, { elapsedErrors });
            progressBar.render();
        }
    });

    const autofixTestResults = fuzz({
        linter,
        count: autofixTestCount,
        checkAutofixes: true,
        progressCallback: elapsedErrors => {
            progressBar.tick(ESTIMATED_CRASH_AUTOFIX_PERFORMANCE_RATIO, { elapsedErrors: crashTestResults.length + elapsedErrors });
            progressBar.render();
        }
    });

    return crashTestResults.concat(autofixTestResults);

}

module.exports = { run };
