/**
 * Load dependent libraries
 * fs = Filesystem
 *
 * dotenv loads config from .env file and stores it in process.env
 */
const fs = require('fs'),
    path = require('path'),
    excludes = [
        'core/css/import.less'
    ];

require('dotenv').config();


/**
 * Get a list of folders within the specified directory
 *
 * @param dir String
 * @return array
 */
function getFolders(dir) {
    'use strict';

    try {
        return fs.readdirSync(dir)
            .filter((file) => {
                return fs.statSync(path.join(dir, file)).isDirectory();
            });
    } catch (e) {
        // Exception due to folder not found.
        // Instead of failing, return an empty set.
        return [];
    }
}

/**
 * Get files in a directory by type
 *
 * @param dir
 * @param type
 * @returns {*}
 */
function getFiles(dir, type) {
    'use strict';

    try {
        return fs.readdirSync(dir)
            .filter((file) => {
                return fs.statSync(path.join(dir, file)).isFile()
                    && file.indexOf(type) !== -1;
            });
    } catch (e) {
        // Exception due to folder not found.
        // Instead of failing, return an empty set.
        return [];
    }
}

/**
 * Set the site ID in app less file
 * Ensure we have site id available in less and it is synchronised with .env file
 *
 * @param site_id
 */
function writeSiteIdForAppLess(site_id)
{
    const replace = require('replace-in-file'),
        sample_file = './resources/less/app_sample.less',
        file = './resources/less/app.less',
        options = {
            files: file,
            from: /@site_id: '([a-z0-9\-_]+)';/g,
            to: "@site_id: '" + site_id + "';",
        };

    // copy app_sample.less to app.less before updating the site id
    fs.copyFile(sample_file, file, (err) => {
        if (err) throw err;
        console.log(sample_file + ' was copied to ' + file);
    });

    try {
        replace(options);
    } catch (error) {
        console.error('Error occurred:', error);
    }
}

/**
 * Generate public site dynamic less
 * Loads dynamic assets (theme, modules) and pushes them into a single file
 * so assets can included during generation
 */
function appDynamicLess() {
    var prefix = '../../',
        files = [
            path.join(prefix, 'resources/less/font_awesome/fontawesome.less')
        ],
        site_path = path.join('./sites', process.env.SITE_ID, 'css'),
        dynamic_less = path.join('./sites', process.env.SITE_ID, 'dynamic.less');

    // Load root module assets
    [
        'core',
        'cms',
        'shop',
        'abs'
    ].forEach((root_module) => {
        let asset_path = path.join(root_module, 'css');
        // get files and add them to file stack
        getFiles(asset_path, '.less')
            .forEach((file_name) => {
                if (excludes.indexOf(path.join(asset_path, file_name)) !== -1) {
                    return;
                }
                files.push(path.join(prefix, asset_path, file_name));
            });

        // For legacy purposes we also allow css files in root modules
        getFiles(asset_path, '.css')
            .forEach((file_name) => {
                if (excludes.indexOf(path.join(asset_path, file_name)) !== -1) {
                    return;
                }
                files.push(path.join(prefix, asset_path, file_name));
            });
    });

    // Loop through all plugin folders
    [
        'plugins',
        'modules'
    ].forEach((component_name) => {
        getFolders(component_name)
            .forEach((component) => {
                let component_path = path.join(component_name, component, 'css');
                // get files and add them to file stack
                getFiles(component_path, '.less')
                    .forEach((file_name) => {
                        files.push(path.join(prefix, component_path, file_name));
                    });
            });
    });

    /*
     * Add in vendor published less files
     * These files are published to /resources/less/vendor/app/*.less
     */
    getFiles('./resources/less/vendor/app', '.less')
        .forEach((file_name) => {
            files.push(path.join('../../resources/less/vendor/app', file_name));
        });

    /*
     * Add asset files from /sites/ into the array
     * Map files to have the @import wrapper so less can process them
     * Join them into one long string so we can write to file
     */
    file_data = files.concat(getFiles(site_path, '.less')
        .map((file_name) => {
            return path.join(prefix, site_path, file_name);
        }))
        .map((file_name) => {
            return "@import '" + file_name + "';";
        })
        .join("\n");

    writeSiteIdForAppLess(process.env.SITE_ID);
    // Write data to file
    fs.writeFile(dynamic_less, file_data, (err) => {  });
}

/**
 * Generate admin site dynamic less
 * Generates assets for admin side - removes theme and attaches admin path to assets
 */
