import { Client } from 'ssh2';
import constants from '../constants.js';
import OutputManager from '../outputManager.js';
export default class BuildHostWaiter {
    _DOCKER_TIMEOUT_MS = constants.BUILD_HOST_WAITER_DOCKER_TIMEOUT;
    _MAX_WAIT_ATTEMPTS = constants.BUILD_HOST_WAITER_MAX_ATTEMPTS;
    _WAIT_MS = constants.BUILD_HOST_WAITER_WAIT_TIME_MS;
    dockerRemoteHost;
    dockerRemotePort;
    sshHost;
    sshPort;
    sshPrivateKey;
    sshUsername;
    constructor(params) {
        this.dockerRemoteHost = params.dockerRemoteHost;
        this.dockerRemotePort = params.dockerRemotePort;
        this.sshHost = params.sshHost;
        this.sshPort = params.sshPort;
        this.sshPrivateKey = params.sshPrivateKey;
        this.sshUsername = params.sshUsername;
    }
    async wait() {
        let attempts = 1;
        while (true) {
            // eslint-disable-next-line no-await-in-loop
            if (await this.isReady()) {
                return;
            }
            if (attempts < this._MAX_WAIT_ATTEMPTS) {
                OutputManager.detail(`Docker not available yet, waiting to retry (attempt ${attempts}/${this._MAX_WAIT_ATTEMPTS})`);
                attempts++;
                // eslint-disable-next-line no-await-in-loop
                await new Promise((resolve) => {
                    setTimeout(resolve, this._WAIT_MS);
                });
            }
            else {
                break;
            }
        }
        throw new Error(`Docker was still not available after ${this._MAX_WAIT_ATTEMPTS} attempts.`);
    }
    async isReady() {
        return new Promise((resolve, _) => {
            const conn = new Client();
            const timeout = setTimeout(() => {
                conn.end();
                resolve(false);
            }, this._DOCKER_TIMEOUT_MS);
            conn.on('ready', () => {
                conn.exec(`nc -z ${this.dockerRemoteHost} ${this.dockerRemotePort}`, (err, stream) => {
                    if (err) {
                        clearTimeout(timeout);
                        conn.end();
                        return resolve(false);
                    }
                    stream.on('close', (code) => {
                        clearTimeout(timeout);
                        conn.end();
                        resolve(code === 0);
                    }).on('data', () => { }).stderr.on('data', () => { });
                });
            }).on('error', () => {
                clearTimeout(timeout);
                resolve(false);
            }).connect({ host: this.sshHost, port: this.sshPort, privateKey: this.sshPrivateKey, username: this.sshUsername });
        });
    }
}
