import path from 'path';
import { copyFile, readdir, mkdir, existsSync, writeFile } from 'fs';
import { promisify } from 'util';
import master from './master';
import creeveyApi from './api';
import { isDefined } from '../../types';
import { shutdown, shutdownWorkers, testsToImages, readDirRecursive } from '../utils';
import { subscribeOn } from '../messages';
import { logger } from '../logger';
import { sendScreenshotsCount } from '../telemetry';
const writeFileAsync = promisify(writeFile);
const copyFileAsync = promisify(copyFile);
const readdirAsync = promisify(readdir);
const mkdirAsync = promisify(mkdir);
async function copyStatics(reportDir) {
  const clientDir = path.join(__dirname, '../../client/web');
  const files = (await readdirAsync(clientDir, {
    withFileTypes: true
  })).filter(dirent => dirent.isFile() && !dirent.name.endsWith('.d.ts') && !dirent.name.endsWith('.tsx')).map(dirent => dirent.name);
  await mkdirAsync(reportDir, {
    recursive: true
  });
  for (const file of files) {
    await copyFileAsync(path.join(clientDir, file), path.join(reportDir, file));
  }
}
function reportDataModule(data) {
  return `
(function (root, factory) {
  if (typeof module === 'object' && module.exports) {
    module.exports = factory();
  } else {
    root.__CREEVEY_DATA__ = factory();
  }
}(this, function () { return ${JSON.stringify(data)} }));
`;
}
function outputUnnecessaryImages(imagesDir, images) {
  if (!existsSync(imagesDir)) return;
  const unnecessaryImages = readDirRecursive(imagesDir).map(imagePath => path.posix.relative(imagesDir, imagePath)).filter(imagePath => !images.has(imagePath));
  if (unnecessaryImages.length > 0) {
    logger.warn('We found unnecessary screenshot images, those can be safely removed:\n', unnecessaryImages.join('\n'));
  }
}
export default async function (config, options, resolveApi) {
  let runner = null;
  if (config.hooks.before) {
    await config.hooks.before();
  }
  subscribeOn('shutdown', () => {
    var _config$hooks$after, _config$hooks;
    return (_config$hooks$after = (_config$hooks = config.hooks).after) === null || _config$hooks$after === void 0 ? void 0 : _config$hooks$after.call(_config$hooks);
  });
  process.removeListener('SIGINT', shutdown);
  process.on('SIGINT', () => {
    var _runner, _runner2;
    (_runner = runner) === null || _runner === void 0 || _runner.removeAllListeners('stop');
    if ((_runner2 = runner) !== null && _runner2 !== void 0 && _runner2.isRunning) {
      var _runner4;
      // TODO Better handle stop
      void Promise.race([new Promise(resolve => setTimeout(resolve, 10000)), new Promise(resolve => {
        var _runner3;
        return (_runner3 = runner) === null || _runner3 === void 0 ? void 0 : _runner3.once('stop', resolve);
      })]).then(() => shutdownWorkers());
      (_runner4 = runner) === null || _runner4 === void 0 || _runner4.stop();
    } else {
      void shutdownWorkers();
    }
  });
  runner = await master(config, {
    watch: options.ui,
    debug: options.debug,
    port: options.port
  });
  if (options.saveReport) {
    runner.on('stop', () => {
      void copyStatics(config.reportDir).then(() => {
        var _runner5;
        return writeFileAsync(path.join(config.reportDir, 'data.js'), reportDataModule((_runner5 = runner) === null || _runner5 === void 0 ? void 0 : _runner5.status.tests));
      });
    });
  }
  if (options.ui) {
    resolveApi(creeveyApi(runner));
    logger.info(`Started on http://localhost:${options.port}`);
  } else {
    if (Object.values(runner.status.tests).filter(test => test && !test.skip).length == 0) {
      logger.warn("Don't have any tests to run");
      // eslint-disable-next-line no-process-exit
      void shutdownWorkers().then(() => process.exit());
      return;
    }
    runner.once('stop', () => {
      var _runner6, _runner7;
      const tests = Object.values(((_runner6 = runner) === null || _runner6 === void 0 ? void 0 : _runner6.status.tests) ?? {});
      const isSuccess = tests.filter(isDefined).filter(({
        skip
      }) => !skip).every(({
        status
      }) => status == 'success');
      // TODO output summary
      process.exitCode = isSuccess ? 0 : -1;
      if (!config.failFast) outputUnnecessaryImages(config.screenDir, testsToImages(tests));
      sendScreenshotsCount(config, options, (_runner7 = runner) === null || _runner7 === void 0 ? void 0 : _runner7.status).catch(reason => {
        const error = reason instanceof Error ? reason.stack ?? reason.message : reason;
        logger.warn(`Can't send telemetry: ${error}`);
      }).finally(() => {
        // eslint-disable-next-line no-process-exit
        void shutdownWorkers().then(() => process.exit());
      });
    });
    // TODO grep
    runner.start(Object.keys(runner.status.tests));
  }
}