"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());
    });
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const https_1 = require("https");
const ripple_address_codec_1 = require("ripple-address-codec");
const errors_1 = require("../errors");
const defaultFaucets_1 = require("./defaultFaucets");
const _1 = __importDefault(require("."));
const INTERVAL_SECONDS = 1;
const MAX_ATTEMPTS = 20;
function fundWallet(wallet, options) {
    return __awaiter(this, void 0, void 0, function* () {
        if (!this.isConnected()) {
            throw new errors_1.RippledError('Client not connected, cannot call faucet');
        }
        const walletToFund = wallet && (0, ripple_address_codec_1.isValidClassicAddress)(wallet.classicAddress)
            ? wallet
            : _1.default.generate();
        const postBody = Buffer.from(new TextEncoder().encode(JSON.stringify({
            destination: walletToFund.classicAddress,
            xrpAmount: options === null || options === void 0 ? void 0 : options.amount,
        })));
        let startingBalance = 0;
        try {
            startingBalance = Number(yield this.getXrpBalance(walletToFund.classicAddress));
        }
        catch (_a) {
        }
        const httpOptions = getHTTPOptions(this, postBody, {
            hostname: options === null || options === void 0 ? void 0 : options.faucetHost,
            pathname: options === null || options === void 0 ? void 0 : options.faucetPath,
        });
        return returnPromise(httpOptions, this, startingBalance, walletToFund, postBody);
    });
}
function returnPromise(options, client, startingBalance, walletToFund, postBody) {
    return __awaiter(this, void 0, void 0, function* () {
        return new Promise((resolve, reject) => {
            const request = (0, https_1.request)(options, (response) => {
                const chunks = [];
                response.on('data', (data) => chunks.push(data));
                response.on('end', () => __awaiter(this, void 0, void 0, function* () {
                    return onEnd(response, chunks, client, startingBalance, walletToFund, resolve, reject);
                }));
            });
            request.write(postBody);
            request.on('error', (error) => {
                reject(error);
            });
            request.end();
        });
    });
}
function getHTTPOptions(client, postBody, options) {
    var _a, _b;
    const finalHostname = (_a = options === null || options === void 0 ? void 0 : options.hostname) !== null && _a !== void 0 ? _a : (0, defaultFaucets_1.getFaucetHost)(client);
    const finalPathname = (_b = options === null || options === void 0 ? void 0 : options.pathname) !== null && _b !== void 0 ? _b : (0, defaultFaucets_1.getDefaultFaucetPath)(finalHostname);
    return {
        hostname: finalHostname,
        port: 443,
        path: finalPathname,
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Content-Length': postBody.length,
        },
    };
}
function onEnd(response, chunks, client, startingBalance, walletToFund, resolve, reject) {
    var _a;
    return __awaiter(this, void 0, void 0, function* () {
        const body = Buffer.concat(chunks).toString();
        if ((_a = response.headers['content-type']) === null || _a === void 0 ? void 0 : _a.startsWith('application/json')) {
            const faucetWallet = JSON.parse(body);
            const classicAddress = faucetWallet.account.classicAddress;
            yield processSuccessfulResponse(client, classicAddress, walletToFund, startingBalance, resolve, reject);
        }
        else {
            reject(new errors_1.XRPLFaucetError(`Content type is not \`application/json\`: ${JSON.stringify({
                statusCode: response.statusCode,
                contentType: response.headers['content-type'],
                body,
            })}`));
        }
    });
}
function processSuccessfulResponse(client, classicAddress, walletToFund, startingBalance, resolve, reject) {
    return __awaiter(this, void 0, void 0, function* () {
        if (!classicAddress) {
            reject(new errors_1.XRPLFaucetError(`The faucet account is undefined`));
            return;
        }
        try {
            const updatedBalance = yield getUpdatedBalance(client, classicAddress, startingBalance);
            if (updatedBalance > startingBalance) {
                resolve({
                    wallet: walletToFund,
                    balance: yield getUpdatedBalance(client, walletToFund.classicAddress, startingBalance),
                });
            }
            else {
                reject(new errors_1.XRPLFaucetError(`Unable to fund address with faucet after waiting ${INTERVAL_SECONDS * MAX_ATTEMPTS} seconds`));
            }
        }
        catch (err) {
            if (err instanceof Error) {
                reject(new errors_1.XRPLFaucetError(err.message));
            }
            reject(err);
        }
    });
}
function getUpdatedBalance(client, address, originalBalance) {
    return __awaiter(this, void 0, void 0, function* () {
        return new Promise((resolve, reject) => {
            let attempts = MAX_ATTEMPTS;
            const interval = setInterval(() => __awaiter(this, void 0, void 0, function* () {
                if (attempts < 0) {
                    clearInterval(interval);
                    resolve(originalBalance);
                }
                else {
                    attempts -= 1;
                }
                try {
                    let newBalance;
                    try {
                        newBalance = Number(yield client.getXrpBalance(address));
                    }
                    catch (_a) {
                    }
                    if (newBalance > originalBalance) {
                        clearInterval(interval);
                        resolve(newBalance);
                    }
                }
                catch (err) {
                    clearInterval(interval);
                    if (err instanceof Error) {
                        reject(new errors_1.XRPLFaucetError(`Unable to check if the address ${address} balance has increased. Error: ${err.message}`));
                    }
                    reject(err);
                }
            }), INTERVAL_SECONDS * 1000);
        });
    });
}
exports.default = fundWallet;
//# sourceMappingURL=fundWallet.js.map