function adminDynamicLess() {

    var prefix = '../../',
        files = [],
        dynamic_less = './resources/less/dynamic_admin.less';

    // Load root module assets
    [
        'cms',
        'shop',
        'abs'
    ].forEach((root_module) => {
        let asset_path = path.join(root_module, 'admin/css');
        // get files and add them to file stack
        getFiles(asset_path, '.less')
            .forEach((file_name) => {
                if (excludes.indexOf(path.join(asset_path, file_name)) !== -1) {
                    return;
                }
                files.push(path.join(prefix, asset_path, file_name));
            });

        // For legacy purposes we also allow css files in root modules
        getFiles(asset_path, '.css')
            .forEach((file_name) => {
                if (excludes.indexOf(path.join(asset_path, file_name)) !== -1) {
                    return;
                }
                files.push(path.join(prefix, asset_path, file_name));
            });
    });

    // Loop through all plugin folders
    [
        'plugins',
        'modules'
    ].forEach((component_name) => {
        getFolders(component_name)
            .forEach((component) => {
                let component_path = path.join(component_name, component, 'admin/css');
                // get files and add them to file stack
                getFiles(component_path, '.less')
                    .forEach((file_name) => {
                        files.push(path.join(prefix, component_path, file_name));
                    });
            });
    });

    /*
     * Add in vendor published less files
     * These files are published to /resources/less/vendor/admin/*.less
     */
    getFiles('./resources/less/vendor/admin', '.less')
        .forEach((file_name) => {
            files.push(path.join('../../resources/less/vendor/admin', file_name));
        });

    /*
     * Map files to have the @import wrapper so less can process them
     * Join them into one long string so we can write to file
     */
    file_data = files
        .map((file_name) => {
            return "@import '" + file_name + "';";
        })
        .join("\n");

    // Write data to file
    fs.writeFile(dynamic_less, file_data, (err) => {  });
}

/**
 * Generate dynamic_app.js
 * This asset file loads js files from site and modules
 * Custom bits are starting the list with functions.js in sites path
 * and ending with script.js from the sites path
 */
function appDynamicJs() {
    var prefix = '../../',
        site_path = path.join('./sites', process.env.SITE_ID, 'js'),
        files = [
            path.join(prefix, site_path, 'functions.js')
        ],
        dynamic_js = './resources/js/dynamic_app.js';

    // Loop through all plugin folders
    [
        'plugins',
        'modules'
    ].forEach((component_name) => {
        getFolders(component_name)
            .forEach((component) => {
                let component_path = path.join(component_name, component, 'js');
                // get files and add them to file stack
                getFiles(component_path, '.js')
                    .forEach((file_name) => {
                        files.push(path.join(prefix, component_path, file_name));
                    });
            });
    });

    // Load root module assets
    [
        'shop',
        'cms',
        'abs'
    ].forEach((root_module) => {
        let asset_path = path.join(root_module, 'js');
        // get files and add them to file stack
        getFiles(asset_path, '.js')
            .forEach((file_name) => {
                files.push(path.join(prefix, asset_path, file_name));
            });
    });

    /*
     * Add asset files from /sites/ into the array
     * This takes all files from js/site_scripts path and adds js/script.js on top
     */
    files = files.concat(getFiles(path.join(site_path, 'site_scripts'), '.js')
        .map((file_name) => {
            return path.join(prefix, site_path, 'site_scripts', file_name);
        }));

    files.push(path.join(prefix, site_path, 'script.js'));

    /**
     * Map files to have the @import wrapper so js can process them
     * Join them into one long string so we can write to file
     */
    file_data = files
        .map((file_name) => {
            return "require('" + file_name + "');";
        })
        .join("\n");

    // Write data to file
    fs.writeFile(dynamic_js, file_data, (err) => {  });
}

/**
 * Generate admin dynamic js file
 */
function adminDynamicJs() {
    var prefix = '../../',
        files = [],
        dynamic_js = './resources/js/dynamic_admin.js';

    // Load root module assets
    [
        'shop',
        'cms',
        'abs'
    ].forEach((root_module) => {
        let asset_path = path.join(root_module, 'admin/js');
        // get files and add them to file stack
        getFiles(asset_path, '.js')
            .forEach((file_name) => {
                files.push(path.join(prefix, asset_path, file_name));
            });
    });

    // Loop through all plugin folders
    [
        'plugins',
        'modules'
    ].forEach((component_name) => {
        getFolders(component_name)
            .forEach((component) => {
                let component_path = path.join(component_name, component, 'admin/js');
                // get files and add them to file stack
                getFiles(component_path, '.js')
                    .forEach((file_name) => {
                        files.push(path.join(prefix, component_path, file_name));
                    });
            });
    });


    /**
     * Map files to have the @import wrapper so js can process them
     * Join them into one long string so we can write to file
     */
    file_data = files
        .map((file_name) => {
            return "require('" + file_name + "');";
        })
        .join("\n");

    // Write data to file
    fs.writeFile(dynamic_js, file_data, (err) => {  });
}

appDynamicLess();
adminDynamicLess();
appDynamicJs();
adminDynamicJs();
