"use strict";
/**
 * penguins-eggs
 * class: tailor.ts
 * author: Piero Proietti
 * email: piero.proietti@gmail.com
 * license: MIT
 */
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const chalk_1 = tslib_1.__importDefault(require("chalk"));
const utils_1 = tslib_1.__importDefault(require("./utils"));
const utils_2 = require("../lib/utils");
const fs_1 = tslib_1.__importDefault(require("fs"));
const path_1 = tslib_1.__importDefault(require("path"));
const js_yaml_1 = tslib_1.__importDefault(require("js-yaml"));
const pacman_1 = tslib_1.__importDefault(require("./pacman"));
const distro_1 = tslib_1.__importDefault(require("./distro"));
const sources_list_1 = tslib_1.__importDefault(require("./sources_list"));
const pjson = require('../../package.json');
/**
*
*/
class Tailor {
    /**
    * @param wardrobe
    * @param costume
    */
    constructor(costume, category = 'costume') {
        this.verbose = false;
        this.echo = {};
        this.costume = '';
        this.wardrobe = '';
        this.category = 'costume';
        this.materials = {};
        this.costume = costume;
        this.wardrobe = path_1.default.dirname((path_1.default.dirname(costume)));
        this.category = category;
    }
    /**
    *
    */
    async prepare(verbose = true, no_accessories = false, no_firmwares = false) {
        this.verbose = verbose;
        this.echo = utils_1.default.setEcho(verbose);
        utils_1.default.warning(`preparing ${this.costume}`);
        /**
        * check curl presence
        */
        if (!pacman_1.default.packageIsInstalled('curl')) {
            utils_1.default.pressKeyToExit('In this tailoring shop we use curl. sudo apt update | apt install curl');
            process.exit();
        }
        // Analyze distro
        let distro = new distro_1.default();
        let tailorList = '';
        if (distro.distroLike === 'Debian') {
            tailorList = `${this.costume}/debian.yml`;
            if (!fs_1.default.existsSync(tailorList)) {
                tailorList = `${this.costume}/devuan.yml`;
                if (!fs_1.default.existsSync(tailorList)) {
                    tailorList = `${this.costume}/ubuntu.yml`;
                    if (!fs_1.default.existsSync(tailorList)) {
                        console.log(`no costume definition found compatible Debian`);
                        process.exit();
                    }
                }
            }
        }
        else if (distro.distroLike === 'Devuan') {
            tailorList = `${this.costume}/devuan.yml`;
            if (!fs_1.default.existsSync(tailorList)) {
                tailorList = `${this.costume}/debian.yml`;
                if (!fs_1.default.existsSync(tailorList)) {
                    tailorList = `${this.costume}/ubuntu.yml`;
                    if (!fs_1.default.existsSync(tailorList)) {
                        console.log(`no costume definition found compatible Devuan`);
                        process.exit();
                    }
                }
            }
        }
        else if (distro.distroLike === 'Ubuntu') {
            tailorList = `${this.costume}/ubuntu.yml`;
            if (!fs_1.default.existsSync(tailorList)) {
                tailorList = `${this.costume}/debian.yml`;
                console.log(`trying ` + tailorList);
                if (!fs_1.default.existsSync(tailorList)) {
                    tailorList = `${this.costume}/devuan.yml`;
                    console.log(`trying ` + tailorList);
                    if (!fs_1.default.existsSync(tailorList)) {
                        console.log(`no costume definition found compatible Ubuntu`);
                        process.exit();
                    }
                }
            }
        }
        else if (distro.distroLike === 'Arch') {
            tailorList = `${this.costume}/arch.yml`;
            if (!fs_1.default.existsSync(tailorList)) {
                tailorList = `${this.costume}/debian.yml`;
                if (!fs_1.default.existsSync(tailorList)) {
                    console.log(`no costume definition found compatible Arch`);
                    process.exit();
                }
            }
        }
        if (fs_1.default.existsSync(tailorList)) {
            this.materials = js_yaml_1.default.load(fs_1.default.readFileSync(tailorList, 'utf-8'));
        }
        else
            switch (this.category) {
                case 'costume': {
                    this.titles(`${this.category}: ${this.costume}`);
                    console.log('Tailor\'s list ' + chalk_1.default.cyan(tailorList) + ' is not found \non your wardrobe ' + chalk_1.default.cyan(this.wardrobe) + '.\n');
                    console.log('Costume will not be installed, operations will abort.\n');
                    utils_1.default.pressKeyToExit();
                    process.exit();
                    break;
                }
                case 'accessory': {
                    this.titles(`${this.category}: ${this.costume}`);
                    console.log('Tailor\'s list ' + chalk_1.default.cyan(tailorList) + ' is not found \non your wardrobe ' + chalk_1.default.cyan(this.wardrobe) + '.\n');
                    console.log('Accessory will not be installed, operations will continue.\n');
                    utils_1.default.pressKeyToExit();
                    return;
                }
                case 'try_accessory': {
                    return;
                }
                // No default
            }
        /**
        * distro e sources_list
        * vengono definite qua perchè servono a tutti
        */
        const sources_list = new sources_list_1.default();
        let step = '';
        if (this.materials.distributions !== undefined) {
            step = 'analyzing distribution';
            utils_1.default.warning(step);
            if (!await sources_list.distribution(this.materials.distributions)) {
                switch (this.category) {
                    case 'costume': {
                        this.titles('step');
                        console.log('Costume ' + chalk_1.default.cyan(this.materials.name) + ' is not compatible \nwith your ' + chalk_1.default.cyan(distro.distroId + '/' + distro.codenameId) + ' system.\n');
                        console.log('Costume will not be installed, operations will abort.\n');
                        utils_1.default.pressKeyToExit();
                        process.exit();
                        break;
                    }
                    case 'accessory': {
                        this.titles('step');
                        console.log('Accessory ' + chalk_1.default.cyan(this.materials.name) + ' is not compatible \nwith your ' + chalk_1.default.cyan(distro.distroId + '/' + distro.codenameId) + ' system.\n');
                        console.log('Accessory will not be installed, operations will continue.\n');
                        utils_1.default.pressKeyToExit();
                        return;
                    }
                    case 'try_accessory': {
                        return;
                    }
                    // No default
                }
            }
        }
        /**
        * sequence
        */
        if (this.materials.sequence !== undefined) {
            step = 'analyzing sequence';
            utils_1.default.warning(step);
            /**
            * sequence/repositories
            */
            if (this.materials.sequence.repositories !== undefined) {
                if (distro.familyId === "debian") {
                    /**
                    * sequence/repositories/sources_list
                    */
                    // evito di fallire se sources_list non è presente
                    if (this.materials.sequence.repositories.sources_list !== undefined) {
                        step = 'analyzing repositories';
                        utils_1.default.warning(step);
                        if (distro.distroLike === 'Debian') {
                            await sources_list.components(this.materials.sequence.repositories.sources_list);
                        }
                    }
                    /**
                    * sequence/repositories/sources_list_d
                    */
                    if (this.materials.sequence.repositories.sources_list_d !== undefined && this.materials.sequence.repositories.sources_list_d[0] !== null) {
                        step = 'adding repositories to /etc/apt/sources_list_d';
                        utils_1.default.warning(step);
                        for (const cmd of this.materials.sequence.repositories.sources_list_d) {
                            try {
                                // repeat 3 times if fail curl or others commands
                                for (let i = 0; i < 2; i++) {
                                    const result = await (0, utils_2.exec)(cmd, this.echo);
                                    if (result.code === 0) {
                                        break;
                                    }
                                }
                            }
                            catch (error) {
                                await utils_1.default.pressKeyToExit(JSON.stringify(error));
                            }
                        }
                    }
                }
                /**
                * sequence/repositories/update
                */
                if (this.materials.sequence.repositories.update === undefined) {
                    console.log('repositiories, and repositories.update MUST be defined on sequence');
                    process.exit();
                }
                step = 'repositories update';
                utils_1.default.warning(step);
                if (this.materials.sequence.repositories.update) {
                    if (distro.familyId === "debian") {
                        await (0, utils_2.exec)('apt-get update', utils_1.default.setEcho(false));
                    }
                    else {
                        await (0, utils_2.exec)('pacman -Sy', utils_1.default.setEcho(false));
                    }
                }
                /**
                * sequence/repositories/upgrade
                */
                if (this.materials.sequence.repositories.upgrade !== undefined) {
                    step = 'repositories upgrade';
                    utils_1.default.warning(step);
                    if (this.materials.sequence.repositories.upgrade) {
                        if (distro.familyId === "debian") {
                            await (0, utils_2.exec)('apt-get full-upgrade -y', utils_1.default.setEcho(false));
                        }
                        else {
                            await (0, utils_2.exec)('pacman -Su', utils_1.default.setEcho(false));
                        }
                    } //  upgrade true
                } // undefined upgrade
            } // end sequence/repositories
            /**
            * sequence/preinst
            */
            if (this.materials.sequence.preinst !== undefined && Array.isArray(this.materials.sequence.preinst)) {
                step = 'preinst scripts';
                utils_1.default.warning(step);
                for (const script of this.materials.sequence.preinst) {
                    if (fs_1.default.existsSync(`${this.costume}/${script}`)) {
                        await (0, utils_2.exec)(`${this.costume}/${script} ${this.materials.name}`, utils_1.default.setEcho(true));
                    }
                    else {
                        // exec script real env
                        await (0, utils_2.exec)(`${script} ${this.materials.name}`, utils_1.default.setEcho(true));
                    }
                }
            }
            /**
            * install packages
            */
            if (this.materials.sequence.packages !== undefined) {
                if (distro.familyId === 'debian') {
                    const packages = await this.helperExists(this.materials.sequence.packages, true, 'packages');
                    if (packages.length > 1) {
                        await this.helperInstall(packages);
                    }
                }
                else {
                    await this.helperInstall(this.materials.sequence.packages, 'packages', `pacman -Sy --noconfirm`);
                }
            }
            if (distro.familyId === "debian") {
                /**
                * sequence/packages_no_install_recommends
                */
                if (this.materials.sequence.packages_no_install_recommends !== undefined) {
                    const packages_no_install_recommends = await this.helperExists(this.materials.sequence.packages_no_install_recommends, true, 'packages_no_install_recommends');
                    if (packages_no_install_recommends.length > 1) {
                        await this.helperInstall(packages_no_install_recommends, 'packages without recommends and suggests', 'apt-get install --no-install-recommends --no-install-suggests -yq ');
                    }
                }
                /**
                * sequence/try_packages
                */
                if (this.materials.sequence.try_packages !== undefined) {
                    const try_packages = await this.helperExists(this.materials.sequence.try_packages, false);
                    if (try_packages.length > 1) {
                        await this.helperInstall(try_packages, 'try packages ');
                    }
                }
                /**
                * sequence/try_packages_no_install_recommends
                */
                if (this.materials.sequence.try_packages_no_install_recommends !== undefined) {
                    const try_packages_no_install_recommends = await this.helperExists(this.materials.sequence.try_packages_no_install_recommends, false);
                    if (try_packages_no_install_recommends.length > 1) {
                        await this.helperInstall(try_packages_no_install_recommends, 'try packages without recommends and suggests', 'apt-get install --no-install-recommends --no-install-suggests -yq ');
                    }
                }
                /**
                * sequence/debs
                */
                if (this.materials.sequence.debs !== undefined && this.materials.sequence.debs) {
                    step = 'installing local packages';
                    utils_1.default.warning(step);
                    let pathDebs = `${this.costume}/debs/${distro.codenameLikeId}`;
                    if (!fs_1.default.existsSync(pathDebs)) {
                        pathDebs = `${this.costume}/debs`;
                    }
                    // if exists pathDebs
                    if (fs_1.default.existsSync(pathDebs)) {
                        await (0, utils_2.exec)(`dpkg -i ${pathDebs}/*.deb`);
                    }
                }
            }
            /**
            * sequence/packages_python
            */
            if (this.materials.sequence.packages_python !== undefined && Array.isArray(this.materials.sequence.packages_python)) {
                let cmd = 'pip install ';
                let pip = '';
                for (const elem of this.materials.sequence.packages_python) {
                    cmd += ` ${elem}`;
                    pip += `, ${elem}`;
                }
                step = `installing python packages pip ${pip.slice(2)}`;
                utils_1.default.warning(step);
                await (0, utils_2.exec)(cmd, this.echo);
            }
            /**
            * sequence/accessories
            */
            if (!no_accessories) {
                // accessories
                if (this.materials.sequence.accessories !== undefined && Array.isArray(this.materials.sequence.accessories)) {
                    step = 'wearing accessories';
                    for (const elem of this.materials.sequence.accessories) {
                        if ((elem === 'firmwares' || elem === './firmwares') && no_firmwares) {
                            continue;
                        }
                        if (elem.slice(0, 2) === './') {
                            const tailor = new Tailor(`${this.costume}/${elem.slice(2)}`, 'accessory');
                            await tailor.prepare(verbose);
                        }
                        else {
                            const tailor = new Tailor(`${this.wardrobe}/accessories/${elem}`, 'accessory');
                            await tailor.prepare(verbose);
                        }
                    }
                }
                if (distro.familyId === "debian") {
                    // try_accessories
                    if (this.materials.sequence.try_accessories !== undefined && Array.isArray(this.materials.sequence.try_accessories)) {
                        step = 'wearing try_accessories';
                        for (const elem of this.materials.sequence.try_accessories) {
                            if ((elem === 'firmwares' || elem === './firmwares') && no_firmwares) {
                                continue;
                            }
                            if (elem.slice(0, 2) === './') {
                                const tailor = new Tailor(`${this.costume}/${elem.slice(2)}`, 'try_accessory');
                                await tailor.prepare(verbose);
                            }
                            else {
                                const tailor = new Tailor(`${this.wardrobe}/accessories/${elem}`, 'try_accessory');
                                await tailor.prepare(verbose);
                            }
                        }
                    }
                }
            } // no-accessories
        } // end sequence
        /**
           * customize
           */
        if (this.materials.customize !== undefined) {
            /**
            * customize/dirs
            */
            if (this.materials.customize.dirs && fs_1.default.existsSync(`/${this.costume}/dirs`)) {
                step = 'copying dirs';
                utils_1.default.warning(step);
                let cmd = `rsync -avx  ${this.costume}/dirs/* /`;
                await (0, utils_2.exec)(cmd, this.echo);
                // chown root:root /etc -R
                cmd = 'chown root:root /etc/sudoers.d /etc/skel -R';
                await (0, utils_2.exec)(cmd, this.echo);
                /**
                * Copyng skel in /home/user
                */
                if (fs_1.default.existsSync(`${this.costume}/dirs/etc/skel`)) {
                    const user = await utils_1.default.getPrimaryUser();
                    step = `copying skel in /home/${user}/`;
                    utils_1.default.warning(step);
                    cmd = `rsync -avx  ${this.costume}/dirs/etc/skel/.config /home/${user}/`;
                    await (0, utils_2.exec)(cmd, this.echo);
                    await (0, utils_2.exec)(`chown ${user}:${user} /home/${user}/ -R`);
                }
            }
            /**
            * customize/scripts
            */
            if (this.materials.customize.scripts !== undefined && Array.isArray(this.materials.customize.scripts)) {
                step = 'customize script';
                utils_1.default.warning(step);
                for (const script of this.materials.customize.scripts) {
                    if (fs_1.default.existsSync(`${this.costume}/${script}`)) {
                        // exec script in costume passing costume-name
                        await (0, utils_2.exec)(`${this.costume}/${script} ${this.materials.name}`, utils_1.default.setEcho(true));
                    }
                    else {
                        // exec script real env
                        await (0, utils_2.exec)(`${script}`, utils_1.default.setEcho(true));
                    }
                }
            }
        }
        /**
        * reboot
        */
        if (this.materials.reboot) {
            utils_1.default.warning('Reboot');
            await utils_1.default.pressKeyToExit('system need to reboot', true);
            await (0, utils_2.exec)('reboot');
        }
        else {
            console.log(`You look good with: ${this.materials.name}`);
        }
    }
    /**
    *
    * @param packages
    * @param verbose
    * @param section
    * @returns
    */
    async helperExists(packages, verbose = false, section = '') {
        const packages_we_want = '/tmp/packages_we_want';
        const packages_not_exists = '/tmp/packages_not_exists';
        const packages_exists = '/tmp/packages_exists';
        await (0, utils_2.exec)(`rm -f ${packages_we_want}`);
        await (0, utils_2.exec)(`rm -f ${packages_not_exists}`);
        await (0, utils_2.exec)(`rm -f ${packages_exists}`);
        /**
        * packages_we_want
        */
        let content = '';
        packages.sort();
        for (const elem of packages) {
            if (!pacman_1.default.packageIsInstalled(elem)) {
                content += elem + '\n';
            }
        }
        fs_1.default.writeFileSync(packages_we_want, content, 'utf-8');
        /**
        * packages_exists
        */
        let distro = new distro_1.default();
        if (distro.familyId === 'debian') {
            await (0, utils_2.exec)(`apt-cache --no-generate pkgnames | sort | comm -12 - ${packages_we_want} > ${packages_exists}`);
        }
        else {
            await (0, utils_2.exec)(`pacman -S --list | awk '{print $2}' | sort | comm -12 - ${packages_we_want} > ${packages_exists}`);
        }
        /**
        * packages_not_exists
        */
        if (verbose) {
            if (distro.familyId === "debian") {
                await (0, utils_2.exec)(`apt-cache --no-generate pkgnames | sort | comm -13 - ${packages_we_want} > ${packages_not_exists}`);
            }
            else {
                await (0, utils_2.exec)(`pacman -S --list | awk '{print $2}' | sort | comm -13 - ${packages_we_want} > ${packages_not_exists}`);
            }
            const not_exist_packages = fs_1.default.readFileSync(packages_not_exists, 'utf-8').split('\n');
            if (not_exist_packages.length > 1) { // Una riga c'è sempre
                let content = '';
                // for (const elem of not_exist_packages) {
                for (let i = 0; i < not_exist_packages.length - 1; i++) {
                    content += `- ${not_exist_packages[i]}\n`;
                }
                this.titles('tailor');
                console.log('Following packages from ' + chalk_1.default.cyan(this.materials.name) + ' section: ' + chalk_1.default.cyan(section) + ', was not found:');
                console.log(content);
                utils_1.default.pressKeyToExit('Press a key to continue...');
            }
        }
        return fs_1.default.readFileSync(packages_exists, 'utf-8').split('\n');
    }
    /**
    * - check if every package if installed
    * - if find any packages to install, install it
    */
    async helperInstall(packages, comment = 'packages', cmd = 'apt-get install -yqq ') {
        if (packages[0] !== null) {
            const elements = [];
            let strElements = '';
            for (const elem of packages) {
                elements.push(elem);
                cmd += ` ${elem}`;
                strElements += `, ${elem}`;
            }
            if (elements.length > 0) {
                let step = `installing ${comment}: `;
                // if not verbose show strElements
                if (!this.verbose) {
                    step += strElements.slice(2);
                }
                /**
                * prova 3 volte
                */
                const limit = 3;
                for (let tempts = 1; tempts < limit; tempts++) {
                    this.titles(step);
                    utils_1.default.warning(`tempts ${tempts} of ${limit}`);
                    if (await tryCheckSuccess(cmd, this.echo)) {
                        break;
                    }
                }
            }
        }
    }
    /**
     *
     * @param command
     */
    titles(command = '') {
        console.clear();
        console.log('');
        console.log(' E G G S: the reproductive system of penguins');
        console.log('');
        console.log(chalk_1.default.bgGreen.whiteBright('      ' + pjson.name + '      ') +
            chalk_1.default.bgWhite.blue(" Perri's Brewery edition ") +
            chalk_1.default.bgRed.whiteBright('       ver. ' + pjson.version + '       '));
        console.log('wearing: ' + chalk_1.default.bgBlack.cyan(this.costume) + ' ' + chalk_1.default.bgBlack.white(command) + '\n');
    }
}
exports.default = Tailor;
/**
 *
 * @param cmd
 * @param echo
 * @returns
 */
async function tryCheckSuccess(cmd, echo) {
    let success = false;
    try {
        await (0, utils_2.exec)(cmd, echo);
        success = true;
    }
    catch {
        success = false;
    }
    return success;
}
