"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SshClient = void 0;
const ssh2_1 = require("ssh2");
const event_1 = require("@f2/event");
const ssh_shell_1 = require("./ssh-shell");
class SshClient {
    constructor(host, options = {}) {
        this.host = host;
        this.options = options;
        this._closed = true;
        (0, event_1.beginEventEmitter)(this);
    }
    connect() {
        if (!this._closed) {
            throw new Error('Already opened');
        }
        const client = new ssh2_1.Client();
        return new Promise((resolve, reject) => {
            client.on('ready', () => {
                this._client = client;
                client.removeAllListeners('error');
                client.on('error', (err) => this.emit('error', err));
                resolve();
            });
            client.on('close', () => {
                this._closed = true;
                this.close();
            });
            client.on('connect', () => {
                this._client = client;
                this._closed = false;
            });
            client.on('timeout', () => {
                this.close();
            });
            client.on('error', reject);
            client.connect({
                host: this.host,
                port: this.options.port,
                password: this.options.password,
                username: this.options.user,
                timeout: 5000,
                readyTimeout: 5000,
                keepaliveInterval: 10000,
                keepaliveCountMax: 2
            });
        });
    }
    close() {
        return __awaiter(this, void 0, void 0, function* () {
            const client = this._client;
            if (!client) {
                return;
            }
            this._client = undefined;
            const end = () => {
                this.emit('disconnect');
            };
            return new Promise((resolve) => {
                if (this._closed) {
                    end();
                }
                else {
                    client.on('close', () => {
                        end();
                        resolve();
                    });
                    client.destroy();
                }
            });
        });
    }
    exec(cmd, direct = false) {
        return new Promise((resolve, reject) => {
            this._client.exec(cmd, (err, channel) => {
                if (err) {
                    return reject(err);
                }
                if (!direct) {
                    return resolve(channel);
                }
                const chunks = [];
                channel.stdout.on('data', (chunk) => {
                    chunks.push(chunk);
                });
                channel.stdout.on('end', () => {
                    resolve(Buffer.concat(chunks).toString().trim().split('\n'));
                });
            });
        });
    }
    shell(opts) {
        return new ssh_shell_1.SshShell(this._client, opts);
    }
}
exports.SshClient = SshClient;
