/**
 * ./src/classes/incubation/fisherman.ts
 * penguins-eggs v.25.7.x / ecmascript 2020
 * author: Piero Proietti
 * email: piero.proietti@gmail.com
 * license: MIT
 */
import chalk from 'chalk';
import * as yaml from 'js-yaml';
import mustache from 'mustache';
import fs from 'node:fs';
import path from 'node:path';
import { exec, shx } from '../../lib/utils.js';
import { settings } from './fisherman-helper/settings.js';
// _dirname
const __dirname = path.dirname(new URL(import.meta.url).pathname);
// pjson
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const pjson = require('../../../package.json');
import { remove as removePackages, tryInstall } from './fisherman-helper/packages.js';
export default class Fisherman {
    distro;
    installer = {};
    verbose = false;
    constructor(distro, installer, verbose = false) {
        this.distro = distro;
        this.installer = installer;
        this.verbose = verbose;
    }
    /**
     *
     * @param name
     * @param isScript
     */
    async buildCalamaresModule(name, isScript = true, theme = 'eggs') {
        let moduleTemplate = path.resolve(__dirname, this.installer.templateMultiarch, name);
        if (theme !== 'eggs') {
            moduleTemplate = path.join(theme, 'theme/calamares/calamares-modules', name);
        }
        const moduleDest = path.join(this.installer.multiarchModules, name);
        const moduleScript = `/usr/sbin/${name}.sh`;
        if (this.verbose)
            this.show(name, 'module', moduleDest);
        if (!fs.existsSync(moduleDest)) {
            fs.mkdirSync(moduleDest);
        }
        shx.cp(path.join(moduleTemplate, 'module.yaml'), path.join(moduleDest, 'module.desc'));
        if (isScript) {
            shx.cp(path.join(moduleTemplate, `${name}.sh`), moduleScript);
            await exec(`chmod +x ${moduleScript}`);
        }
        return moduleScript;
    }
    /**
     *
     * @param name
     */
    async buildCalamaresPy(name) {
        const moduleSource = path.resolve(__dirname, this.installer.templateMultiarch, name);
        const moduleDest = path.join(this.installer.multiarchModules, name);
        if (this.verbose)
            this.show(name, 'python', moduleDest);
        if (!fs.existsSync(moduleDest)) {
            fs.mkdirSync(moduleDest);
        }
        shx.cp(path.join(moduleSource, 'module.yaml'), path.join(moduleDest, 'module.desc'));
        shx.cp(path.join(moduleSource, `${name}.yaml`), path.join(moduleDest, `${name}.conf`));
        shx.cp(path.join(moduleSource, 'main.py'), moduleDest);
        await exec(`chmod +x ${path.join(moduleSource, 'main.py')}`);
    }
    /**
     *
     * @param name
     * @param replaces [['search','replace']]
     */
    async buildModule(name, vendor = 'eggs') {
        let moduleSource = path.resolve(__dirname, this.installer.templateModules, `${name}.yaml`);
        /**
         * We need vendor here to have possibility to load custom modules for calamares
         * the custom modules live in: /addons/vendor/theme/calamares/modules
         * and - if exist - take priority on distro modules on /conf/distros/calamares/modules
         *
         * example:
         *
         * ./addons/openos/theme/calamares/modules/partition.yml
         * take place of:
         * ./conf/distros/bullseye/calamares/modules/partition.yml
         * and end in:
         * /etc/calamares/modules/partition.conf
         *
         */
        if (vendor !== 'eggs') {
            let customModuleSource = path.resolve(__dirname, `../../../addons/${vendor}/theme/calamares/modules/${name}.yaml`);
            if (vendor.includes('/')) {
                customModuleSource = path.join(vendor, `theme/calamares/modules/${name}.yaml`);
            }
            if (fs.existsSync(customModuleSource)) {
                moduleSource = customModuleSource;
            }
        }
        const moduleDest = path.join(this.installer.modules, `${name}.conf`);
        if (fs.existsSync(moduleSource)) {
            shx.cp(moduleSource, moduleDest);
            if (this.verbose) {
                this.show(name, 'module', moduleDest);
            }
        }
        else if (this.verbose) {
            console.log('unchanged: ' + chalk.greenBright(name));
        }
    }
    /**
     * buildModuleDracut
     */
    async buildModuleDracut(initrd) {
        const name = 'dracut';
        const moduleSource = path.resolve(__dirname, this.installer.templateModules, `${name}.mustache`);
        const moduleDest = path.join(this.installer.modules, `${name}.conf`);
        const template = fs.readFileSync(moduleSource, 'utf8');
        const view = { initrd };
        fs.writeFileSync(moduleDest, mustache.render(template, view));
    }
    /**
     * buildModuleFinished
     */
    async buildModuleFinished() {
        const name = 'finished';
        await this.buildModule(name);
        const file = path.join(this.installer.modules, `${name}.conf`);
        const fileContent = fs.readFileSync(file, 'utf8');
        const values = yaml.load(fileContent);
        values.restartNowCommand = 'reboot';
        let destContent = `# ${name}.conf, created by penguins-eggs ${pjson.version}\n`;
        destContent += '---\n';
        destContent += yaml.dump(values);
        fs.writeFileSync(file, destContent, 'utf8');
    }
    async buildModuleInitcpio() {
        const { initcpio } = await import('./fisherman-helper/initcpio.js');
        const preset = await initcpio();
        const name = 'initcpio';
        const moduleSource = path.resolve(__dirname, this.installer.templateModules, `${name}.mustache`);
        const moduleDest = path.join(this.installer.modules, `${name}.conf`);
        const template = fs.readFileSync(moduleSource, 'utf8');
        const view = { preset };
        fs.writeFileSync(moduleDest, mustache.render(template, view));
    }
    /**
     * buildModulePackages
     */
    async buildModulePackages(distro, release = false) {
        let backend = 'apt';
        switch (distro.familyId) {
            case 'alpine': {
                backend = 'apk';
                break;
            }
            case 'archlinux': {
                backend = 'pacman';
                break;
            }
            case 'fedora': {
                backend = 'dnf';
                break;
            }
            case 'fedora': {
                backend = 'zypper';
                break;
            }
            // No default
        }
        const yamlInstall = tryInstall(distro);
        let yamlRemove = '';
        if (release) {
            yamlRemove = removePackages(distro);
        }
        let operations = '';
        if (yamlRemove !== '' || yamlInstall !== '') {
            operations = 'operations:\n' + yamlRemove + yamlInstall;
        }
        const name = 'packages';
        const moduleSource = path.resolve(__dirname, this.installer.templateModules, `${name}.mustache`);
        const moduleDest = path.join(this.installer.modules, `${name}.conf`);
        const template = fs.readFileSync(moduleSource, 'utf8');
        const view = {
            backend,
            operations
        };
        fs.writeFileSync(moduleDest, mustache.render(template, view));
    }
    /**
     * buildModuleRemoveuser
     */
    async buildModuleRemoveuser(username) {
        const name = 'removeuser';
        const moduleSource = path.resolve(__dirname, this.installer.templateModules, `${name}.mustache`);
        const moduleDest = path.join(this.installer.modules, `${name}.conf`);
        const template = fs.readFileSync(moduleSource, 'utf8');
        const view = { username };
        fs.writeFileSync(moduleDest, mustache.render(template, view));
    }
    /**
     * ====================================================================================
     * buildModule...
     * ====================================================================================
     */
    /**
     * buildModuleUnpackfs
     */
    async buildModuleUnpackfs() {
        const name = 'unpackfs';
        const moduleSource = path.resolve(__dirname, this.installer.templateModules, `${name}.mustache`);
        const moduleDest = path.join(this.installer.modules, `${name}.conf`);
        const template = fs.readFileSync(moduleSource, 'utf8');
        const source = path.join(this.distro.liveMediumPath, this.distro.squashfs);
        const view = { source };
        fs.writeFileSync(moduleDest, mustache.render(template, view));
    }
    /**
     *
     * @param name
     */
    async contextualprocess(name) {
        const moduleSource = path.resolve(__dirname, this.installer.templateModules, `${name}_context.yaml`);
        const moduleDest = path.join(this.installer.modules, `${name}_context.conf`);
        if (fs.existsSync(moduleSource)) {
            if (this.verbose)
                this.show(name, 'contextualprocess', moduleDest);
            shx.cp(moduleSource, moduleDest);
        }
        else if (this.verbose) {
            console.log(`calamares: ${name} contextualprocess, nothing to do!`);
        }
    }
    /**
     * write settings
     */
    async createCalamaresSettings(theme = 'eggs', isClone = false) {
        await settings(this.installer.template, this.installer.configRoot, theme, isClone);
    }
    /**
     *
     * @param name
     */
    async helper(name) {
        const helperSource = path.resolve(__dirname, this.installer.template, 'libexec', `${name}.sh`);
        const helperDest = `/usr/libexec/calamares/${name}.sh`;
        if (fs.existsSync(helperSource)) {
            if (this.verbose)
                this.show(name, 'helper', helperDest);
            let fileContent = fs.readFileSync(helperSource, 'utf8');
            fileContent = fileContent.replaceAll('__LIVE_MEDIUM_PATH__', this.distro.liveMediumPath);
            fs.writeFileSync(helperDest, fileContent, { encoding: 'utf-8', mode: 0o755 });
        }
        else if (this.verbose) {
            console.log(`calamares: ${name} helper, nothing to do`);
        }
    }
    /**
     *
     * @param name
     */
    async shellprocess(name) {
        const moduleSource = path.resolve(__dirname, this.installer.templateModules, `shellprocess@${name}.yaml`);
        const moduleDest = path.join(this.installer.modules, `shellprocess@${name}.conf`);
        if (fs.existsSync(moduleSource)) {
            if (this.verbose)
                this.show(name, 'shellprocess', moduleDest);
            let fileContent = fs.readFileSync(moduleSource, 'utf8');
            fileContent = fileContent.replaceAll('__LIVE_MEDIUM_PATH__', this.distro.liveMediumPath);
            fs.writeFileSync(moduleDest, fileContent, 'utf-8');
        }
        else if (this.verbose) {
            console.log(`calamares: ${name} shellprocess, nothing to do`);
        }
    }
    /**
     *
     * @param module
     * @param type
     * @param path
     */
    show(name, type, path) {
        switch (type) {
            case 'calamares_module': {
                console.log('fisherman: ' + chalk.cyanBright(name) + ' calamares_module in ' + chalk.cyanBright(path));
                break;
            }
            case 'contextualprocess': {
                console.log('fisherman: ' + chalk.cyanBright(name) + ' shellprocess in ' + chalk.cyanBright(path));
                break;
            }
            case 'module': {
                console.log('fisherman: ' + chalk.yellow(name) + ' module in ' + chalk.yellow(path));
                break;
            }
            case 'shellprocess': {
                console.log('fisherman: ' + chalk.green(name) + ' shellprocess in ' + chalk.green(path));
                break;
            }
            // No default
        }
    }
}
