// We want to keep `system` export to be used as namespace
import os from "node:os";
import path from "node:path";
// We want to keep `system` export as it's more readable and easier to understand
import system from "systeminformation";
/**
 * Singleton class to collect and provide system information
 */
class System {
    // static data only needs to be collected once
    staticDataCollected = false;
    // disk I/O is not measurable in some environments
    diskIOMeasurable = true;
    cpuCores = 0;
    cpuThreads = 0;
    cpuNodeSystemSecondsTotal = 0;
    cpuNodeUserSecondsTotal = 0;
    // Note: CPU I/O wait is not measured by os.cpus()
    cpuNodeIOWaitSecondsTotal = 0;
    cpuNodeIdleSecondsTotal = 0;
    memoryNodeBytesTotal = 0;
    memoryNodeBytesFree = 0;
    memoryNodeBytesCached = 0;
    memoryNodeBytesBuffers = 0;
    diskNodeBytesTotal = 0;
    diskNodeBytesFree = 0;
    // Note: disk I/O seconds is currently unused by beaconcha.in
    diskNodeIOSeconds = 0;
    diskNodeReadsTotal = 0;
    diskNodeWritesTotal = 0;
    networkNodeBytesTotalReceive = 0;
    networkNodeBytesTotalTransmit = 0;
    miscNodeBootTsSeconds = 0;
    miscOs = "unk";
    /**
     * Collect system data and update cached values
     */
    async collectData(logger) {
        const debug = (dataType, e) => logger.debug(`Failed to collect ${dataType} data`, {}, e);
        await Promise.all([
            this.collectStaticData().catch((e) => debug("static system", e)),
            this.collectCpuData().catch((e) => debug("CPU", e)),
            this.collectMemoryData().catch((e) => debug("memory", e)),
            this.collectDiskData().catch((e) => debug("disk", e)),
            this.collectNetworkData().catch((e) => debug("network", e)),
        ]);
        this.miscNodeBootTsSeconds = this.getSystemBootTime();
    }
    async collectStaticData() {
        if (this.staticDataCollected)
            return;
        const cpu = await system.cpu();
        // Note: inside container this might be inaccurate as
        // physicalCores in some cases is the count of logical CPU cores
        this.cpuCores = cpu.physicalCores;
        this.cpuThreads = cpu.cores;
        this.miscOs = this.getNormalizedOsVersion();
        this.staticDataCollected = true;
    }
    async collectCpuData() {
        const cpuTimes = {};
        for (const cpu of os.cpus()) {
            // sum up CPU times per mode and convert to seconds
            for (const [mode, time] of Object.entries(cpu.times)) {
                if (cpuTimes[mode] == null)
                    cpuTimes[mode] = 0;
                cpuTimes[mode] += Math.floor(time / 1000);
            }
        }
        // Note: currently beaconcha.in expects system CPU seconds to be everything
        this.cpuNodeSystemSecondsTotal = Object.values(cpuTimes).reduce((total, time) => total + time, 0);
        this.cpuNodeUserSecondsTotal = cpuTimes.user;
        this.cpuNodeIdleSecondsTotal = cpuTimes.idle;
    }
    async collectMemoryData() {
        const memory = await system.mem();
        this.memoryNodeBytesTotal = memory.total;
        this.memoryNodeBytesFree = memory.free;
        this.memoryNodeBytesCached = memory.cached;
        this.memoryNodeBytesBuffers = memory.buffers;
    }
    async collectDiskData() {
        const fileSystems = await system.fsSize();
        // get file system root, on windows this is the name of the hard disk partition
        const rootFs = process.platform === "win32" ? process.cwd().split(path.sep)[0] : "/";
        // only consider root file system, if it does not exist use first entry in the list
        const fileSystem = fileSystems.find((fs) => fs.mount === rootFs) ?? fileSystems[0];
        this.diskNodeBytesTotal = fileSystem.size;
        this.diskNodeBytesFree = fileSystem.available;
        if (this.diskIOMeasurable) {
            const disk = await system.disksIO();
            if (disk != null && disk.rIO !== 0) {
                // Note: rIO and wIO might not be available inside container
                // see https://github.com/sebhildebrandt/systeminformation/issues/777
                this.diskNodeReadsTotal = disk.rIO;
                this.diskNodeWritesTotal = disk.wIO;
            }
            else {
                this.diskIOMeasurable = false;
            }
        }
    }
    async collectNetworkData() {
        // defaults to first external network interface
        const [network] = await system.networkStats();
        // Note: rx_bytes and tx_bytes will be inaccurate if process
        // runs inside container as it only captures local network traffic
        this.networkNodeBytesTotalReceive = network.rx_bytes;
        this.networkNodeBytesTotalTransmit = network.tx_bytes;
    }
    getNormalizedOsVersion() {
        switch (process.platform) {
            case "linux":
                return "lin";
            case "darwin":
                return "mac";
            case "win32":
                return "win";
            default:
                return "unk";
        }
    }
    getSystemBootTime() {
        return Math.floor(Date.now() / 1000 - os.uptime());
    }
}
export default new System();
//# sourceMappingURL=system.js.map