"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
exports.default = resolveConfig;
var _negateValue = _interopRequireDefault(require("./negateValue"));
var _corePluginList = _interopRequireDefault(require("../corePluginList"));
var _configurePlugins = _interopRequireDefault(require("./configurePlugins"));
var _defaultConfigStub = _interopRequireDefault(require("../../stubs/defaultConfig.stub"));
var _colors = _interopRequireDefault(require("../public/colors"));
var _defaults = require("./defaults");
var _toPath = require("./toPath");
var _normalizeConfig = require("./normalizeConfig");
var _isPlainObject = _interopRequireDefault(require("./isPlainObject"));
var _cloneDeep = require("./cloneDeep");
function _interopRequireDefault(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
function isFunction(input) {
    return typeof input === 'function';
}
function isObject(input) {
    return typeof input === 'object' && input !== null;
}
function mergeWith(target, ...sources) {
    let customizer = sources.pop();
    for (let source of sources){
        for(let k in source){
            let merged = customizer(target[k], source[k]);
            if (merged === undefined) {
                if (isObject(target[k]) && isObject(source[k])) {
                    target[k] = mergeWith(target[k], source[k], customizer);
                } else {
                    target[k] = source[k];
                }
            } else {
                target[k] = merged;
            }
        }
    }
    return target;
}
const configUtils = {
    colors: _colors.default,
    negative (scale) {
        // TODO: Log that this function isn't really needed anymore?
        return Object.keys(scale).filter((key)=>scale[key] !== '0'
        ).reduce((negativeScale, key)=>{
            let negativeValue = (0, _negateValue).default(scale[key]);
            if (negativeValue !== undefined) {
                negativeScale[`-${key}`] = negativeValue;
            }
            return negativeScale;
        }, {
        });
    },
    breakpoints (screens) {
        return Object.keys(screens).filter((key)=>typeof screens[key] === 'string'
        ).reduce((breakpoints, key)=>({
                ...breakpoints,
                [`screen-${key}`]: screens[key]
            })
        , {
        });
    }
};
function value(valueToResolve, ...args) {
    return isFunction(valueToResolve) ? valueToResolve(...args) : valueToResolve;
}
function collectExtends(items) {
    return items.reduce((merged, { extend  })=>{
        return mergeWith(merged, extend, (mergedValue, extendValue)=>{
            if (mergedValue === undefined) {
                return [
                    extendValue
                ];
            }
            if (Array.isArray(mergedValue)) {
                return [
                    extendValue,
                    ...mergedValue
                ];
            }
            return [
                extendValue,
                mergedValue
            ];
        });
    }, {
    });
}
function mergeThemes(themes) {
    return {
        ...themes.reduce((merged, theme)=>(0, _defaults).defaults(merged, theme)
        , {
        }),
        // In order to resolve n config objects, we combine all of their `extend` properties
        // into arrays instead of objects so they aren't overridden.
        extend: collectExtends(themes)
    };
}
function mergeExtensionCustomizer(merged, value1) {
    // When we have an array of objects, we do want to merge it
    if (Array.isArray(merged) && isObject(merged[0])) {
        return merged.concat(value1);
    }
    // When the incoming value is an array, and the existing config is an object, prepend the existing object
    if (Array.isArray(value1) && isObject(value1[0]) && isObject(merged)) {
        return [
            merged,
            ...value1
        ];
    }
    // Override arrays (for example for font-families, box-shadows, ...)
    if (Array.isArray(value1)) {
        return value1;
    }
    // Execute default behaviour
    return undefined;
}
function mergeExtensions({ extend , ...theme }) {
    return mergeWith(theme, extend, (themeValue, extensions)=>{
        // The `extend` property is an array, so we need to check if it contains any functions
        if (!isFunction(themeValue) && !extensions.some(isFunction)) {
            return mergeWith({
            }, themeValue, ...extensions, mergeExtensionCustomizer);
        }
        return (resolveThemePath, utils)=>mergeWith({
            }, ...[
                themeValue,
                ...extensions
            ].map((e)=>value(e, resolveThemePath, utils)
            ), mergeExtensionCustomizer)
        ;
    });
}
function resolveFunctionKeys(object) {
    const resolvePath = (key, defaultValue)=>{
        const path = (0, _toPath).toPath(key);
        let index = 0;
        let val = object;
        while(val !== undefined && val !== null && index < path.length){
            val = val[path[index++]];
            val = isFunction(val) ? val(resolvePath, configUtils) : val;
        }
        if (val === undefined) {
            return defaultValue;
        }
        if ((0, _isPlainObject).default(val)) {
            return (0, _cloneDeep).cloneDeep(val);
        }
        return val;
    };
    resolvePath.theme = resolvePath;
    for(let key1 in configUtils){
        resolvePath[key1] = configUtils[key1];
    }
    return Object.keys(object).reduce((resolved, key)=>{
        return {
            ...resolved,
            [key]: isFunction(object[key]) ? object[key](resolvePath, configUtils) : object[key]
        };
    }, {
    });
}
function extractPluginConfigs(configs) {
    let allConfigs = [];
    configs.forEach((config)=>{
        allConfigs = [
            ...allConfigs,
            config
        ];
        var ref1;
        const plugins = (ref1 = config === null || config === void 0 ? void 0 : config.plugins) !== null && ref1 !== void 0 ? ref1 : [];
        if (plugins.length === 0) {
            return;
        }
        plugins.forEach((plugin)=>{
            if (plugin.__isOptionsFunction) {
                plugin = plugin();
            }
            var ref;
            allConfigs = [
                ...allConfigs,
                ...extractPluginConfigs([
                    (ref = plugin === null || plugin === void 0 ? void 0 : plugin.config) !== null && ref !== void 0 ? ref : {
                    }
                ])
            ];
        });
    });
    return allConfigs;
}
function resolveCorePlugins(corePluginConfigs) {
    const result = [
        ...corePluginConfigs
    ].reduceRight((resolved, corePluginConfig)=>{
        if (isFunction(corePluginConfig)) {
            return corePluginConfig({
                corePlugins: resolved
            });
        }
        return (0, _configurePlugins).default(corePluginConfig, resolved);
    }, _corePluginList.default);
    return result;
}
function resolvePluginLists(pluginLists) {
    const result = [
        ...pluginLists
    ].reduceRight((resolved, pluginList)=>{
        return [
            ...resolved,
            ...pluginList
        ];
    }, []);
    return result;
}
function resolveConfig(configs) {
    let allConfigs = [
        ...extractPluginConfigs(configs),
        {
            prefix: '',
            important: false,
            separator: ':',
            variantOrder: _defaultConfigStub.default.variantOrder
        }, 
    ];
    var ref, ref2;
    return (0, _normalizeConfig).normalizeConfig((0, _defaults).defaults({
        theme: resolveFunctionKeys(mergeExtensions(mergeThemes(allConfigs.map((t)=>{
            return (ref = t === null || t === void 0 ? void 0 : t.theme) !== null && ref !== void 0 ? ref : {
            };
        })))),
        corePlugins: resolveCorePlugins(allConfigs.map((c)=>c.corePlugins
        )),
        plugins: resolvePluginLists(configs.map((c)=>{
            return (ref2 = c === null || c === void 0 ? void 0 : c.plugins) !== null && ref2 !== void 0 ? ref2 : [];
        }))
    }, ...allConfigs));
}
