"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.selfCheckMiddleware = void 0;
exports.selfCheck = selfCheck;
const cross_const_1 = require("./cross-const");
const github_1 = require("./github");
const net_1 = require("net");
const lodash_1 = __importDefault(require("lodash"));
const cross_1 = require("./cross");
const util_http_1 = require("./util-http");
let selfChecking = false;
const CHECK_URL = cross_const_1.SPECIAL_URI + 'self-check';
const selfCheckMiddleware = (ctx, next) => {
    if (!selfChecking || !ctx.url.startsWith(CHECK_URL))
        return next();
    ctx.body = 'HFS';
    ctx.state.skipFilters = true;
};
exports.selfCheckMiddleware = selfCheckMiddleware;
async function selfCheck(url) {
    const prjInfo = await (0, github_1.getProjectInfo)();
    console.log(`Checking server ${url}`);
    const parsed = new URL(url);
    const family = !(0, net_1.isIP)(parsed.hostname) ? undefined : (0, net_1.isIPv6)(parsed.hostname) ? 6 : 4;
    try {
        selfChecking = true;
        for (const services of lodash_1.default.chunk(lodash_1.default.shuffle(prjInfo.selfCheckServices), 2)) {
            try {
                const results = await Promise.allSettled(services.map(async (svc) => {
                    if (!svc.url || svc.type)
                        throw 'unsupported ' + svc.type; // only default type supported for now
                    let { url: serviceUrl, body, regexpSuccess, regexpFailure, ...rest } = svc;
                    const service = new URL(serviceUrl).hostname;
                    console.log('Trying external service', service);
                    console.debug(svc);
                    body = applySymbols(body);
                    serviceUrl = applySymbols(serviceUrl);
                    const timeout = 8_000;
                    const res = await (0, cross_1.haveTimeout)(timeout, (0, util_http_1.httpString)(serviceUrl, { family, timeout, ...rest, body }));
                    const success = new RegExp(regexpSuccess).test(res);
                    const failure = new RegExp(regexpFailure).test(res);
                    if (success === failure)
                        throw 'inconsistent: ' + service + ': ' + res; // this result cannot be trusted
                    console.debug(service, 'responded', success);
                    return { success, service, url };
                }));
                // prefer a positive check so a fast false negative doesn't mask a working service
                return (0, cross_1.findDefined)(results, x => x.status === 'fulfilled' && x.value.success ? x.value : undefined)
                    || (0, cross_1.findDefined)(results, x => x.status === 'fulfilled' ? x.value : undefined);
            }
            catch (e) {
                console.debug(e?.errors?.map(String) || e?.cause || String(e));
            }
        }
    }
    finally {
        selfChecking = false;
    }
    function applySymbols(s) {
        return s?.replace('$IP', parsed.hostname)
            .replace('$PORT', parsed.port || (parsed.protocol === 'https:' ? '443' : '80'))
            .replace('$URL', url.replace(/\/$/, '') + CHECK_URL);
    }
}
