"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RemoteNodeService = void 0;
/*
 * Copyright 2021 NEM
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
const dns_1 = require("dns");
const symbol_sdk_1 = require("symbol-sdk");
const logger_1 = require("../logger");
const LoggerFactory_1 = require("../logger/LoggerFactory");
const logger = LoggerFactory_1.default.getLogger(logger_1.LogType.System);
class RemoteNodeService {
    async resolveCurrentFinalizationEpoch(presetData) {
        var _a;
        const votingNode = (_a = presetData.nodes) === null || _a === void 0 ? void 0 : _a.find((n) => n.voting);
        if (!votingNode) {
            return presetData.lastKnownNetworkEpoch;
        }
        const remoteNodeService = new RemoteNodeService();
        if (!(await remoteNodeService.isConnectedToInternet())) {
            return presetData.lastKnownNetworkEpoch;
        }
        return (await remoteNodeService.getBestFinalizationEpoch(presetData.knownRestGateways)) || presetData.lastKnownNetworkEpoch;
    }
    async getBestFinalizationEpoch(urls) {
        if (!urls || !urls.length) {
            return undefined;
        }
        const repositoryInfo = this.sortByHeight(await this.getKnownNodeRepositoryInfos(urls)).find((i) => i);
        const finalizationEpoch = repositoryInfo === null || repositoryInfo === void 0 ? void 0 : repositoryInfo.chainInfo.latestFinalizedBlock.finalizationEpoch;
        if (finalizationEpoch) {
            logger.info(`The current network finalization epoch is ${finalizationEpoch}`);
        }
        return finalizationEpoch;
    }
    async getBestRepositoryInfo(urls) {
        const repositoryInfo = this.sortByHeight(await this.getKnownNodeRepositoryInfos(urls)).find((i) => i);
        if (!repositoryInfo) {
            throw new Error(`No up and running node could be found out of: \n - ${urls.join('\n - ')}`);
        }
        logger.info(`Connecting to node ${repositoryInfo.restGatewayUrl}`);
        return repositoryInfo;
    }
    sortByHeight(repos) {
        return repos
            .filter((b) => b.chainInfo)
            .sort((a, b) => {
            if (!a.chainInfo) {
                return 1;
            }
            if (!b.chainInfo) {
                return -1;
            }
            return b.chainInfo.height.compare(a.chainInfo.height);
        });
    }
    isConnectedToInternet() {
        return new Promise((resolve) => {
            dns_1.lookup('google.com', (err) => {
                if (err && err.code == 'ENOTFOUND') {
                    resolve(false);
                }
                else {
                    resolve(true);
                }
            });
        });
    }
    async getKnownNodeRepositoryInfos(urls) {
        logger.info(`Looking for the best node out of:  \n - ${urls.join('\n - ')}`);
        return (await Promise.all(urls.map(async (restGatewayUrl) => {
            const repositoryFactory = new symbol_sdk_1.RepositoryFactoryHttp(restGatewayUrl);
            try {
                const chainInfo = await repositoryFactory.createChainRepository().getChainInfo().toPromise();
                return {
                    restGatewayUrl,
                    repositoryFactory,
                    chainInfo,
                };
            }
            catch (e) {
                const message = `There has been an error talking to node ${restGatewayUrl}. Error: ${e.message}}`;
                logger.warn(message);
                return undefined;
            }
        })))
            .filter((i) => i)
            .map((i) => i);
    }
}
exports.RemoteNodeService = RemoteNodeService;
