"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.get_dynamic_dns_error = get_dynamic_dns_error;
const config_1 = require("./config");
const cross_1 = require("./cross");
const lodash_1 = __importDefault(require("lodash"));
const util_http_1 = require("./util-http");
const node_net_1 = require("node:net");
const net_1 = require("net");
const const_1 = require("./const");
const events_1 = __importDefault(require("./events"));
const nat_1 = require("./nat");
const node_stream_1 = require("node:stream");
// optionally you can append '>' and a regular expression to determine what body is considered successful
const dynamicDnsUrl = (0, config_1.defineConfig)(cross_1.CFG.dynamic_dns_url, '');
// listening this event will trigger public-ips fetching
const EVENT = 'publicIpsChanged';
let stopFetching;
let lastIPs;
let lastMap;
events_1.default.onListeners(EVENT, cbs => {
    stopFetching?.();
    if (!cbs?.size)
        return;
    stopFetching = (0, cross_1.repeat)(cross_1.HOUR, async () => {
        const IPs = await (0, nat_1.getPublicIps)();
        if (lodash_1.default.isEqual(lastIPs, IPs))
            return;
        lastIPs = IPs;
        events_1.default.emit(EVENT, lastMap = {
            IPs,
            IPX: IPs[0] || '',
            IP4: lodash_1.default.find(IPs, node_net_1.isIPv4) || '',
            IP6: lodash_1.default.find(IPs, net_1.isIPv6) || '',
        });
    });
});
let stopListening;
let last;
dynamicDnsUrl.sub(v => {
    stopListening?.();
    if (!v)
        return;
    stopListening = events_1.default.on(EVENT, async () => {
        if (!lastMap)
            return; // called at start once, before first getPublicIps. Just skip it
        const lines = dynamicDnsUrl.get();
        const all = await Promise.all(lines.split('\n').map(async (line) => {
            const [templateUrl, re] = (0, cross_1.splitAt)('>', line);
            const url = (0, cross_1.replace)(templateUrl, lastMap, '$');
            const error = await (0, util_http_1.httpWithBody)(url, { httpThrow: false, headers: { 'User-Agent': "HFS/" + const_1.VERSION } }) // UA specified as requested by no-ip guidelines
                .then(async (res) => {
                const str = String(res.body).trim();
                return (re ? str.match(re) : res.ok) ? '' : (str || res.statusMessage);
            }, (err) => err.code || err.message || String(err));
            return { ts: new Date().toJSON(), error, url };
        }));
        last = lodash_1.default.find(all, 'error') || all[0]; // the system is designed for just one result, and we give precedence to errors
        events_1.default.emit('dynamicDnsError', last);
        console.log('Dynamic dns update', last?.error || 'ok');
    }, { callNow: true });
});
async function get_dynamic_dns_error() {
    let unsub;
    return new node_stream_1.Readable({
        objectMode: true,
        async read() {
            if (unsub)
                return;
            if (last)
                this.push(last); // start by sending current state
            unsub = events_1.default.on('dynamicDnsError', x => this.push(x)); // send updates, if any. This simplified way to manage the data stream is acceptable for this case of extremely low throughput
        },
        async destroy() {
            unsub();
            this.push(null);
        }
    });
}
