"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const lodash_1 = __importDefault(require("lodash"));
const consoleLog_1 = require("./consoleLog");
const cross_1 = require("./cross");
const misc_1 = require("./misc");
const events_1 = __importDefault(require("./events"));
const log_1 = require("./log");
const SendList_1 = require("./SendList");
const serveFile_1 = require("./serveFile");
const ips_1 = require("./ips");
const connections_1 = require("./connections");
exports.default = {
    async get_log_info() {
        const current = Object.fromEntries(await Promise.all(log_1.loggers.map(async (x) => [x.name, await (0, misc_1.statWithTimeout)(x.path).then(s => s.size, () => 0)])));
        return { current, rotated: await (0, log_1.getRotatedFiles)() };
    },
    async get_log_file({ file = 'log', range = '' }, ctx) {
        (0, misc_1.apiAssertTypes)({ string: { file, range } });
        const log = lodash_1.default.find(log_1.loggers, { name: file });
        if (!log)
            throw cross_1.HTTP_NOT_FOUND;
        if (!log.path)
            throw cross_1.HTTP_NOT_ACCEPTABLE;
        (0, serveFile_1.forceDownload)(ctx, log.path);
        if (range)
            ctx.request.header.range = `bytes=${range}`;
        if (ctx.method === 'POST') // this would cause method_not_allowed
            ctx.method = 'GET';
        await (0, serveFile_1.serveFile)(ctx, log.path);
        return null;
    },
    get_log({ file = 'log' }, ctx) {
        (0, misc_1.apiAssertTypes)({ string: { file } });
        const files = file.split('|'); // potentially more than one
        return new SendList_1.SendListReadable({
            bufferTime: 10,
            async doAtStart(list) {
                if (file === 'disconnections') {
                    for (const x of connections_1.disconnectionsLog)
                        list.add(x);
                    ctx.res.once('close', events_1.default.on('disconnection', x => list.add(x)));
                    return list.ready();
                }
                if (file === 'ips') {
                    for await (const [k, v] of ips_1.ips.iterator())
                        list.add({ ip: k, ...v });
                    return list.ready();
                }
                if (file === 'console') {
                    for (const chunk of lodash_1.default.chunk(consoleLog_1.consoleLog, 1000)) { // avoid occupying the thread too long
                        for (const x of chunk)
                            list.add(x);
                        await (0, cross_1.wait)(0);
                    }
                    ctx.res.once('close', events_1.default.on('console', x => list.add(x)));
                    return list.ready();
                }
                // for other logs we only provide updates. Use get_log_file to download past content
                if (lodash_1.default.some(files, x => !lodash_1.default.some(log_1.loggers, { name: x })))
                    return list.error(cross_1.HTTP_NOT_FOUND, true);
                list.ready();
                // unsubscribe when the connection is interrupted
                ctx.res.once('close', events_1.default.on(files, x => list.add(Object.assign(lodash_1.default.pick(x.ctx, ['ip', 'method', 'status']), x, { ctx: undefined }))));
            }
        });
    },
    async delete_ips({ ip, ts }) {
        (0, misc_1.apiAssertTypes)({ string_undefined: { ip, ts } });
        if (ip) {
            if (!ips_1.ips.has(ip))
                throw cross_1.HTTP_NOT_FOUND;
            ips_1.ips.del(ip);
            return {};
        }
        if (ts) {
            ts = new Date(ts);
            let n = 0;
            for await (const [k, rec] of ips_1.ips.iterator())
                if (rec.ts <= ts) {
                    ips_1.ips.del(k);
                    ++n;
                }
            return { n };
        }
        throw cross_1.HTTP_BAD_REQUEST;
    },
    reset_ips() {
        return ips_1.ips.clear();
    },
};
