function cartesian(array) {
    function* cartesian(head, ...tail) {
        let remainder = tail.length ? cartesian(...tail) : [[]]
        for (let r of remainder) for (let h of head) yield [h, ...r]
    }

    return [...cartesian(...array)]
}

function deepKeys(object, separator = '.', prefix = '') {
    return Object.keys(object).reduce((result, key) => {
        if (Array.isArray(object[key])) {
            return [...result, prefix + key];
        } else if (typeof object[key] === 'object' && object[key] !== null) {
            return [...result, ...deepKeys(object[key], separator, prefix + key + separator)];
        }

        return [...result, prefix + key];
  }, []);
}

const extractTokens = pattern =>
    pattern.split(/(?={[^}]+})|(?<={[^}]+})/)

const expandTokens = theme => tokens => {
    return tokens.map(token => {
        if(token.startsWith('{')) {
            const cleanToken = token.replace(/{|}/g, '')
            if(cleanToken.includes('.')) {
                const color = cleanToken.split('.')[1]
                const colorToken = {
                    [color]: theme(cleanToken, {})
                }
                return deepKeys(colorToken, '-')
            }
            return deepKeys(theme(cleanToken, {}), '-')
        } else {
            return [token]
        }
    })
}
    

const mapToClasses = expanded =>
    expanded.map(values =>
        values.join('').replace('-DEFAULT', ''))

module.exports = theme => patterns => {
    return patterns                       // ["text-{gray}", …]
        .map(extractTokens)        // [["text", "{gray}"], …]
        .map(expandTokens(theme))  // [[["text"], ["gray-100", "gray-200",…]], …]
        .map(cartesian)            // [[["text", "gray-100"], ["text", "gray-200"], …], …]
        .flatMap(mapToClasses)     // ["text-gray-100", "text-gray-200",…]
}
    
