/**
 * ./src/lib/utils.ts
 * penguins-eggs v.25.7.x / ecmascript 2020
 * author: Piero Proietti
 * email: piero.proietti@gmail.com
 * license: MIT
 */
// Importiamo con alias per poter wrappare
import { spawn as nodeSpawn, spawnSync as nodeSpawnSync } from 'child_process';
import fs from 'fs';
import path from 'path';
/**
 * Pulizia AppImage
 * Variabili d'ambiente da rimuovere per evitare conflitti quando si eseguono
 * comandi di sistema dall'interno di una AppImage.
 */
const APPIMAGE_ENV_BLACKLIST = ['LD_LIBRARY_PATH', 'LD_PRELOAD', 'PYTHONPATH', 'PERLLIB', 'GSETTINGS_SCHEMA_DIR', 'QT_PLUGIN_PATH', 'XDG_DATA_DIRS', 'LIBRARY_PATH', 'PKG_CONFIG_PATH', 'GIO_MODULE_DIR', 'APPIMAGE', 'APPDIR'];
/**
 * Ottiene un ambiente pulito dalle variabili AppImage
 * @returns Oggetto process.env sanificato
 */
function getCleanEnv() {
    const env = { ...process.env };
    if (process.env.APPIMAGE) {
        for (const key of APPIMAGE_ENV_BLACKLIST)
            delete env[key];
    }
    return env;
}
/**
 * spawnSync (WRAPPER INTELLIGENTE)
 * Supporta:
 * 1. (command, args, options)
 * 2. (command, options) -> args diventa []
 * Pulisce automaticamente l'ambiente.
 */
export function spawnSync(command, arg2, arg3) {
    let args = [];
    let options = {};
    // Rilevamento argomenti (Polimorfismo)
    if (Array.isArray(arg2)) {
        args = arg2;
        options = arg3 || {};
    }
    else if (arg2 && typeof arg2 === 'object') {
        // TypeScript fix: cast esplicito per evitare errori di tipo union
        options = arg2;
    }
    const env = getCleanEnv();
    const finalEnv = { ...env, ...options.env };
    return nodeSpawnSync(command, args, {
        ...options,
        env: finalEnv
    });
}
/**
 * spawn (WRAPPER INTELLIGENTE)
 * Supporta:
 * 1. (command, args, options)
 * 2. (command, options) -> args diventa []
 * Pulisce automaticamente l'ambiente.
 */
export function spawn(command, arg2, arg3) {
    let args = [];
    let options = {};
    // Rilevamento argomenti (Polimorfismo)
    if (Array.isArray(arg2)) {
        args = arg2;
        options = arg3 || {};
    }
    else if (arg2 && typeof arg2 === 'object') {
        // TypeScript fix: cast esplicito
        options = arg2;
    }
    const env = getCleanEnv();
    const finalEnv = { ...env, ...options.env };
    return nodeSpawn(command, args, {
        ...options,
        env: finalEnv
    });
}
/**
 * shx
 * Sostituto drop-in per shelljs che usa API native e ambiente pulito.
 */
