/**
 * External dependencies
 */
import classnames from 'classnames';
import { paramCase as kebabCase } from 'change-case';
import { getCSSRules } from '@wordpress/style-engine';
import { isString, isObject } from '@germanized/types';

/**
 * Returns the inline styles to add depending on the style object
 *
 * @param {Object} styles Styles configuration.
 * @return {Object} Flattened CSS variables declaration.
 */
function getInlineStyles( styles = {} ) {
    const output = {};

    getCSSRules( styles, { selector: '' } ).forEach( ( rule ) => {
        output[ rule.key ] = rule.value;
    } );

    return output;
}

/**
 * Get the classname for a given color.
 */
function getColorClassName(
    colorContextName,
    colorSlug
) {
    if ( ! colorContextName || ! colorSlug ) {
        return '';
    }
    return `has-${ kebabCase( colorSlug ) }-${ colorContextName }`;
}

/**
 * Generates a CSS class name consisting of all the applicable border color
 * classes given the current block attributes.
 */
function getBorderClassName( attributes ) {
    const { borderColor, style } = attributes;
    const borderColorClass = borderColor
        ? getColorClassName( 'border-color', borderColor )
        : '';

    return classnames( {
        'has-border-color': !! borderColor || !! style?.border?.color,
        [ borderColorClass ]: !! borderColorClass,
    } );
}

function getGradientClassName( gradientSlug ) {
    if ( ! gradientSlug ) {
        return undefined;
    }
    return `has-${ gradientSlug }-gradient-background`;
}

/**
 * Provides the CSS class names and inline styles for a block's color support
 * attributes.
 */
export function getColorClassesAndStyles( props ) {
    const { backgroundColor, textColor, gradient, style } = props;

    // Collect color CSS classes.
    const backgroundClass = getColorClassName(
        'background-color',
        backgroundColor
    );
    const textClass = getColorClassName( 'color', textColor );

    const gradientClass = getGradientClassName( gradient );
    const hasGradient = gradientClass || style?.color?.gradient;

    // Determine color CSS class name list.
    const className = classnames( textClass, gradientClass, {
        // Don't apply the background class if there's a gradient.
        [ backgroundClass ]: ! hasGradient && !! backgroundClass,
        'has-text-color': textColor || style?.color?.text,
        'has-background':
            backgroundColor ||
            style?.color?.background ||
            gradient ||
            style?.color?.gradient,
        'has-link-color': isObject( style?.elements?.link )
            ? style?.elements?.link?.color
            : undefined,
    } );

    // Collect inline styles for colors.
    const colorStyles = style?.color || {};

    return {
        className,
        style: getInlineStyles( { color: colorStyles } ),
    };
}

/**
 * Provides the CSS class names and inline styles for a block's border support
 * attributes.
 */
export function getBorderClassesAndStyles( props ) {
    const border = props.style?.border || {};
    const className = getBorderClassName( props );

    return {
        className,
        style: getInlineStyles( { border } ),
    };
}

/**
 * Provides the CSS class names and inline styles for a block's spacing support
 * attributes.
 */
export function getSpacingClassesAndStyles( props ) {
    const spacingStyles = props.style?.spacing || {};
    const styleProp = getInlineStyles( { spacing: spacingStyles } );

    return {
        className: undefined,
        style: styleProp,
    };
}
