// We can't use lodash merge, because it skip the specified undefined value
// which we use to reset styles.
import compactObject from '../compact-object';
import { maybeDecode, maybeEncode } from '../encode-decode';
import { getMediaSizes } from '../get-media-sizes';
import merge from '../merge';

/**
 * Prepare and decode styles to work with.
 *
 * @param {Object} styles - styles object.
 *
 * @return {Object}
 */
export function prepareStyles(styles) {
	return maybeDecode(styles);
}

/**
 * Get style value by property name, selected device and optional selector.
 *
 * @param {Object} styles   - styles object.
 * @param {string} name     - style property name.
 * @param {string} device   - responsive device.
 * @param {string} selector - optional custom selector.
 *
 * @return {any}
 */
export function getStyle(styles, name, device = false, selector = false) {
	let result;
	let processStyles = prepareStyles(styles);

	if (device) {
		processStyles = processStyles?.[`media_${device}`];
	}

	if (selector) {
		processStyles = processStyles?.[selector];
	}

	if (typeof processStyles?.[name] !== 'undefined') {
		result = processStyles?.[name];
	}

	return result;
}

/**
 * Check if style exists.
 *
 * @param {Object} styles   - styles object.
 * @param {string} name     - style property name.
 * @param {string} device   - responsive device.
 * @param {string} selector - optional custom selector.
 *
 * @return {boolean}
 */
export function hasStyle(styles, name, device = false, selector = false) {
	return typeof getStyle(styles, name, device, selector) !== 'undefined';
}

/**
 * Set new styles.
 *
 * @param {Object} styles    - styles object.
 * @param {string} name      - style property name.
 * @param          newStyles
 * @param {string} device    - responsive device.
 * @param {string} selector  - optional custom selector.
 *
 * @return {boolean}
 */
export function getUpdatedStyles(
	styles,
	newStyles,
	device = false,
	selector = false
) {
	let result;

	if (selector) {
		newStyles = { [selector]: newStyles };
	}

	if (device) {
		result = {};
		result[`media_${device}`] = newStyles;
	} else {
		result = newStyles;
	}

	// Add default properties to keep sorting.
	result = merge(
		{
			media_xl: {},
			media_lg: {},
			media_md: {},
			media_sm: {},
		},
		maybeDecode(styles),
		result
	);

	const cleanResult = compactObject(result);

	return maybeEncode(cleanResult);
}

/**
 * Get object with styles to reset.
 *
 * @param {Array}    resetProps     - array with CSS props to reset.
 * @param {boolean}  withResponsive - reset responsive styles.
 * @param {Selector} selectors      - reset styles in custom selectors set.
 */
export function getStylesToReset(
	resetProps,
	withResponsive = false,
	selectors = ['']
) {
	const result = {};

	selectors.forEach((selector) => {
		if (selector) {
			result[selector] = {};
		}
	});

	['', ...(withResponsive ? Object.keys(getMediaSizes()) : [])].forEach(
		(thisDevice) => {
			if (thisDevice) {
				result[`media_${thisDevice}`] = {};

				selectors.forEach((selector) => {
					if (selector) {
						result[`media_${thisDevice}`][selector] = {};
					}
				});
			}

			resetProps.forEach((thisProp) => {
				selectors.forEach((selector) => {
					if (thisDevice) {
						if (selector) {
							result[`media_${thisDevice}`][selector][thisProp] =
								undefined;
						} else {
							result[`media_${thisDevice}`][thisProp] = undefined;
						}
					} else if (selector) {
						result[selector][thisProp] = undefined;
					} else {
						result[thisProp] = undefined;
					}
				});
			});
		}
	);

	return result;
}

/**
 * Get object with styles of specific props.
 *
 * @param {Object}   styles         - styles object.
 * @param {Array}    findProps      - array with CSS props to find.
 * @param {boolean}  withResponsive - reset responsive styles.
 * @param {Selector} selectors      - reset styles in custom selectors set.
 */
export function getSpecificPropsFromStyles(
	styles,
	findProps,
	withResponsive = false,
	selectors = ['']
) {
	// Firs of all, prepare reset styles.
	const result = getStylesToReset(findProps, withResponsive, selectors);

	const decodedStyles = maybeDecode(styles || {});

	['', ...(withResponsive ? Object.keys(getMediaSizes()) : [])].forEach(
		(thisDevice) => {
			findProps.forEach((thisProp) => {
				selectors.forEach((selector) => {
					if (thisDevice) {
						if (selector) {
							if (
								typeof decodedStyles?.[`media_${thisDevice}`]?.[
									selector
								]?.[thisProp] !== 'undefined'
							) {
								result[`media_${thisDevice}`][selector][
									thisProp
								] =
									decodedStyles[`media_${thisDevice}`][
										selector
									][thisProp];
							}
						} else if (
							typeof decodedStyles?.[`media_${thisDevice}`]?.[
								thisProp
							] !== 'undefined'
						) {
							result[`media_${thisDevice}`][thisProp] =
								decodedStyles[`media_${thisDevice}`][thisProp];
						}
					} else if (selector) {
						if (
							typeof decodedStyles?.[selector]?.[thisProp] !==
							'undefined'
						) {
							result[selector][thisProp] =
								decodedStyles[selector][thisProp];
						}
					} else if (
						typeof decodedStyles?.[thisProp] !== 'undefined'
					) {
						result[thisProp] = decodedStyles[thisProp];
					}
				});
			});
		}
	);

	return maybeEncode(result);
}
