import { createWriteStream, existsSync, readFileSync, readdirSync, unlink } from 'fs';
import cluster from 'cluster';
import { isDefined, noop, isFunction } from '../types';
import { emitShutdownMessage, sendShutdownMessage } from './messages';
import findCacheDir from 'find-cache-dir';
import { get } from 'https';
export const isShuttingDown = {
  current: false
};
export const LOCALHOST_REGEXP = /(localhost|127\.0\.0\.1)/i;
export const extensions = ['.js', '.jsx', '.ts', '.tsx'];
export const skipOptionKeys = ['in', 'kinds', 'stories', 'tests', 'reason'];
function matchBy(pattern, value) {
  return typeof pattern == 'string' && pattern == value || Array.isArray(pattern) && pattern.includes(value) || pattern instanceof RegExp && pattern.test(value) || !isDefined(pattern);
}
export function shouldSkip(browser, meta, skipOptions, test) {
  if (typeof skipOptions != 'object') {
    return skipOptions;
  }
  for (const skipKey in skipOptions) {
    const reason = shouldSkipByOption(browser, meta, skipOptions[skipKey], skipKey, test);
    if (reason) return reason;
  }
  return false;
}
export function shouldSkipByOption(browser, meta, skipOption, reason, test) {
  if (Array.isArray(skipOption)) {
    for (const skip of skipOption) {
      const result = shouldSkipByOption(browser, meta, skip, reason, test);
      if (result) return result;
    }
    return false;
  }
  const {
    in: browsers,
    kinds,
    stories,
    tests
  } = skipOption;
  const {
    kind,
    story
  } = meta;
  const skipByBrowser = matchBy(browsers, browser);
  const skipByKind = matchBy(kinds, kind);
  const skipByStory = matchBy(stories, story);
  const skipByTest = !isDefined(test) || matchBy(tests, test);
  return skipByBrowser && skipByKind && skipByStory && skipByTest && reason;
}
export async function shutdownWorkers() {
  isShuttingDown.current = true;
  await Promise.all(Object.values(cluster.workers ?? {}).filter(isDefined).filter(worker => worker.isConnected()).map(worker => new Promise(resolve => {
    const timeout = setTimeout(() => worker.kill(), 10000);
    worker.on('exit', () => {
      clearTimeout(timeout);
      resolve();
    });
    sendShutdownMessage(worker);
  })));
  emitShutdownMessage();
}
export function shutdown() {
  // eslint-disable-next-line no-process-exit
  process.exit();
}
export function getCreeveyCache() {
  return findCacheDir({
    name: 'creevey',
    cwd: __dirname
  });
}
export async function runSequence(seq, predicate) {
  for (const fn of seq) {
    if (predicate()) await fn();
  }
}
export function testsToImages(tests) {
  return new Set([].concat(...tests.filter(isDefined).map(({
    browser,
    testName,
    storyPath,
    results
  }) => {
    var _results$slice$;
    return Object.keys((results === null || results === void 0 || (_results$slice$ = results.slice(-1)[0]) === null || _results$slice$ === void 0 ? void 0 : _results$slice$.images) ?? {}).map(image => `${[...storyPath, testName, browser, browser == image ? undefined : image].filter(isDefined).join('/')}.png`);
  })));
}

// https://tuhrig.de/how-to-know-you-are-inside-a-docker-container/
export const isInsideDocker = existsSync('/proc/1/cgroup') && /docker/.test(readFileSync('/proc/1/cgroup', 'utf8'));
export const downloadBinary = (downloadUrl, destination) => new Promise((resolve, reject) => get(downloadUrl, response => {
  if (response.statusCode == 302) {
    const {
      location
    } = response.headers;
    if (!location) return reject(new Error(`Couldn't download selenoid. Status code: ${response.statusCode ?? 'UNKNOWN'}`));
    return resolve(downloadBinary(location, destination));
  }
  if (response.statusCode != 200) return reject(new Error(`Couldn't download selenoid. Status code: ${response.statusCode ?? 'UNKNOWN'}`));
  const fileStream = createWriteStream(destination);
  response.pipe(fileStream);
  fileStream.on('finish', () => {
    fileStream.close();
    resolve();
  });
  fileStream.on('error', error => {
    unlink(destination, noop);
    reject(error);
  });
}));
export function removeProps(obj, propPath) {
  const [prop, ...restPath] = propPath;
  if (restPath.length > 0) {
    if (typeof prop == 'string') obj[prop] && removeProps(obj[prop], restPath);
    if (isFunction(prop)) Object.keys(obj).filter(prop).forEach(key => obj[key] && removeProps(obj[key], restPath));
  } else {
    if (typeof prop == 'string') delete obj[prop];
    if (isFunction(prop)) Object.keys(obj).filter(prop).forEach(key => delete obj[key]);
  }
}
export function readDirRecursive(dirPath) {
  return [].concat(...readdirSync(dirPath, {
    withFileTypes: true
  }).map(dirent => dirent.isDirectory() ? readDirRecursive(`${dirPath}/${dirent.name}`) : [`${dirPath}/${dirent.name}`]));
}