/**
 * ./src/classes/ovary.d/make-efi.ts
 * penguins-eggs v.25.7.x / ecmascript 2020
 * author: Piero Proietti
 * email: piero.proietti@gmail.com
 * license: MIT
 */
import mustache from 'mustache';
// packages
import fs from 'node:fs';
import path from 'node:path';
// classes
import { exec } from '../../lib/utils.js';
import Diversions from '../diversions.js';
import Utils from '../utils.js';
// _dirname
const __dirname = path.dirname(new URL(import.meta.url).pathname);
/**
 * @param this
 * @param theme
 */
export async function makeEfi(theme = 'eggs') {
    const bootloaders = Diversions.bootloaders(this.familyId);
    /**
     * Define default paths based on arch
     */
    let signed = false;
    let grubEfi = '';
    let shimEfi = '';
    // Default paths based on architecture
    switch (process.arch) {
        case 'arm64': {
            grubEfi = path.resolve(bootloaders, `grub/arm64-efi/monolithic/grubaa64.efi`);
            shimEfi = path.resolve(bootloaders, `shim/shimaa64.efi`);
            break;
        }
        case 'ia32': {
            grubEfi = path.resolve(bootloaders, `grub/i386-efi/monolithic/grubia32.efi`);
            shimEfi = path.resolve(bootloaders, `shim/shimia32.efi`); // raramente usato non firmato
            break;
        }
        case 'riscv64': {
            // Percorso per RISC-V (assumendo struttura simile)
            // Nota: Assicurati che Diversions.bootloaders punti al posto giusto o che i file esistano lì
            grubEfi = path.resolve(bootloaders, `grub/riscv64-efi/monolithic/grubriscv64.efi`);
            shimEfi = ''; // Solitamente niente SHIM su RISC-V per ora
            break;
        }
        case 'x64': {
            grubEfi = path.resolve(bootloaders, `grub/x86_64-efi/monolithic/grubx64.efi`);
            shimEfi = path.resolve(bootloaders, `shim/shimx64.efi`);
            break;
        }
        // No default
    }
    /**
     * Gestione Secure Boot (Debian/Ubuntu/Devuan)
     */
    if (this.familyId === 'debian') {
        signed = true;
        switch (process.arch) {
            case 'arm64': {
                grubEfi = path.resolve(bootloaders, `grub/arm64-efi-signed/grubaa64.efi.signed`);
                shimEfi = path.resolve(bootloaders, `shim/shimaa64.efi.signed`);
                break;
            }
            case 'ia32': {
                grubEfi = path.resolve(bootloaders, `grub/i386-efi-signed/grubia32.efi.signed`);
                shimEfi = path.resolve(bootloaders, `shim/shimia32.efi.signed`);
                break;
            }
            case 'riscv64': {
                // Per ora niente firma su RISC-V Debian, fallback su unsigned
                signed = false;
                grubEfi = path.resolve(bootloaders, `grub/riscv64-efi/monolithic/grubriscv64.efi`);
                shimEfi = '';
                break;
            }
            case 'x64': {
                grubEfi = path.resolve(bootloaders, `grub/x86_64-efi-signed/grubx64.efi.signed`);
                shimEfi = path.resolve(bootloaders, `shim/shimx64.efi.signed`);
                break;
            }
            // No default
        }
    }
    // Safety check se i file non esistono (es. grub riscv non trovato)
    if (!fs.existsSync(grubEfi) && process.arch === 'riscv64') {
        // Fallback tentativo path alternativo o warning
        Utils.warning(`Warning: ${grubEfi} not found. Checking alternate paths...`);
    }
    if (signed) {
        Utils.warning(`Your live system ${this.distroId}/${process.arch} can boot with Secure Boot enabled`);
    }
    else {
        Utils.warning(`You must disable Secure Boot to boot live system ${this.distroId}/${process.arch}`);
    }
    // 2 secondi per leggere...
    await new Promise((resolve) => setTimeout(resolve, 2000));
    const efiPath = this.settings.efi_work;
    const efiWorkDir = path.join(efiPath, '/work/');
    const efiMemdiskDir = path.join(efiPath, '/memdisk/');
    const efiImgMnt = path.join(efiPath, 'mnt');
    const isoDir = this.settings.iso_work;
    // create (ISO)/boot/grub
    await exec(`mkdir ${path.join(isoDir, `/boot/grub/${Utils.uefiFormat()}`)} -p`, this.echo);
    // create (ISO)/EFI
    await exec(`mkdir ${isoDir}/EFI/boot -p`, this.echo);
    // Copy EFI binaries to ISO root
    if (shimEfi && fs.existsSync(shimEfi)) {
        await exec(`cp ${shimEfi} ${isoDir}/EFI/boot/${bootEfiName()}`, this.echo);
    }
    // Se non c'è shim (es. RISC-V), copiamo grub direttamente come boot<arch>.efi?
    // Solitamente se c'è shim: bootx64.efi = shim, grubx64.efi = grub
    // Se NO shim: bootx64.efi = grub
    if (!shimEfi || !fs.existsSync(shimEfi)) {
        await exec(`cp ${grubEfi} ${isoDir}/EFI/boot/${bootEfiName()}`, this.echo);
    }
    else {
        await exec(`cp ${grubEfi} ${isoDir}/EFI/boot/${grubEfiName()}`, this.echo);
    }
    // clean/create all in efiPath
    if (fs.existsSync(efiPath)) {
        await exec(`rm -rf ${efiPath}`);
    }
    await exec(`mkdir ${efiPath}`, this.echo);
    await exec(`mkdir ${efiMemdiskDir}`, this.echo);
    await exec(`mkdir ${efiImgMnt}`, this.echo);
    await exec(`mkdir ${efiWorkDir}`, this.echo);
    /**
     * create efi.img logic
     */
    // Seeker GRUB config: cerca il file .disk/id/UUID
    Utils.warning(`UUID used for boot search: ${this.uuid}`);
    let seeker = '';
    seeker += `search --file --set=root /.disk/id/${this.uuid}\n`;
    seeker += 'set prefix=($root)/boot/grub\n';
    seeker += 'source $prefix/${grub_cpu}-efi/grub.cfg\n';
    // Fallback generico se la source fallisce (utile per debug)
    seeker += 'configfile ($root)/boot/grub/grub.cfg\n';
    /**
     * creating grub.cfg (1) seeker for usb on (efi.img)/boot/grub/grub.cfg
     */
    Utils.warning('creating grub.cfg seeker USB on (efi.img)/boot/grub');
    await exec(`mkdir ${path.join(efiMemdiskDir, '/boot/grub')} -p`, this.echo);
    const cfgSeekerUsb = `${efiMemdiskDir}/boot/grub/grub.cfg`;
    let cfgSeekerUsbText = '';
    cfgSeekerUsbText += `# grub.cfg seeker\n`;
    cfgSeekerUsbText += `# created on ${efiMemdiskDir}, path ${cfgSeekerUsb}\n`;
    cfgSeekerUsbText += `\n`;
    cfgSeekerUsbText += seeker;
    Utils.write(cfgSeekerUsb, cfgSeekerUsbText);
    /**
     * create grub.cfg (bridge) on (ISO)/boot/grub/x86_64-efi/grub.cfg
     */
    Utils.warning(`creating grub.cfg bridge to main. (ISO)/boot/grub/${Utils.uefiFormat()}`);
    const cfgBridge = path.join(isoDir, '/boot/grub/', Utils.uefiFormat(), '/grub.cfg');
    let cfgBridgeText = `# grub.cfg bridge\n`;
    if (!this.hidden) {
        cfgBridgeText += `# created on ${cfgBridge}\n`;
    }
    cfgBridgeText += `\n`;
    // Qui è dove l'architettura specifica punta al grub generico
    cfgBridgeText += `source /boot/grub/grub.cfg\n`;
    fs.writeFileSync(cfgBridge, cfgBridgeText);
    /**
     * grub bait
     */
    let pathBait = path.join(isoDir, '/EFI/debian');
    if (this.distroLike === 'Ubuntu') {
        pathBait = path.join(isoDir, '/EFI/ubuntu');
    }
    await exec(`mkdir ${pathBait} -p`, this.echo);
    Utils.warning(`creating grub.cfg seeker ISO/DVD on (ISO)/EFI/${path.basename(pathBait)}`);
    const cfgBait = path.join(pathBait, '/grub.cfg');
    let cfgBaitText = '';
    cfgBaitText += `\n`;
    cfgBaitText += seeker;
    Utils.write(cfgBait, cfgBaitText);
    /**
     * creating grub.cfg configuration on (ISO)/EFI/boot/grub.cfg
     * This ensures fallback bootloader finds the config being in the same dir
     */
    const cfgEfiBoot = path.join(isoDir, 'EFI/boot/grub.cfg');
    Utils.write(cfgEfiBoot, cfgBaitText);
    /**
     * README.md
     */
    const baitReadme = path.join(pathBait, '/README.md');
    let baitReadmeText = ``;
    if (this.distroLike !== 'Debian' && this.distroLike !== 'Ubuntu') {
        baitReadmeText += `# penguins-eggs\n`;
        baitReadmeText += '\n';
        baitReadmeText += `This is just an hack, to let ${this.distroId} boot using Debian trixie bootloaders\n`;
        fs.writeFileSync(baitReadme, baitReadmeText);
    }
    /**
     * creating structure efiWorkDir
     */
    await exec(`mkdir -p ${efiWorkDir}/boot/grub`, this.echo); // qua va grub.cfg 2
    await exec(`mkdir -p ${efiWorkDir}/EFI/boot`);
    /**
     * create tarred efiMemdiskDir (Legacy/Memdisk method)
     */
    const currentDir = process.cwd();
    process.chdir(efiMemdiskDir);
    await exec('tar -cvf memdisk boot', this.echo);
    process.chdir(currentDir);
    /**
     * Create boot image "boot/grub/efi.img"
     */
    const efiImg = path.join(efiWorkDir, `boot/grub/efi.img`);
    // Aumentato leggermente il size per sicurezza
    if ((await exec(`dd if=/dev/zero of=${efiImg} bs=1M count=16`, this.echo)).code !== 0) {
        Utils.error(`Error creating ${efiImg}`);
        process.exit(1);
    }
    if ((await exec(`/sbin/mkdosfs -F 16 ${efiImg}`, this.echo)).code !== 0) {
        Utils.error(`Error formatting ${efiImg}`);
        process.exit(1);
    }
    await new Promise((resolve) => setTimeout(resolve, 500));
    // Use mtools to populate efi.img without mounting
    // 1. Create directories
    if ((await exec(`mmd -i ${efiImg} ::/EFI`, this.echo)).code !== 0) {
        Utils.error(`Error creating ::/EFI on ${efiImg}`);
        process.exit(1);
    }
    await exec(`mmd -i ${efiImg} ::/EFI/boot`, this.echo);
    await exec(`mmd -i ${efiImg} ::/boot`, this.echo);
    await exec(`mmd -i ${efiImg} ::/boot/grub`, this.echo);
    // 2. Copy grub.cfg to /boot/grub/grub.cfg
    if ((await exec(`mcopy -i ${efiImg} ${cfgSeekerUsb} ::/boot/grub/grub.cfg`, this.echo)).code !== 0) {
        Utils.error(`Error copying grub.cfg to ${efiImg}`);
        process.exit(1);
    }
    // 3. Copy EFI binaries
    if (shimEfi && fs.existsSync(shimEfi)) {
        await exec(`mcopy -i ${efiImg} ${shimEfi} ::/EFI/boot/${bootEfiName()}`, this.echo);
        await exec(`mcopy -i ${efiImg} ${grubEfi} ::/EFI/boot/${grubEfiName()}`, this.echo);
    }
    else {
        // Se no shim (RISC-V), grub diventa il boot loader principale
        if ((await exec(`mcopy -i ${efiImg} ${grubEfi} ::/EFI/boot/${bootEfiName()}`, this.echo)).code !== 0) {
            Utils.error(`Error copying ${grubEfi} to ${efiImg}`);
            process.exit(1);
        }
    }
    // 4. FIX CRUCIALE PER RISC-V (e compatibilità x86):
    // Copia il grub.cfg seeker ANCHE accanto al binario EFI in /EFI/boot/
    if (process.arch === 'riscv64') {
        await exec(`mcopy -i ${efiImg} ${cfgSeekerUsb} ::/EFI/boot/grub.cfg`, this.echo);
    }
    // Copy isoImg in ${${isoDir}/boot/grub
    Utils.warning('copyng (efi.img) on (ISO)/boot/grub');
    await exec(`cp ${efiImg} ${isoDir}/boot/grub`, this.echo);
    /**
     * creating grub.cfg (4) on (ISO)/boot/grub
     */
    Utils.warning('creating grub.cfg main on (ISO)/boot/grub');
    // splash.png
    let splashSrc = '';
    const splashDest = `${efiWorkDir}/boot/grub/splash.png`;
    let themeSrc = '';
    const themeDest = `${isoDir}/boot/grub/theme.cfg`;
    let grubTemplate = '';
    if (this.hidden) {
        splashSrc = path.resolve(__dirname, `../../../addons/${theme}/theme/livecd/generic-splash.png`);
        grubTemplate = path.resolve(__dirname, '../../../addons/eggs/theme/livecd/generic.grub.main.cfg');
        themeSrc = path.resolve(__dirname, '../../../addons/eggs/theme/livecd/generic.grub.theme.cfg');
    }
    else {
        // ... (resto della logica temi invariata)
        splashSrc = path.resolve(__dirname, `../../../addons/${theme}/theme/livecd/splash.png`);
        if (this.theme.includes('/')) {
            splashSrc = `${theme}/theme/livecd/splash.png`;
        }
        if (!fs.existsSync(splashSrc)) {
            Utils.warning(`warning: ${splashSrc} does not exists`);
            process.exit(1);
        }
        // select themeSrc
        themeSrc = path.resolve(__dirname, `../../../addons/${theme}/theme/livecd/grub.theme.cfg`);
        if (this.theme.includes('/')) {
            themeSrc = `${theme}/theme/livecd/grub.theme.cfg`;
        }
        if (!fs.existsSync(themeSrc)) {
            Utils.error(`error: ${themeSrc} does not exist`);
            process.exit(1);
        }
        // fonts... (invariato)
        if (fs.existsSync('/usr/share/grub/font.pf2')) {
            await exec(`cp /usr/share/grub/font.pf2 ${efiWorkDir}boot/grub/font.pf2`, this.echo);
        }
        else if (fs.existsSync('/usr/share/grub/unicode.pf2')) {
            await exec(`cp /usr/share/grub/unicode.pf2 ${efiWorkDir}boot/grub/font.pf2`, this.echo);
        }
        else if (fs.existsSync('/usr/share/grub/ascii.pf2')) {
            await exec(`cp /usr/share/grub/ascii.pf2 ${efiWorkDir}boot/grub/font.pf2`, this.echo);
        }
        // Copy workdir files to ISO/boot
        await exec(`rsync -avx  ${efiWorkDir}/boot ${isoDir}/`, this.echo);
        /**
         * prepare main grub.cfg from grub.main.cfg
         */
        grubTemplate = `${theme}/theme/livecd/grub.main.cfg`;
        if (!fs.existsSync(grubTemplate)) {
            grubTemplate = path.resolve(__dirname, '../../../addons/eggs/theme/livecd/grub.main.cfg');
        }
        if (!fs.existsSync(grubTemplate)) {
            Utils.error(`error: ${grubTemplate} does not exist`);
            process.exit(1);
        }
    }
    // splash.png
    await exec(`cp ${splashSrc} ${splashDest}`, this.echo);
    // grub.theme.png
    fs.copyFileSync(themeSrc, themeDest);
    // grub.main.png
    const kernel_parameters = Diversions.kernelParameters(this.familyId, this.volid, this.fullcrypt);
    const cfgMain = path.join(isoDir, '/boot/grub/grub.cfg');
    const template = fs.readFileSync(grubTemplate, 'utf8');
    let fullname = this.settings.remix.fullname.toUpperCase();
    if (this.hidden) {
        fullname = 'LINUX';
    }
    /**
     * SMART KERNEL DETECTION FOR RISC-V
     * Invece di forzare ciecamante 'vmlinux', controlliamo cosa c'è davvero
     * nella cartella /live della ISO.
     * Questo supporta sia Debian (spesso vmlinux) che Ubuntu (vmlinuz).
     */
    let kernelName = path.basename(this.vmlinuz); // Nome originale rilevato dal sistema
    if (process.arch === 'riscv64') {
        const liveDir = path.join(isoDir, 'live');
        // Generiamo le due possibili varianti del nome
        const nameX = kernelName.replace('vmlinuz', 'vmlinux'); // variante uncompressed
        const nameZ = kernelName.replace('vmlinux', 'vmlinuz'); // variante compressed
        // Controllo esistenza fisica
        if (fs.existsSync(path.join(liveDir, nameX))) {
            kernelName = nameX; // Abbiamo trovato vmlinux (Debian style)
        }
        else if (fs.existsSync(path.join(liveDir, nameZ))) {
            kernelName = nameZ; // Abbiamo trovato vmlinuz (Ubuntu style)
        }
        // Se non trova nulla, mantiene l'originale kernelName come fallback
    }
    const devicetree = '';
    const view = {
        devicetree,
        fullname,
        initrdImg: `/live/${path.basename(this.initrd)}`,
        kernel: this.kernel,
        kernel_parameters,
        vmlinuz: `/live/${kernelName}` // Usiamo il nome verificato
    };
    let cfgMainText = '';
    cfgMainText += `# grub.cfg (4) main\n`;
    if (!this.hidden) {
        cfgMainText += `# created on ${cfgMain}`;
    }
    cfgMainText += `\n`;
    cfgMainText += mustache.render(template, view);
    fs.writeFileSync(cfgMain, cfgMainText);
}
/**
 * FUNCTIONS
 */
/**
 * @returns
 */
function bootEfiName() {
    let ben = '';
    switch (process.arch) {
        case 'arm64': {
            ben = 'bootaa64.efi';
            break;
        }
        case 'ia32': {
            ben = 'bootia32.efi';
            break;
        }
        case 'riscv64': {
            ben = 'bootriscv64.efi';
            break;
        }
        case 'x64': {
            ben = 'bootx64.efi';
            break;
        }
        // No default
    }
    return ben;
}
/**
 * @returns
 */
function grubEfiName() {
    let gen = '';
    switch (process.arch) {
        case 'arm64': {
            gen = 'grubaa64.efi';
            break;
        }
        case 'ia32': {
            gen = 'grubia32.efi'; // c'era uno spazio typo nel tuo codice originale "grub ia32", corretto qui
            break;
        }
        case 'riscv64': {
            gen = 'grubriscv64.efi';
            break;
        }
        case 'x64': {
            gen = 'grubx64.efi';
            break;
        }
        // No default
    }
    return gen;
}
