let path = require('path');
const webpack = require('webpack');
const ThemeColorReplacer = require('webpack-theme-color-replacer');
const globalConfig = require('./src/config/global.config');
const settingConfig = require('./src/config/setting.config');
const cssResolverConfig = require('./src/config/cssResolver.config');
const {
    getMenuColors,
    getAntdColors,
    getThemeToggleColors,
    getFunctionalColors,
} = require('./src/utils/colorUtil');

const CompressionWebpackPlugin = require('compression-webpack-plugin');

const productionGzipExtensions = ['js', 'css'];
const isProd = process.env.NODE_ENV === 'production';

const theme = settingConfig.theme;

function getThemeColors(color, $theme) {
    const _color = color || theme.color;
    const mode = $theme || theme.mode;
    const replaceColors = getThemeToggleColors(_color, mode);
    const themeColors = [
        ...replaceColors.mainColors,
        ...replaceColors.subColors,
        ...replaceColors.menuColors,
        ...replaceColors.contentColors,
        ...replaceColors.rgbColors,
        ...replaceColors.functionalColors.success,
        ...replaceColors.functionalColors.warning,
        ...replaceColors.functionalColors.error,
    ];
    return themeColors;
}

function modifyVars(color) {
    let _color = color || theme.color;
    const palettes = getAntdColors(_color, theme.mode);
    const menuColors = getMenuColors(_color, theme.mode);
    const { success, warning, error } = getFunctionalColors(theme.mode);
    const primary = palettes[5];
    return {
        'primary-color': primary,
        'primary-1': palettes[0],
        'primary-2': palettes[1],
        'primary-3': palettes[2],
        'primary-4': palettes[3],
        'primary-5': palettes[4],
        'primary-6': palettes[5],
        'primary-7': palettes[6],
        'primary-8': palettes[7],
        'primary-9': palettes[8],
        'primary-10': palettes[9],
        'info-color': primary,
        'success-color': success[5],
        'warning-color': warning[5],
        'error-color': error[5],
        'alert-info-bg-color': palettes[0],
        'alert-info-border-color': palettes[2],
        'alert-success-bg-color': success[0],
        'alert-success-border-color': success[2],
        'alert-warning-bg-color': warning[0],
        'alert-warning-border-color': warning[2],
        'alert-error-bg-color': error[0],
        'alert-error-border-color': error[2],
        'processing-color': primary,
        'menu-dark-submenu-bg': menuColors[0],
        'layout-header-background': menuColors[1],
        'layout-trigger-background': menuColors[2],
        'btn-danger-bg': error[4],
        'btn-danger-border': error[4],
        ...globalConfig.theme[theme.mode],
    };
}

// 修正 webpack-theme-color-replacer 插件提取的 css 结果
function resolveCss(output, srcArr) {
    let regExps = [];
    // 提取 resolve 配置中所有的正则配置
    Object.keys(cssResolverConfig).forEach(key => {
        let isRegExp = false;
        let reg = {};
        try {
            reg = eval(key);
            isRegExp = reg instanceof RegExp;
        } catch (e) {
            isRegExp = false;
        }
        if (isRegExp) {
            regExps.push([reg, cssResolverConfig[key]]);
        }
    });

    // 去重
    srcArr = dropDuplicate(srcArr);

    // 处理 css
    let outArr = [];
    srcArr.forEach(text => {
        // 转换为 css 对象
        let cssObj = parseCssObj(text);
        // 根据selector匹配配置,匹配成功,则按配置处理 css
        if (cssResolverConfig[cssObj.selector] != undefined) {
            let cfg = cssResolverConfig[cssObj.selector];
            if (cfg) {
                outArr.push(cfg.resolve(text, cssObj));
            }
        } else {
            let cssText = '';
            // 匹配不成功,则测试是否有匹配的正则配置,有则按正则对应的配置处理
            for (let regExp of regExps) {
                if (regExp[0].test(cssObj.selector)) {
                    let cssCfg = regExp[1];
                    cssText = cssCfg ? cssCfg.resolve(text, cssObj) : '';
                    break;
                }
                // 未匹配到正则,则设置 cssText 为默认的 css(即不处理)
                cssText = text;
            }
            if (cssText != '') {
                outArr.push(cssText);
            }
        }
    });
    output = outArr.join('\n');
    return output;
}

// 数组去重
function dropDuplicate(arr) {
    let map = {};
    let r = [];
    for (let s of arr) {
        if (!map[s]) {
            r.push(s);
            map[s] = 1;
        }
    }
    return r;
}

/**
 * 从字符串解析 css 对象
 * @param cssText
 * @returns {{
 *   name: String,
 *   rules: Array[String],
 *   toText: function
 * }}
 */