export const shx = {
    chmod(mode, file) {
        if (!fs.existsSync(file))
            return;
        let finalMode = mode;
        if (mode === '+x')
            finalMode = 0o755;
        if (typeof mode === 'string' && !isNaN(Number.parseInt(mode, 8))) {
            finalMode = Number.parseInt(mode, 8);
        }
        fs.chmodSync(file, finalMode);
    },
    cp(arg1, arg2, arg3) {
        const src = arg3 ? arg2 : arg1;
        const dest = arg3 ? arg3 : arg2;
        // --- GESTIONE WILDCARD (*) ---
        if (src.endsWith('*')) {
            const srcDir = path.dirname(src);
            if (!fs.existsSync(srcDir))
                return;
            if (!fs.existsSync(dest))
                fs.mkdirSync(dest, { recursive: true });
            const items = fs.readdirSync(srcDir);
            for (const item of items) {
                const s = path.join(srcDir, item);
                const d = path.join(dest, item);
                fs.cpSync(s, d, { force: true, recursive: true });
            }
            return;
        }
        // ----------------------------
        let finalDest = dest;
        if (fs.existsSync(dest) && fs.statSync(dest).isDirectory()) {
            finalDest = path.join(dest, path.basename(src));
        }
        if (fs.existsSync(src)) {
            fs.cpSync(src, finalDest, { force: true, recursive: true });
        }
    },
    exec(command, options = {}) {
        const env = getCleanEnv();
        const spawnOpts = {
            encoding: 'utf-8',
            env,
            shell: '/bin/bash',
            stdio: options.silent ? 'pipe' : 'inherit'
        };
        // Usiamo nodeSpawnSync perché calcoliamo l'env qui sopra
        const result = nodeSpawnSync(command, [], spawnOpts);
        return {
            code: result.status ?? 1,
            stderr: result.stderr ? result.stderr.toString() : '',
            stdout: result.stdout ? result.stdout.toString() : ''
        };
    },
    ln(flag, target, link) {
        if (fs.existsSync(link) || fs.lstatSync(link, { throwIfNoEntry: false })) {
            fs.rmSync(link, { force: true });
        }
        fs.symlinkSync(target, link);
    },
    ls(arg1, arg2) {
        let options = '';
        let paths = [];
        // Rilevamento argomenti: ls('-R', path) vs ls(path)
        if (typeof arg1 === 'string' && arg1.startsWith('-')) {
            options = arg1;
            // Se c'è arg2 lo usa, altrimenti default a '.'
            paths = arg2 ? (Array.isArray(arg2) ? arg2 : [arg2]) : ['.'];
        }
        else {
            // Nessuna opzione, arg1 sono i path. Se null, default a '.'
            paths = arg1 ? (Array.isArray(arg1) ? arg1 : [arg1]) : ['.'];
        }
        const recursive = options.includes('R');
        let results = [];
        for (const p of paths) {
            if (!fs.existsSync(p))
                continue;
            const stat = fs.statSync(p);
            if (stat.isDirectory()) {
                if (recursive) {
                    // Funzione ricorsiva interna
                    const walk = (dir) => {
                        const files = fs.readdirSync(dir);
                        for (const f of files) {
                            const fullPath = path.join(dir, f);
                            results.push(fullPath); // Aggiunge il path completo
                            if (fs.statSync(fullPath).isDirectory()) {
                                walk(fullPath);
                            }
                        }
                    };
                    walk(p);
                }
                else {
                    // Comportamento standard: ritorna solo i nomi dei file nella cartella
                    results = results.concat(fs.readdirSync(p));
                }
            }
            else {
                // È un file singolo
                results.push(p);
            }
        }
        return results;
    },
    mkdir(arg1, arg2) {
        const dir = arg2 ? arg2 : arg1;
        fs.mkdirSync(dir, { recursive: true });
    },
    mv(src, dest) {
        if (!fs.existsSync(src))
            return;
        fs.renameSync(src, dest);
    },
    rm(arg1, arg2) {
        const target = arg2 ? arg2 : arg1;
        fs.rmSync(target, { force: true, recursive: true });
    },
    sed(flag, regex, replacement, file) {
        if (!fs.existsSync(file))
            return;
        const content = fs.readFileSync(file, 'utf8');
        const searchRegex = typeof regex === 'string' ? new RegExp(regex, 'g') : regex;
        const newContent = content.replace(searchRegex, replacement);
        fs.writeFileSync(file, newContent, 'utf8');
    },
    test(flag, pathToCheck) {
        try {
            const stats = fs.statSync(pathToCheck);
            if (flag === '-f')
                return stats.isFile();
            if (flag === '-d')
                return stats.isDirectory();
            return true; // -e
        }
        catch {
            return false;
        }
    },
    touch(file) {
        const time = new Date();
        try {
            fs.utimesSync(file, time, time);
        }
        catch {
            fs.closeSync(fs.openSync(file, 'w'));
        }
    },
    which(cmd) {
        const result = shx.exec(`command -v ${cmd}`, { silent: true });
        return result.code === 0 ? result.stdout.trim() : null;
    }
};
/**
 * execSync
 */
export function execSync(command, options = {}) {
    const { echo = false, ignore = false, stdio } = options;
    if (echo)
        console.log(command);
    const isSilent = ignore || stdio === 'ignore';
    const result = shx.exec(command, { silent: isSilent });
    if (result.code !== 0) {
        throw new Error(`Command failed: ${command}\nExit Code: ${result.code}\nStderr: ${result.stderr}`);
    }
    return result.stdout.trim();
}
/**
 * exec (Async)
 */
export async function exec(command, { capture = false, echo = false, ignore = false } = {}) {
    return new Promise((resolve, reject) => {
        if (echo)
            console.log(command);
        const env = getCleanEnv();
        // Usiamo nodeSpawn direttamente qui per coerenza
        const child = nodeSpawn(command, [], {
            env,
            shell: '/bin/bash',
            stdio: ignore ? 'ignore' : capture ? 'pipe' : 'inherit'
        });
        let stdout = '';
        let stderr = '';
        if (capture && child.stdout)
            child.stdout.on('data', (d) => (stdout += d.toString()));
        if (capture && child.stderr)
            child.stderr.on('data', (d) => (stderr += d.toString()));
        child.on('error', (error) => {
            reject({ code: 1, error, stderr });
        });
        child.on('close', (code) => {
            resolve({
                code: code || 0,
                data: stdout.trim(),
                error: code === 0 ? undefined : stderr.trim()
            });
        });
    });
}
