'use strict';

var tslib = require('tslib');
var logger = require('@component-controls/logger');
var webpack = require('webpack');
var path = require('path');
var fs = require('fs');
var config = require('@component-controls/config');
var core = require('@component-controls/core');
var LoaderPlugin = require('@component-controls/loader/plugin');
var webpackConfigs = require('@component-controls/webpack-configs');
var yargs = require('yargs');

function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

var webpack__default = /*#__PURE__*/_interopDefaultLegacy(webpack);
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
var LoaderPlugin__default = /*#__PURE__*/_interopDefaultLegacy(LoaderPlugin);
var yargs__default = /*#__PURE__*/_interopDefaultLegacy(yargs);

const defaultCliArgs = [
    {
        name: 'config',
        options: {
            alias: 'c',
            description: 'configuration folder',
            type: 'string',
            default: '.config',
        },
    },
    {
        name: 'watch',
        options: {
            alias: 'w',
            description: 'watch mode (default compile mode)',
            default: false,
            type: 'boolean',
        },
    },
    {
        name: 'presets',
        options: {
            alias: 'p',
            description: 'presets ',
            type: 'array',
            default: ['react', 'react-docgen-typescript'],
        },
    },
    {
        name: 'bundle',
        options: {
            alias: 'b',
            description: 'bundle name',
            type: 'string',
            default: 'component-controls.js',
        },
    },
    {
        name: 'dist',
        options: {
            alias: 'd',
            description: 'distribution (bundle output) folder',
            type: 'string',
            default: 'public',
        },
    },
    {
        name: 'static',
        options: {
            alias: 's',
            description: 'static assets folder',
            type: 'string',
            default: 'dist/static',
        },
    },
    {
        name: 'loglevel',
        options: {
            alias: 'l',
            description: 'log level',
            choices: ['all', 'errors', 'none'],
            default: 'all',
        },
    },
    {
        name: 'mode',
        options: {
            alias: 'm',
            description: 'NODE_ENV mode ',
            choices: ['production', 'development'],
            default: 'development',
        },
    },
];
const cliArgs = (options = []) => {
    return [...defaultCliArgs, ...options].reduce((acc, option) => acc.option(option.name, option.options), yargs__default['default'](process.argv));
};

class ResolveExternals {
    constructor(config) {
        this.addAlias = (props) => {
            const { lib, externalName } = props;
            if (typeof this._config.externals !== 'object') {
                this._config.externals = {};
            }
            this._config.externals[lib] =
                externalName || lib;
        };
        this._config = config;
    }
}

const defaultExternals = [
    {
        lib: 'react',
        externalName: {
            commonjs: 'react',
            commonjs2: 'react',
            amd: 'React',
            root: 'React',
        },
    },
    {
        lib: 'react-dom',
        externalName: {
            commonjs: 'react-dom',
            commonjs2: 'react-dom',
            amd: 'ReactDOM',
            root: 'ReactDOM',
        },
    },
    {
        lib: 'prop-types',
        externalName: {
            commonjs: 'prop-types',
            commonjs2: 'prop-types',
            amd: 'PropTypes',
            root: 'PropTypes',
        },
    },
    { lib: 'regenerator-runtime' },
    {
        lib: 'lodash',
        externalName: {
            commonjs: 'lodash',
            commonjs2: 'lodash',
            amd: 'lodash',
            root: '_',
        },
    },
    { lib: 'theme-ui' },
    { lib: '@theme-ui/presets' },
    { lib: '@theme-ui/core' },
    { lib: '@theme-ui/css' },
    { lib: '@component-controls/core' },
    { lib: '@component-controls/store' },
    { lib: '@component-controls/blocks' },
    { lib: '@component-controls/components' },
    { lib: 'broadcast-channel' },
    { lib: 'react-helmet' },
    { lib: 'react-table' },
    { lib: 'react-colorful' },
    { lib: 'react-google-charts' },
    { lib: 'react-tabs' },
    { lib: 'react-switch' },
    { lib: 'react-popper' },
    { lib: 'react-popper-tooltip' },
    { lib: 'react-animate-height' },
    { lib: 'polished' },
    { lib: 'tinycolor2' },
    { lib: 'prism-react-renderer/themes' },
    { lib: 'prism-react-renderer' },
    { lib: '@primer/octicons-react' },
    { lib: '@emotion/styled' },
    { lib: '@mdx-js/react' },
    { lib: 'markdown-to-jsx' },
    { lib: 'axe-core' },
    { lib: 'faker/locale/en_US' },
    { lib: 'fuse.js' },
];

/**
 * returns the default bumnle full path or args config
 *  ./public/component-controls.js
 */