function parseCssObj(cssText) {
    let css = {};
    const ruleIndex = cssText.indexOf('{');
    css.selector = cssText.substring(0, ruleIndex);
    const ruleBody = cssText.substring(ruleIndex + 1, cssText.length - 1);
    const rules = ruleBody.split(';');
    css.rules = rules;
    css.toText = function() {
        let body = '';
        this.rules.forEach(item => {
            body += item + ';';
        });
        return `${this.selector}{${body}}`;
    };
    return css;
}

const assetsCDN = {
    // webpack build externals cdn预加载使用
    externals: {
        vue: 'Vue',
        'vue-router': 'VueRouter',
        vuex: 'Vuex',
        axios: 'axios',
        nprogress: 'NProgress',
        clipboard: 'ClipboardJS',
        '@antv/data-set': 'DataSet',
    },
    css: [],
    js: [
        '//cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js',
        '//cdn.jsdelivr.net/npm/vue-router@3.3.4/dist/vue-router.min.js',
        '//cdn.jsdelivr.net/npm/vuex@3.4.0/dist/vuex.min.js',
        '//cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js',
        '//cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.js',
        '//cdn.jsdelivr.net/npm/clipboard@2.0.6/dist/clipboard.min.js',
        '//cdn.jsdelivr.net/npm/@antv/data-set@0.11.4/build/data-set.min.js',
    ],
};

module.exports = {
    devServer: {
        proxy: {
            '/api': {
                //此处要与 /services/api.js 中的 API_PROXY_PREFIX 值保持一致
                // target: process.env.VUE_APP_API_BASE_URL,
                target: 'http://platform.kuopu.net:9300',
                changeOrigin: true,
                // pathRewrite: {
                //   '^/api': ''
                // }
            },
        },
    },
    pluginOptions: {
        /**
         * 导入css 预处理器的一些公共的样式文件变量,比如:variables , mixins , functions,
         * 避免在每个样式文件中手动的@import导入,然后在各个css 文件中直接使用 变量。
         *
         * theme.less 定义的变量预先导入 直接使用
         */
        'style-resources-loader': {
            preProcessor: 'less',
            //这个是绝对路径,不能使用 alias中配置的别名路径,如@表示的src
            patterns: [path.resolve(__dirname, './src/theme/default.less')],
        },
    },
    configureWebpack: config => {
        config.entry.app = ['babel-polyfill', 'whatwg-fetch', './src/main.js'];
        config.performance = {
            hints: false,
        };
        config.plugins.push(
            new ThemeColorReplacer({
                //Optional. output css file name, suport [contenthash] and [hash].
                fileName: 'css/theme-colors-[contenthash:8].css',
                //matchColors: Array<string> Colors array for extracting css file.
                //Css rules which have any one of these colors will be extracted out.
                //抽取所有相关颜色做替换
                matchColors: getThemeColors(),
                //injectCss: boolean Optional. Inject css text into js file, no need to download theme-colors-xxx.css any more.
                injectCss: true,
                //resolveCss: Function(resultCss : string) : string Optional. Resolve result css code as you wish.
                //转换原有CSS中的特定样式为自定义样式
                resolveCss,
            }),
        );
        // Ignore all locale files of moment.js
        config.plugins.push(new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/));
        // 生产环境下将资源压缩成gzip格式
        if (isProd) {
            // add `CompressionWebpack` plugin to webpack plugins
            config.plugins.push(
                new CompressionWebpackPlugin({
                    algorithm: 'gzip',
                    test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
                    threshold: 10240,
                    minRatio: 0.8,
                }),
            );
        }
        // if prod, add externals
        if (isProd) {
            config.externals = assetsCDN.externals;
        }
    },
    chainWebpack: config => {
        // 生产环境下关闭css压缩的 colormin 项,因为此项优化与主题色替换功能冲突
        if (isProd) {
            config.plugin('optimize-css').tap(args => {
                args[0].cssnanoOptions.preset[1].colormin = false;
                return args;
            });
        }
        // 生产环境下使用CDN
        if (isProd) {
            config.plugin('html').tap(args => {
                args[0].cdn = assetsCDN;
                return args;
            });
        }
    },
    css: {
        loaderOptions: {
            less: {
                lessOptions: {
                    modifyVars: modifyVars(),
                    javascriptEnabled: true,
                },
            },
            postcss: {
                plugins: [require('tailwindcss')],
            },
        },
    },
    publicPath: process.env.VUE_APP_PUBLIC_PATH,
    outputDir: 'dist',
    assetsDir: 'static',
    productionSourceMap: false,
};