// @flow strict-local

import assert from 'assert';
import {PassThrough} from 'stream';
import {_report} from '../src/CLIReporter';
import {_setStdio} from '../src/render';
import {inputFS, outputFS} from '@parcel/test-utils';
import {NodePackageManager} from '@parcel/package-manager';
import stripAnsi from 'strip-ansi';

const EMPTY_OPTIONS = {
  cacheDir: '.parcel-cache',
  entries: [],
  logLevel: 'info',
  targets: [],
  projectRoot: '',
  distDir: 'dist',
  lockFile: undefined,
  shouldAutoInstall: false,
  shouldBuildLazily: false,
  hmrOptions: undefined,
  serveOptions: false,
  mode: 'development',
  shouldScopeHoist: false,
  shouldOptimize: false,
  env: {},
  shouldDisableCache: false,
  sourceMaps: false,
  inputFS,
  outputFS,
  instanceId: 'test',
  packageManager: new NodePackageManager(inputFS, '/'),
  detailedReport: {
    assetsPerBundle: 10,
  },
};

describe('CLIReporter', () => {
  let originalStdout;
  let originalStderr;
  let stdoutOutput;
  let stderrOutput;

  beforeEach(async () => {
    // Stub these out to avoid writing noise to real stdio and to read from these
    // otherwise only writable streams
    originalStdout = process.stdout;
    originalStderr = process.stderr;

    stdoutOutput = '';
    stderrOutput = '';

    let mockStdout = new PassThrough();
    mockStdout.on('data', d => (stdoutOutput += stripAnsi(d.toString())));
    let mockStderr = new PassThrough();
    mockStderr.on('data', d => (stderrOutput += stripAnsi(d.toString())));
    _setStdio(mockStdout, mockStderr);

    await _report(
      {
        type: 'buildStart',
      },
      EMPTY_OPTIONS,
    );
  });

  afterEach(() => {
    _setStdio(originalStdout, originalStderr);
  });

  it('writes log, info, success, and verbose log messages to stdout', async () => {
    let options = {
      ...EMPTY_OPTIONS,
      logLevel: 'verbose',
    };

    await _report(
      {
        type: 'log',
        level: 'info',
        diagnostics: [
          {
            origin: 'test',
            message: 'info',
          },
        ],
      },
      options,
    );
    await _report({type: 'log', level: 'success', message: 'success'}, options);
    await _report(
      {
        type: 'log',
        level: 'verbose',
        diagnostics: [
          {
            origin: 'test',
            message: 'verbose',
          },
        ],
      },
      options,
    );

    assert.equal(stdoutOutput, 'test: info\nsuccess\ntest: verbose\n');
  });

  it('writes errors and warnings to stderr', async () => {
    await _report(
      {
        type: 'log',
        level: 'error',
        diagnostics: [
          {
            origin: 'test',
            message: 'error',
          },
        ],
      },
      EMPTY_OPTIONS,
    );
    await _report(
      {
        type: 'log',
        level: 'warn',
        diagnostics: [
          {
            origin: 'test',
            message: 'warn',
          },
        ],
      },
      EMPTY_OPTIONS,
    );

    assert.equal(stdoutOutput, '\n\n\n\n');
    assert.equal(stderrOutput, 'test: error\ntest: warn\n');
  });

  it('prints errors nicely', async () => {
    await _report(
      {
        type: 'log',
        level: 'error',
        diagnostics: [
          {
            origin: 'test',
            message: 'error',
          },
        ],
      },
      EMPTY_OPTIONS,
    );
    await _report(
      {
        type: 'log',
        level: 'warn',
        diagnostics: [
          {
            origin: 'test',
            message: 'warn',
          },
        ],
      },
      EMPTY_OPTIONS,
    );

    assert.equal(stdoutOutput, '\n\n\n\n');
    assert(stderrOutput.includes('test: error\n'));
    assert(stderrOutput.includes('test: warn\n'));
  });

  it('writes buildProgress messages to stdout on the default loglevel', async () => {
    await _report({type: 'buildProgress', phase: 'bundling'}, EMPTY_OPTIONS);
    assert.equal(stdoutOutput, 'Bundling...\n');
  });

  it('writes buildSuccess messages to stdout on the default loglevel', async () => {
    await _report({type: 'buildProgress', phase: 'bundling'}, EMPTY_OPTIONS);
    assert.equal(stdoutOutput, 'Bundling...\n');
  });
});