const getBundleName = () => {
    const args = cliArgs().parse();
    let distFolder = args.dist || `${path__default['default'].join(process.cwd(), 'public')}`;
    if (!path__default['default'].isAbsolute(distFolder)) {
        distFolder = path__default['default'].resolve(process.cwd(), distFolder);
    }
    const bundleName = args.bundle || core.defBundleName;
    return path__default['default'].join(distFolder, bundleName);
};
const createConfig = (options) => {
    const { webpack: webpack$1, presets, configPath, mode, staticFolder: propStaticFolder, distFolder: propDistFolder, bundleName = core.defBundleName, } = options;
    let distFolder = propDistFolder || `${path__default['default'].join(process.cwd(), 'public')}`;
    if (!path__default['default'].isAbsolute(distFolder)) {
        distFolder = path__default['default'].resolve(process.cwd(), distFolder);
    }
    const staticFolder = propStaticFolder || path__default['default'].join(distFolder, 'static');
    const plugins = [
        new LoaderPlugin__default['default']({
            config: configPath,
            escapeOutput: mode === 'development',
        }),
        new webpack.BannerPlugin({
            raw: true,
            banner: '/* eslint-disable */',
        }),
        mode === 'development' && new webpack.HotModuleReplacementPlugin({}),
    ].filter(Boolean);
    const webpackConfig = webpackConfigs.mergeWebpackConfig(Object.assign({ mode, entry: {
            stories: require.resolve('@component-controls/loader/story-store-data.js'),
        }, output: {
            path: distFolder,
            filename: bundleName,
            libraryTarget: 'umd',
            globalObject: 'this',
        }, resolve: {
            alias: {},
        }, externals: {}, plugins }, (webpack$1 || {})), presets, Object.assign({ staticFolder, distFolder }, options));
    //add all the aliases to avoid double loading of packages
    const alias = new ResolveExternals(webpackConfig);
    defaultExternals.forEach(a => alias.addAlias(a));
    const userWebpackConfig = options && (options.webpack || options.finalWebpack);
    if (!userWebpackConfig) {
        return webpackConfig;
    }
    return typeof userWebpackConfig === 'function'
        ? userWebpackConfig(webpackConfig, options)
        : webpackConfigs.deepMergeWebpackConfig(webpackConfig, userWebpackConfig);
};
const runCompiler = (run, props) => {
    return new Promise(resolve => {
        const buildConfig = config.mergeBuildConfiguration(props);
        const compiler = webpack__default['default'](createConfig(buildConfig));
        run(compiler, (err, stats) => {
            var _a;
            const bundleName = path__default['default'].join(((_a = stats === null || stats === void 0 ? void 0 : stats.compilation.outputOptions) === null || _a === void 0 ? void 0 : _a.path) || '', typeof (stats === null || stats === void 0 ? void 0 : stats.compilation.outputOptions.filename) === 'string'
                ? stats.compilation.outputOptions.filename
                : '');
            const bailError = (error) => {
                fs__default['default'].writeFileSync(bundleName, `
module.exports = ${JSON.stringify({
                    stores: [],
                    packages: [],
                    components: [],
                    error,
                })}

          `);
                console.error(error);
                return resolve({ bundleName, stats: stats }); //reject(error);
            };
            if (err) {
                return bailError(err.toString());
            }
            if ((stats === null || stats === void 0 ? void 0 : stats.hasErrors()) || (stats === null || stats === void 0 ? void 0 : stats.hasWarnings())) {
                const error = stats.toString({
                    errorDetails: true,
                    warnings: true,
                });
                return bailError(error);
            }
            const { store } = require('@component-controls/loader/store');
            if (store) {
                logger.log(`compiled ${store.stores.length} documents`, bundleName);
            }
            else {
                logger.error('error creating bundle', bundleName);
            }
            resolve({ bundleName, stats: stats, store });
        });
    });
};

/**
 * compile the stories with webpack
 * returns the stories store object
 */
const compile = async (_a, callback) => {
    var { logOptions } = _a, rest = tslib.__rest(_a, ["logOptions"]);
    logger.setLogOptions(logOptions);
    logger.log('start compilation', 'optimized build');
    const results = await runCompiler((compiler, callback) => compiler.run(callback), Object.assign(Object.assign({}, rest), { mode: 'production' }));
    if (callback) {
        await callback(results);
    }
    return results;
};
/**
 * compile the stories with webpack and launch watching for changes
 * returns the stories store object
 */
const watch = async (_a, callback) => {
    var { watchOptions, logOptions } = _a, rest = tslib.__rest(_a, ["watchOptions", "logOptions"]);
    logger.setLogOptions(logOptions);
    logger.log('start compilation', 'development mode watch');
    const results = await runCompiler((compiler, callback) => compiler.watch(Object.assign({}, watchOptions), callback), Object.assign(Object.assign({}, rest), { mode: 'development' }));
    if (callback) {
        await callback(results);
    }
    return results;
};
const searchIndexing = async (store) => {
    const { search } = store.config;
    if (search === null || search === void 0 ? void 0 : search.indexingModule) {
        const createSearchIndex = require(search.indexingModule);
        await createSearchIndex(store);
    }
};

exports.cliArgs = cliArgs;
exports.compile = compile;
exports.defaultCliArgs = defaultCliArgs;
exports.getBundleName = getBundleName;
exports.searchIndexing = searchIndexing;
exports.watch = watch;
