"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const events_1 = require("events");
const url_1 = require("url");
const axios_1 = __importDefault(require("axios"));
const debug_1 = __importDefault(require("debug"));
const TunnelCluster_1 = __importDefault(require("./TunnelCluster"));
const debugLog = (0, debug_1.default)('localtunnel:client');
class Tunnel extends events_1.EventEmitter {
    constructor(opts = {}) {
        super();
        this.opts = opts;
        this.closed = false;
        if (!this.opts.host) {
            this.opts.host = 'https://localtunnel.me';
        }
    }
    _getInfo(body) {
        const { id, ip, port, url, cached_url, max_conn_count } = body;
        const { host, port: local_port, local_host } = this.opts;
        const { local_https, local_cert, local_key, local_ca, allow_invalid_cert } = this.opts;
        return {
            name: id,
            url,
            cached_url,
            max_conn: max_conn_count || 1,
            remote_host: (0, url_1.parse)(host).hostname,
            remote_ip: ip,
            remote_port: port,
            local_port,
            local_host,
            local_https,
            local_cert,
            local_key,
            local_ca,
            allow_invalid_cert,
        };
    }
    _init(callback) {
        const opt = this.opts;
        const getInfo = this._getInfo.bind(this);
        const params = {
            responseType: 'json',
        };
        const baseUri = `${opt.host}/`;
        // no subdomain at first, maybe use requested domain
        const assignedDomain = opt.subdomain;
        // where to quest
        const uri = baseUri + (assignedDomain || '?new') +
            (opt.port ? `&port=${opt.port}` : '');
        debugLog('请求隧道服务器: %s', uri);
        const getUrl = () => {
            axios_1.default
                .get(uri, params)
                .then(res => {
                const body = res.data;
                debugLog('got tunnel information', body);
                if (res.status !== 200) {
                    const err = new Error((body && body.message) || 'localtunnel server returned an error, please try again');
                    return callback(err);
                }
                callback(null, getInfo(body));
            })
                .catch(err => {
                debugLog(`tunnel server offline: ${err.message}, retry 1s`);
                return setTimeout(getUrl, 1000);
            });
        };
        getUrl();
    }
    _establish(info) {
        // 为 max_conn 设置默认值
        const maxConn = info.max_conn || 1; // 如果 max_conn 未定义，默认为 1
        // increase max event listeners so that localtunnel consumers don't get
        // warning messages as soon as they setup even one listener. See #71
        this.setMaxListeners(maxConn + (events_1.EventEmitter.defaultMaxListeners || 10));
        this.tunnelCluster = new TunnelCluster_1.default(info);
        // only emit the url the first time
        this.tunnelCluster.once('open', () => {
            this.emit('url', info.url);
        });
        // re-emit socket error
        this.tunnelCluster.on('error', err => {
            debugLog('got socket error', err.message);
            this.emit('error', err);
        });
        let tunnelCount = 0;
        // track open count
        this.tunnelCluster.on('open', tunnel => {
            tunnelCount++;
            debugLog('tunnel open [total: %d]', tunnelCount);
            const closeHandler = () => {
                tunnel.destroy();
            };
            if (this.closed) {
                return closeHandler();
            }
            this.once('close', closeHandler);
            tunnel.once('close', () => {
                this.removeListener('close', closeHandler);
            });
        });
        // when a tunnel dies, open a new one
        this.tunnelCluster.on('dead', () => {
            var _a;
            tunnelCount--;
            debugLog('tunnel dead [total: %d]', tunnelCount);
            if (this.closed) {
                return;
            }
            (_a = this.tunnelCluster) === null || _a === void 0 ? void 0 : _a.open();
        });
        this.tunnelCluster.on('request', req => {
            this.emit('request', req);
        });
        // establish as many tunnels as allowed
        for (let count = 0; count < maxConn; ++count) { // 使用 maxConn 变量
            this.tunnelCluster.open();
        }
    }
    open(callback) {
        this._init((err, info) => {
            if (err) {
                return callback(err);
            }
            this.clientId = info.name;
            this.url = info.url;
            // `cached_url` is only returned by proxy servers that support resource caching.
            if (info.cached_url) {
                this.cachedUrl = info.cached_url;
            }
            this._establish(info);
            callback(null);
        });
    }
    close() {
        this.closed = true;
        this.emit('close');
    }
}
exports.default = Tunnel;
