import { normalizeString, lowerFirst } from './stringTools';

const TESTID_ATTR = 'data-testid';
const E2EID_ATTR = 'data-e2e';
const ELEMENT_CONNECTOR = '_';
const MODIFIER_CONNECTOR = '--';
const TEST_CONNECTOR = ':';

/**
 * generating `className`, `data-testid` and `data-e2e` strings based on a common syntax
 */
const wrapProps = currentProps => {
    const { root, classNames, element, testId, e2eId, style } = currentProps;

    const withClass = otherClassNames => {
        if (otherClassNames) {
            const newProps = {
                ...currentProps,
                classNames: currentProps.classNames.concat(otherClassNames)
            };
            return wrapProps(newProps);
        }
        return wrapProps(currentProps);
    };

    const withTestId = customTestId => {
        const testSuffix = normalizeString(customTestId || element || 'testId');
        const newProps = {
            ...currentProps,
            testId: [root, testSuffix].join(TEST_CONNECTOR)
        };
        return wrapProps(newProps);
    };

    const withE2EId = customE2EId => {
        const testSuffix = normalizeString(customE2EId || element || 'e2eId');
        const newProps = {
            ...currentProps,
            e2eId: [root, testSuffix].join(TEST_CONNECTOR)
        };
        return wrapProps(newProps);
    };

    /**
    adds css variables to the `style` props, prepending them with the mandatory `--` css prefix
    which makes it intentionally not working with direct css properties (e.g. color: #fff)
    */
    const withVariables = variables => {
        // if no variables passed, leave unchanged
        if (!variables) {
            return wrapProps(currentProps);
        }
        const parsedVariables = Object.keys(variables).reduce((carry, varName) => {
            const cssVarName = varName.slice(0, 2) === '--' ? varName : `--${varName}`;
            return {
                ...carry,
                [cssVarName]: variables[varName]
            };
        }, {});

        const newProps = {
            ...currentProps,
            style: Object.assign({}, style, parsedVariables)
        };
        return wrapProps(newProps);
    };

    return {
        props: Object.assign(
            {},
            classNames.length && {
                className: classNames.join(' ')
            },
            testId && {
                [TESTID_ATTR]: testId
            },
            e2eId && {
                [E2EID_ATTR]: e2eId
            },
            style && { style },
        ),
        withClass,
        withTestId,
        withE2EId,
        withVariables
    };
};

const namespaceGenerator = namespace => (element, modifier) => {
    const classNames = [];
    const root = lowerFirst(namespace);
    const elementClass = [root, element].join(ELEMENT_CONNECTOR);

    if (element) {
        classNames.push(elementClass);
    }

    if (modifier) {
        const modifiers = Array.isArray(modifier) ? modifier : [modifier];
        modifiers.forEach(modifierItem => {
            if (modifierItem) {
                const modifierClass = [elementClass, modifierItem].join(MODIFIER_CONNECTOR);
                classNames.push(modifierClass);
            }
        });
    }

    const props = {
        element,
        classNames,
        root
    };

    return wrapProps(props);
};
namespaceGenerator.TESTID_ATTR = TESTID_ATTR;
namespaceGenerator.E2EID_ATTR = E2EID_ATTR;
namespaceGenerator.ELEMENT_CONNECTOR = ELEMENT_CONNECTOR;
namespaceGenerator.MODIFIER_CONNECTOR = MODIFIER_CONNECTOR;
namespaceGenerator.TEST_CONNECTOR = TEST_CONNECTOR;

export const createNamespace = namespaceGenerator;
