/**
 * ./src/classes/utils.ts
 * penguins-eggs v.25.7.x / ecmascript 2020
 * author: Piero Proietti
 * email: piero.proietti@gmail.com
 * license: MIT
 *
 * Refactored Utils class - imports from modular utilities
 */
import chalk from 'chalk';
import dns from 'dns';
import fs from 'fs';
// pjson
import { createRequire } from 'module';
import os from 'os';
import path from 'path';
import { shx, spawnSync } from '../lib/utils.js';
// libraries
import Distro from './distro.js';
import Kernel from './utils.d/kernel.js';
const require = createRequire(import.meta.url);
const pjson = require('../../package.json');
// __dirname
const __dirname = path.dirname(new URL(import.meta.url).pathname);
/**
 * Utils: general porpourse utils
 * @remarks all the utilities
 */
export default class Utils {
    /**
     * address
     */
    static address() {
        const interfaces = os.networkInterfaces();
        let address = '';
        if (interfaces !== undefined) {
            for (const devName in interfaces) {
                const iface = interfaces[devName];
                if (iface !== undefined) {
                    for (const alias of iface) {
                        if (alias.family === 'IPv4' &&
                            alias.address !== '127.0.0.1' &&
                            !alias.internal
                            && // take just the first!
                                address === '') {
                            address = alias.address;
                        }
                    }
                }
            }
        }
        return address;
    }
    static broadcast() {
        const netmask = Utils.netmask();
        const ip = Utils.address();
        const ipParts = ip.split('.').map(Number);
        const maskParts = netmask.split('.').map(Number);
        const broadcastParts = ipParts.map((part, index) => 
        // Bitwise OR tra il blocco IP e il blocco Netmask invertito (255 - mask)
        part | (255 - maskParts[index]));
        return broadcastParts.join('.');
    }
    /**
     * chpasswdPath
     * @returns
     */
    static chpasswdPath() {
        let chpasswdPath = '/usr/sbin/chpasswd';
        if (fs.existsSync(chpasswdPath)) {
            chpasswdPath = '/usr/bin/chpasswd';
        }
        return chpasswdPath;
    }
    /**
     * cidr
     */
    static cidr() {
        const interfaces = os.networkInterfaces();
        let cidr = '';
        if (interfaces !== undefined) {
            for (const devName in interfaces) {
                const iface = interfaces[devName];
                if (iface !== undefined) {
                    for (const alias of iface) {
                        if (alias.family === 'IPv4' &&
                            alias.address !== '127.0.0.1' &&
                            !alias.internal
                            && // take just the first!
                                cidr === '' && alias.cidr !== null) {
                            cidr = alias.cidr;
                        }
                    }
                }
            }
        }
        return cidr;
    }
    /**
     *
     * @param cmd
     */
    static commandExists(cmd) {
        return Boolean(shx.which(cmd));
    }
    /**
     *
     * @param msg
     */
    static async customConfirm(msg = 'Select yes to continue... ') {
        const { select } = await import('@inquirer/prompts');
        const answer = await select({
            message: msg,
            choices: [
                { name: 'No', value: 'No' },
                { name: 'Yes', value: 'Yes' },
            ],
            default: 'No',
        });
        console.log('User selected:', answer); // Debug logging
        return answer === 'Yes';
    }
    /**
     *
     * @param msg
     */
    static async customConfirmAbort(msg = 'Confirm') {
        const { select } = await import('@inquirer/prompts');
        const answer = await select({
            message: msg,
            choices: [
                { name: 'No', value: 'No' },
                { name: 'Yes', value: 'Yes' },
                { name: 'Abort', value: 'Abort' },
            ],
            default: 'Yes',
        });
        return JSON.stringify({ confirm: answer });
    }
    /**
     *
     * @param msg
     */
    static async customConfirmCompanion(msg = 'Select yes to continue... ') {
        const { select } = await import('@inquirer/prompts');
        const answer = await select({
            message: msg,
            choices: [
                { name: 'No', value: 'No' },
                { name: 'Yes', value: 'Yes' },
            ],
            default: 'No',
        });
        return JSON.stringify({ confirm: answer });
    }
    static async debug(cmd = 'cmd', procContinue = true) {
        console.log(chalk.redBright('DEBUG >>> ') + cmd + '\n');
        let msg = 'Press a key to exit...';
        if (procContinue) {
            msg = 'Press a key to continue...';
        }
        console.log(msg);
        const pressKeyToExit = spawnSync('read _ ', [], { shell: true, stdio: [0, 1, 2] });
        if (!procContinue) {
            process.exit(0);
        }
    }
    static error(msg = '') {
        console.error(pjson.shortName + ' >>> ' + chalk.bgRed(chalk.whiteBright(msg)));
    }
    /**
     *
     * @returns flag
     */
    static flag() {
        let arch = "-";
        if (Utils.isAppImage()) {
            arch += "AppImage";
        }
        else
            switch (process.arch) {
                case "arm64": {
                    arch += "arm64";
                    break;
                }
                case "ia32": {
                    arch += "i386";
                    break;
                }
                case "riscv64": {
                    arch += "riscv64";
                    break;
                }
                case "x64": {
                    arch += "x86_64";
                    break;
                }
                // No default
            }
        const title = `${pjson.name}`;
        const green = ` ${title}`.padEnd(25, " ");
        const white = ` Perri's brewery edition `.padEnd(25, " ");
        const red = ` v${pjson.version}${arch} `.padStart(25, " ");
        return chalk.bgGreen.whiteBright(green) +
            chalk.bgWhite.blue(white) +
            chalk.bgRed.whiteBright(red);
    }
    /**
     *
     * @param bytes
     * @param decimals
     * @returns
     */
    static formatBytes(bytes, decimals = 2) {
        if (bytes === 0)
            return '0 Bytes';
        const k = 1024;
        const dm = Math.max(decimals, 0);
        const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return Number.parseFloat((bytes / k ** i).toFixed(dm)) + sizes[i];
    }
    /**
     *
     * @param date
     */
    static formatDate(date) {
        const d = new Date(date);
        let month = String(d.getMonth() + 1);
        let day = String(d.getDate());
        const year = d.getFullYear();
        let hh = String(d.getHours());
        let mm = String(d.getMinutes());
        if (month.length < 2) {
            month = '0' + month;
        }
        if (day.length < 2) {
            day = '0' + day;
        }
        if (hh.length < 2) {
            hh = '0' + hh;
        }
        if (mm.length < 2) {
            mm = '0' + mm;
        }
        return [year, month, day].join('-') + '_' + hh + mm;
    }
    /**
     * @returns gateway
     */
    static gateway() {
        return shx.exec(`ip r | grep 'default' | awk '{print $3}'`, { silent: true }).stdout.trim();
        // return shx.exec(`route -n | grep 'UG[ \t]' | awk '{print $2}'`, { silent: true }).stdout.trim()
    }
    /**
     * Get author name
     */
    static getAuthorName() {
        return 'Piero Proietti piero.proietti@gmail.com';
    }
    /**
     * Return the Debian version
     * @returns {number} Versione di Debian
     */
    static getDebianVersion() {
        const cmd = "cat /etc/debian_version | /usr/bin/cut -f1 -d'.'";
        const version = Number(shx.exec(cmd, { silent: true }).stdout);
        return version;
    }
    /**
     * dns
     */
    static getDns() {
        return dns.getServers();
    }
    /**
     * getDomain
     */
    static getDomain() {
        return shx.exec('domainname', { silent: true }).stdout.trim();
        // return shx.exec(`route -n | grep 'UG[ \t]' | awk '{print $2}'`, { silent: true }).stdout.trim()
    }
    /**
     * return the short name of the package: eggs
     * @returns eggs
     */
    static getFriendName() {
        return pjson.shortName;
    }
    /**
     * Estimate the linuxfs dimension
     * (Refactored to use native FS instead of dd/od)
     * @returns {number} GB
     */
    static getLiveRootSpace(type = 'debian-live') {
        let squashFs = '/run/live/medium/live/filesystem.squashfs';
        if (type === 'mx') {
            squashFs = '/live/boot-dev/antiX/linuxfs';
        }
        // 1. Leggiamo il tipo di compressione DIRETTAMENTE dall'header del file SquashFS
        // L'identificativo della compressione è a offset 20 (2 bytes, little endian)
        // 1=gzip, 2=lzo, 3=lzma, 4=xz, 5=lz4, 6=zstd
        let compressionId = 0;
        try {
            if (fs.existsSync(squashFs)) {
                const fd = fs.openSync(squashFs, 'r');
                const buffer = Buffer.alloc(2);
                // Leggi 2 byte alla posizione 20
                fs.readSync(fd, buffer, 0, 2, 20);
                fs.closeSync(fd);
                compressionId = buffer.readUInt16LE(0);
            }
        }
        catch (error) {
            console.error("Error reading squashfs header:", error);
        }
        // 2. Determiniamo il fattore di compressione in base all'ID letto
        let compression_factor = 30; // Default conservative
        switch (compressionId) {
            case 1: { // gzip
                compression_factor = 37;
                break;
            }
            case 2: { // lzo
                compression_factor = 52;
                break;
            }
            case 3: { // lzma
                compression_factor = 52;
                break;
            }
            case 4: { // xz
                compression_factor = 31;
                break;
            }
            case 5: { // lz4
                compression_factor = 52;
                break;
            }
            case 6: { // zstd (aggiunto per completezza)
                compression_factor = 37; // simile a gzip come ratio medio
                break;
            }
            default: {
                compression_factor = 30;
            }
        }
        // 3. Calcolo dimensione Linux FS
        // Nota: shx.exec ritorna un oggetto, dobbiamo prendere .stdout e pulirlo
        let rootfs_file_size = 0;
        const dfCmdLinux = 'df /live/linux --output=used --total | /usr/bin/tail -n1';
        const dfResultLinux = shx.exec(dfCmdLinux, { silent: true }).stdout.trim();
        const linuxfs_used = Number(dfResultLinux) || 0; // Gestione caso NaN
        const linuxfs_file_size = (linuxfs_used * 1024 * 100) / compression_factor;
        // 4. Calcolo persist-root (se esiste)
        if (fs.existsSync('/live/persist-root')) {
            const dfCmdRoot = 'df /live/persist-root --output=used --total | /usr/bin/tail -n1';
            const dfResultRoot = shx.exec(dfCmdRoot, { silent: true }).stdout.trim();
            rootfs_file_size = (Number(dfResultRoot) || 0) * 1024;
        }
        let rootSpaceNeeded;
        if (type === 'mx') {
            rootSpaceNeeded = linuxfs_file_size + rootfs_file_size;
        }
        else {
            rootSpaceNeeded = linuxfs_file_size;
        }
        return rootSpaceNeeded / 1_073_741_824; // Converte in GB
    }
    // Se il metodo fa parte di una classe, usa `static`. Altrimenti, rimuovilo.
    static getOsRelease() {
        const osReleasePath = path.join('/etc', 'os-release');
        // Inizializza l'oggetto con valori predefiniti
        const osInfo = {
            ID: '',
            VERSION_CODENAME: 'n/a',
            VERSION_ID: ''
        };
        // Verifica se il file esiste
        if (!fs.existsSync(osReleasePath)) {
            console.error('/etc/os-release file does not exist.');
            return osInfo;
        }
        // Leggi il contenuto del file
        let fileContent;
        try {
            fileContent = fs.readFileSync(osReleasePath, 'utf8');
        }
        catch (error) {
            console.error('Error reading /etc/os-release:', error);
            return osInfo;
        }
        // Analizza ogni linea
        const lines = fileContent.split('\n');
        for (const line of lines) {
            if (line.startsWith('#') || line.trim() === '')
                continue;
            const [key, value] = line.split('=');
            if (key && value) {
                const trimmedKey = key.trim();
                const trimmedValue = value.trim().replaceAll('"', '');
                // Popola solo le chiavi desiderate
                switch (trimmedKey) {
                    case 'ID': {
                        osInfo.ID = trimmedValue;
                        break;
                    }
                    case 'VERSION_CODENAME': {
                        osInfo.VERSION_CODENAME = trimmedValue;
                        break;
                    }
                    case 'VERSION_ID': {
                        osInfo.VERSION_ID = trimmedValue;
                        break;
                    }
                    // No default
                }
            }
        }
        // capitalize distroId
        osInfo.ID = osInfo.ID[0].toUpperCase() + osInfo.ID.slice(1).toLowerCase();
        osInfo.VERSION_CODENAME = osInfo.VERSION_CODENAME.toLowerCase();
        return osInfo;
    }
    /**
     * return the name of the package: penguins-eggs
     * @returns penguins-eggs
     */
    static getPackageName() {
        return pjson.shortName;
    }
    /**
     * return the version of the package
     * @returns version example 8.0.0
     */
    static getPackageVersion() {
        return pjson.version;
    }
    /**
     * Return postfix
     * @param basename
     * @returns eggName
     */
    static getPostfix() {
        const postfix = '_' + Utils.formatDate(new Date());
        return postfix;
    }
    /**
     *
     * @param prefix
     * @param backup
     * @returns
     */
    static getPrefix(prefix, backup = false) {
        if (backup) {
            if (prefix.slice(0, 7) === 'egg-of_') {
                prefix = 'egg-bk_' + prefix.slice(7);
            }
            else {
                prefix = 'egg-bk_' + prefix;
            }
        }
        return prefix;
    }
    /**
     * Return the primary user's name
     */
    static async getPrimaryUser() {
        const { execSync } = require('child_process');
        let primaryUser = '';
        try {
            // Attempt to get the user from logname
            primaryUser = execSync('/usr/bin/logname 2>/dev/null', { encoding: 'utf-8' }).trim();
        }
        catch {
            // console.log("logname failed, so we continue with other methods")
        }
        if (primaryUser === 'root') {
            primaryUser = '';
        }
        if (primaryUser === '') {
            try {
                // Check if doas is installed and get the DOAS_USER
                execSync('command -v doas', { stdio: 'ignore' });
                primaryUser = execSync('echo $DOAS_USER', { encoding: 'utf-8' }).trim();
            }
            catch {
                // console.log("doas is not installed or DOAS_USER is not set, continue with the next method")
            }
        }
        if (primaryUser === '') {
            try {
                // Check for the SUDO_USER
                primaryUser = execSync('echo $SUDO_USER', { encoding: 'utf-8' }).trim();
            }
            catch {
                // console.log("SUDO_USER is not set, continue with the next method")
            }
        }
        if (primaryUser === '') {
            // console.log("Fallback to the USER environment variable")
            primaryUser = process.env.USER || '';
        }
        if (primaryUser === '') {
            primaryUser = 'dummy';
            // console.error('Cannot determine the primary user.');
            // process.exit(1);
        }
        return primaryUser;
    }
    /**
     * Count the eggs present in the nest
     * @returns {number} Numero degli snapshot presenti
     */
    static getSnapshotCount(snapshot_dir = '/') {
        if (fs.existsSync(snapshot_dir)) {
            const files = fs.readdirSync(snapshot_dir);
            let nIsos = 0;
            for (const f of files) {
                if (f.endsWith('.iso')) {
                    nIsos++;
                }
            }
            return nIsos;
        }
        return 0;
    }
    /**
     * Get the syze of the snapshot
     * @returns {string} grandezza dello snapshot in Byte
     */
    static getSnapshotSize(snapshot_dir = '/') {
        let fileSizeInBytes = 0;
        const size = shx.exec(`/usr/bin/find ${snapshot_dir} -maxdepth 1 -type f -name '*.iso' -exec du -sc {} + | tail -1 | awk '{print $1}'`, { silent: true }).stdout.trim();
        if (size === '') {
            fileSizeInBytes = 0;
        }
        else {
            fileSizeInBytes = Number(size);
        }
        return fileSizeInBytes;
    }
    /**
     * Calculate the space used on the disk
     * @returns {void}
     */
    static getUsedSpace() {
        let fileSizeInBytes = 0;
        if (this.isLive()) {
            fileSizeInBytes = 0; // this.getLiveRootSpace()
        }
        else {
            fileSizeInBytes = Number(shx.exec(`df /home | /usr/bin/awk 'NR==2 {print $3}'`, {
                silent: true
            }).stdout);
        }
        return fileSizeInBytes;
    }
    /**
     * return the name of network device
     */
    static async iface() {
        // return shx.exec(`ifconfig | awk 'FNR==1 { print $1 }' | tr --d :`, { silent: true }).stdout.trim()
        const interfaces = Object.keys(os.networkInterfaces());
        let netDeviceName = '';
        for (const k in interfaces) {
            if (interfaces[k] != 'lo') {
                netDeviceName = interfaces[k];
            }
        }
        return netDeviceName;
    }
    static info(msg = '') {
        console.log(pjson.shortName + ' >>> ' + chalk.white(msg));
    }
    /**
     * @deprecated Use Kernel.initramfs() instead
     */
    static initrdImg(kernel = '') {
        return Kernel.initramfs(kernel);
    }
    /**
     * isAppImage
     */
    static isAppImage() {
        return Boolean(process.env.APPIMAGE) ||
            process.execPath.includes('.AppImage') ||
            process.execPath.includes('/tmp/.mount_');
    }
    /**
     *
     * @param device
     * @returns
     */
    static isBlockDevice(device = '') {
        const cmd = `lsblk -d -o name | grep ${device}`;
        const result = shx.exec(cmd, { silent: true }).code;
        return result === 0;
    }
    /**
     * Detect if running inside a standard chroot environment
     * (Distinguishes chroot from Containers/Host)
     */
    static isChroot() {
        try {
            // 1. Check for Debian specific chroot file
            // Many Debian tools (debootstrap, schroot) create this file.
            if (fs.existsSync('/etc/debian_chroot')) {
                return true;
            }
            // 2. Inode comparison method
            // We compare the Inode and Device ID of '/' vs '/proc/1/root'.
            // In a chroot (sharing the host's PID namespace via bind-mounted /proc),
            // PID 1 refers to the host's init process.
            // Therefore, if '/' is NOT the same file as '/proc/1/root', we are in a chroot.
            const rootStat = fs.statSync('/');
            const procRootStat = fs.statSync('/proc/1/root');
            // If dev or ino are different, we are inside a chroot
            return (rootStat.dev !== procRootStat.dev) || (rootStat.ino !== procRootStat.ino);
        }
        catch {
            // If /proc is not mounted or not accessible, detection via inode fails.
            // However, usually in eggs /proc is mounted. 
            // If strictly needed, one could assume that if /proc/1/root is unreadable 
            // but / exists, it might be a weird chroot state, but returning false is safer.
            return false;
        }
    }
    /**
     * Detect if running inside a container (Docker or LXC)
     */
    static isContainer() {
        // Check for Docker's specific file
        if (fs.existsSync('/.dockerenv')) {
            return true;
        }
        // Check the cgroup file, which works for Docker, Podman, LXC, etc.
        try {
            const cgroupContent = fs.readFileSync('/proc/1/cgroup', 'utf8');
            return cgroupContent.includes('/docker/') || cgroupContent.includes('/kubepods/');
        }
        catch {
            return false;
        }
    }
    /**
     * Return true if i686 architecture
     * @remarks to move in Utils
     * @returns {boolean} true se l'architettura è i686
     */
    static isi686() {
        return process.arch === 'ia32';
    }
    /**
     * Return true if live system
     * @returns {boolean} isLive
     */
    static isLive() {
        let retVal = false;
        const paths = [
            '/lib/live/mount', // debian-live
            '/run/live/rootfs/filesystem.squashfs', // debian trixie
            '/lib/live/mount/rootfs/filesystem.squashfs', // ubuntu bionic
            '/live/aufs', // mx-linux
            '/media/root-rw', // AlpineLinux
            '/run/archiso/airootfs', // Arch
            '/run/miso/sfs/livefs', // Manjarolinux
            '/run/rootfsbase' // Fedora
        ];
        for (const path_ of paths) {
            if (Utils.isMountpoint(path_)) {
                retVal = true;
            }
        }
        return retVal;
    }
    /**
     * Ritorna vero se path è un mountpoint
     * @param path
     */
    static isMountpoint(path = '') {
        const cmd = `mountpoint -q ${path}`;
        // return 0 if the directory is a mountpoint, non-zero if not.
        const result = shx.exec(cmd, { silent: true }).code;
        return result === 0;
    }
    /**
     * Controlla se è un pacchetto npm
     */
    static isNpmPackage() {
        return !(this.isPackage() || this.isSources());
    }
    /**
     * Check if the system uses OpenRC
     */
    static isOpenRc() {
        let isOpenRc = false;
        if (!this.isContainer()) {
            isOpenRc = Utils.commandExists('openrc');
        }
        return isOpenRc;
    }
    /**
     * Controlla se è un pacchetto deb
     * /usr/lib/penguins-eggs/bin/node
     */
    static isPackage() {
        let ret = false;
        // if (process.execPath !== '/usr/bin/node') {
        if (process.execPath === '/usr/lib/penguins-eggs/bin/node') {
            ret = true;
        }
        return ret;
    }
    /**
     * return true if eggs run as root
     * @returns isRoot
     */
    static isRoot(command = '') {
        if (process.getuid && process.getuid() === 0) {
            return true;
        }
        return false;
    }
    /**
     * Controlla se è un pacchetto sorgente
     */
    static isSources() {
        let ret = false;
        if (__dirname.slice(0, 6) === '/home/') {
            ret = true;
        }
        return ret;
    }
    /**
     * Check if the system uses Systemd
     */
    static isSystemd() {
        let isSystemd = false;
        if (this.isContainer()) {
            isSystemd = true;
            const distro = new Distro();
            if (distro.distroId === "Devuan") {
                isSystemd = false;
            }
        }
        else {
            isSystemd = fs.readFileSync("/proc/1/comm").includes('systemd');
        }
        return isSystemd;
    }
    /**
     * Check if the system uses SysVinit
     */
    static isSysvinit() {
        let isSysvinit = false;
        if (this.isContainer()) {
            const distro = new Distro();
            if (distro.distroId === "Devuan") {
                isSysvinit = true;
            }
        }
        else {
            isSysvinit = fs.readFileSync("/proc/1/comm").includes('init');
        }
        return isSysvinit;
    }
    /**
     * get the kernel version
     */
    static kernelVersion() {
        return os.release();
    }
    /**
     * Usata da pacman e config credo non serva affatto
     */
    static machineId() {
        let result = '';
        if (fs.existsSync('/etc/machine-id')) {
            result = fs.readFileSync('/etc/machine-id', 'utf8').trim();
        }
        else if (fs.existsSync('/var/lib/dbus/machine-id')) {
            result = fs.readFileSync('/var/lib/dbus/machine-id', 'utf8').trim();
        }
        return result;
    }
    /**
     * netmask
     */
    static netmask() {
        const interfaces = os.networkInterfaces();
        let netmask = '';
        if (interfaces !== undefined) {
            for (const devName in interfaces) {
                const iface = interfaces[devName];
                if (iface !== undefined) {
                    for (const alias of iface) {
                        if (alias.family === 'IPv4' &&
                            alias.address !== '127.0.0.1' &&
                            !alias.internal
                            && // take just the first!
                                netmask === '') {
                            netmask = alias.netmask;
                        }
                    }
                }
            }
        }
        return netmask;
    }
    /**
     *
     */
    static async pressKeyToExit(warning = 'Process will end', procContinue = true) {
        Utils.warning(warning);
        let msg = 'Press a key to exit...';
        if (procContinue) {
            msg = 'Press a key to continue...';
        }
        console.log(msg);
        const pressKeyToExit = spawnSync('read _ ', [], { shell: true, stdio: [0, 1, 2] });
        if (!procContinue) {
            process.exit(0);
        }
    }
    /**
     *
     */
    static rootPenguin() {
        return path.resolve(__dirname, '../../');
    }
    /**
     *
     * @param file
     * @param search
     * @returns value
     */
    static searchOnFile(file = '', search = '') {
        const lines = fs.readFileSync(file, 'utf8').split("\n");
        let value = '';
        for (let line of lines) {
            line = line.replaceAll(/\s+/g, ' '); // Remove multiple spaces with single space
            if (line.includes(search)) {
                value = line.slice(Math.max(0, line.indexOf('=') + 1));
            }
        }
        value = value.replaceAll('"', ''); // Remove "
        return value.trim();
    }
    /**
     *
     * @param verbose
     */
    static setEcho(verbose = false) {
        let echo = { echo: false, ignore: true };
        if (verbose) {
            echo = { echo: true, ignore: false };
        }
        return echo;
    }
    /* Funzione helper che crea una pausa non bloccante.
    * @param ms Millisecondi da attendere
    */
    static sleep(ms = 60_000) {
        // console.log('wait...')
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    /**
     * Restituisce il prefisso della iso
     * @param distroId
     * @param codenameId
     */
    static snapshotPrefix(distroId, codenameId) {
        let result = `egg-of_${distroId.toLowerCase()}-`;
        if (codenameId === 'rolling' || codenameId === '') {
            const releaseId = Utils.getOsRelease().VERSION_ID.trim();
            if (releaseId !== '') {
                result += releaseId;
            }
        }
        else {
            result += `${codenameId.toLowerCase()}`;
        }
        if (!result.endsWith('-')) {
            result += '-';
        }
        return result;
    }
    /**
     * Custom function to sort object keys
     * @param obj
     * @returns
     */
    static sortObjectKeys(obj) {
        const sorted = {};
        for (const key of Object.keys(obj)
            .sort()) {
            sorted[key] = obj[key];
        }
        return sorted;
    }
    static success(msg = '') {
        console.log(pjson.shortName + ' >>> ' + chalk.greenBright(msg));
    }
    /**
     * titles
     * Penguin's are gettings alive!
     */
    static titles(command = '') {
        console.clear();
        console.log('');
        console.log(' E G G S: the reproductive system of penguins');
        console.log('');
        console.log(Utils.flag());
        console.log('command: ' + chalk.bgBlack.white(command) + '\n');
    }
    /**
     * uefiArch
     * @returns arch
     */
    static uefiArch() {
        let arch = '';
        switch (process.arch) {
            case 'arm64': {
                arch = 'arm64';
                break;
            }
            case 'ia32': {
                arch = 'i386';
                // 
                if (shx.exec('uname -m', { silent: true }).stdout.trim() === 'x86_64') {
                    arch = 'amd64';
                }
                break;
            }
            case 'riscv64': {
                arch = 'riscv64';
                break;
            }
            case 'x64': {
                arch = 'amd64';
                break;
            }
            // No default
        }
        return arch;
    }
    /**
     * i386-pc,
     * i386-efi,
     * x86_64-efi,
     * arm64-efi,
     *
     * ATTEMZIONE: install efibootmgr
     *
     * Fedora/RHEL have i386-pc
     */
    static uefiFormat() {
        let format = '';
        switch (process.arch) {
            case 'arm64': {
                format = 'arm64-efi';
                break;
            }
            case 'ia32': {
                format = 'i386-efi';
                if (shx.exec('uname -m', { silent: true }).stdout.trim() === 'x86_64') {
                    format = 'x86_64-efi';
                }
                break;
            }
            case 'riscv64': {
                format = 'riscv64-efi';
                break;
            }
            case 'x64': {
                format = 'x86_64-efi';
                break;
            }
            // No default
        }
        return format;
    }
    /**
     *
     * @param command
     */
    static useRoot(command = '') {
        Utils.titles(pjson.shortName + ' ' + command + ` need to run with root privileges. Please, prefix it with sudo`);
    }
    /**
     *
     * @returns
     */
    static usrLibPath() {
        let path = '';
        if (process.arch === 'x64') {
            path = 'x86_64-linux-gnu';
        }
        else if (process.arch === 'arm64') {
            path = 'aarch64-linux-gnu';
        }
        return path;
    }
    /**
     * restituisce uuid
     * @param device
     */
    static uuid(device) {
        const uuid = shx.exec(`blkid -p -s UUID -o value ${device}`, { silent: true }).stdout.trim();
        return uuid;
    }
    /**
     *
     * @param device
     * @returns
     */
    static uuidGen() {
        const uuid = shx.exec(`uuidgen`, { silent: true }).stdout.trim();
        return uuid;
    }
    static vmlinuz(kernel = '') {
        return Kernel.vmlinuz(kernel);
    }
    /**
     *
     * @param volid
     */
    static VolidTrim(volid = 'unknown') {
        // // 28 +  4 .iso = 32 lunghezza max di volid
        if (volid.length >= 32) {
            volid = volid.slice(0, 32);
        }
        return volid;
    }
    /**
     *
     * @returns wardrobe
     */
    static async wardrobe() {
        let wardrobe = `${os.homedir()}/.wardrobe`;
        if (Utils.isRoot()) {
            wardrobe = `/home/${await Utils.getPrimaryUser()}/.wardrobe`;
        }
        return wardrobe;
    }
    /**
     *
     * @param msg
     */
    static warning(msg = '') {
        console.log(pjson.shortName + ' >>> ' + chalk.cyanBright(msg));
    }
    /**
     * write a file
     * @param file
     * @param text
     */
    static write(file, text) {
        text = text.trim() + '\n';
        file = file.trim();
        fs.writeFileSync(file, text);
    }
    /**
     *
     * @param file
     * @param cmd
     */
    static writeX(file, cmd) {
        let text = `#!/bin/sh\n\n`;
        text += `# Created at: ${Utils.formatDate(new Date())}\n`;
        text += `# By: penguins_eggs v. ${Utils.getPackageVersion()}\n`;
        text += `# ==> Perri\'s Brewery edition <== \n\n`;
        text += cmd;
        Utils.write(file, text);
        shx.chmod('+x', file);
    }
    /**
     *
     * @param file
     * @param cmd
     */
    static writeXs(file, cmds) {
        let cmd = '';
        for (const elem of cmds) {
            cmd += elem + '\n';
        }
        Utils.writeX(file, cmd);
    }
}
