import cluster from 'cluster';
import { isDockerMessage } from '../types';
import { subscribeOn, sendDockerMessage, emitDockerMessage } from './messages';
import { isInsideDocker, LOCALHOST_REGEXP } from './utils';
import Dockerode from 'dockerode';
import { Writable } from 'stream';
import ora from 'ora';
import { logger } from './logger';
const docker = new Dockerode();
class DevNull extends Writable {
  _write(_chunk, _encoding, callback) {
    setImmediate(callback);
  }
}
export async function pullImages(images, {
  auth,
  platform
} = {}) {
  const args = {};
  if (auth) args.authconfig = auth;
  if (platform) args.platform = platform;
  logger.info('Pull docker images');
  for (const image of images) {
    await new Promise((resolve, reject) => {
      const spinner = ora(`${image}: Pull start`).start();

      // eslint-disable-next-line @typescript-eslint/no-floating-promises
      docker.pull(image, args, function (pullError, stream) {
        if (pullError) {
          spinner.fail();
          return reject(pullError);
        }

        // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
        docker.modem.followProgress(stream, onFinished, onProgress);
        function onFinished(error) {
          if (error) {
            spinner.fail();
            return reject(error);
          }
          spinner.succeed(`${image}: Pull complete`);
          resolve();
        }
        function onProgress(event) {
          if (!/^[a-z0-9]{12}$/i.test(event.id)) return;
          spinner.text = `${image}: [${event.id}] ${event.status} ${event.progress ? `${event.progress}` : ''}`;
        }
      });
    });
  }
}
export async function runImage(image, args, options, debug) {
  await Promise.all((await docker.listContainers({
    all: true,
    filters: {
      ancestor: [image]
    }
  })).map(async info => {
    const container = docker.getContainer(info.Id);
    try {
      await container.stop();
    } catch (_) {
      /* noop */
    }
    await container.remove();
  }));
  const hub = docker.run(image, args, debug ? process.stdout : new DevNull(), options, error => {
    if (error) throw error;
  });
  return new Promise(resolve => {
    hub.once('container', container => {
      // eslint-disable-next-line @typescript-eslint/no-misused-promises
      subscribeOn('shutdown', async () => {
        try {
          await container.stop();
          await container.remove();
        } catch (error) {
          /* noop */
        }
      });
    });
    hub.once('start', container => void container.inspect().then(info => resolve(info.NetworkSettings.Networks.bridge.IPAddress)));
  });
}
export default async function (config, browser, startContainer) {
  if (cluster.isPrimary) {
    const host = await startContainer();
    let gridUrl = 'http://localhost:4444/wd/hub';
    gridUrl = isInsideDocker ? gridUrl.replace(LOCALHOST_REGEXP, host) : gridUrl;
    cluster.on('message', (worker, message) => {
      if (!isDockerMessage(message)) return;
      const dockerMessage = message;
      if (dockerMessage.type != 'start') return;
      sendDockerMessage(worker, {
        type: 'success',
        payload: {
          gridUrl
        }
      });
    });
  } else {
    if (browser && config.browsers[browser].gridUrl) return Promise.resolve();
    return new Promise(resolve => {
      subscribeOn('docker', message => {
        if (message.type == 'success') {
          config.gridUrl = message.payload.gridUrl;
          resolve();
        }
      });
      emitDockerMessage({
        type: 'start'
      });
    });
  }
}