/* wp:polyfill */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.filterObjectByProperties = void 0;
exports.isVariationWithProperties = isVariationWithProperties;
exports.removePropertiesFromObject = removePropertiesFromObject;
exports.useCurrentMergeThemeStyleVariationsWithUserConfig = useCurrentMergeThemeStyleVariationsWithUserConfig;
var _data = require("@wordpress/data");
var _coreData = require("@wordpress/core-data");
var _element = require("@wordpress/element");
var _blockEditor = require("@wordpress/block-editor");
var _editor = require("@wordpress/editor");
var _i18n = require("@wordpress/i18n");
var _lockUnlock = require("../../lock-unlock");
/**
 * WordPress dependencies
 */

/**
 * Internal dependencies
 */

const EMPTY_ARRAY = [];
const {
  GlobalStylesContext,
  areGlobalStyleConfigsEqual
} = (0, _lockUnlock.unlock)(_blockEditor.privateApis);
const {
  mergeBaseAndUserConfigs
} = (0, _lockUnlock.unlock)(_editor.privateApis);

/**
 * Removes all instances of properties from an object.
 *
 * @param {Object}   object     The object to remove the properties from.
 * @param {string[]} properties The properties to remove.
 * @return {Object} The modified object.
 */
function removePropertiesFromObject(object, properties) {
  if (!properties?.length) {
    return object;
  }
  if (typeof object !== 'object' || !object || !Object.keys(object).length) {
    return object;
  }
  for (const key in object) {
    if (properties.includes(key)) {
      delete object[key];
    } else if (typeof object[key] === 'object') {
      removePropertiesFromObject(object[key], properties);
    }
  }
  return object;
}

/**
 * Checks whether a style variation is empty.
 *
 * @param {Object} variation          A style variation object.
 * @param {string} variation.title    The title of the variation.
 * @param {Object} variation.settings The settings of the variation.
 * @param {Object} variation.styles   The styles of the variation.
 * @return {boolean} Whether the variation is empty.
 */
function hasThemeVariation({
  title,
  settings,
  styles
}) {
  return title === (0, _i18n.__)('Default') ||
  // Always preserve the default variation.
  Object.keys(settings).length > 0 || Object.keys(styles).length > 0;
}

/**
 * Fetches the current theme style variations that contain only the specified properties
 * and merges them with the user config.
 *
 * @param {string[]} properties The properties to filter by.
 * @return {Object[]|*} The merged object.
 */
function useCurrentMergeThemeStyleVariationsWithUserConfig(properties = []) {
  const {
    variationsFromTheme
  } = (0, _data.useSelect)(select => {
    const _variationsFromTheme = select(_coreData.store).__experimentalGetCurrentThemeGlobalStylesVariations();
    return {
      variationsFromTheme: _variationsFromTheme || EMPTY_ARRAY
    };
  }, []);
  const {
    user: userVariation
  } = (0, _element.useContext)(GlobalStylesContext);
  const propertiesAsString = properties.toString();
  return (0, _element.useMemo)(() => {
    const clonedUserVariation = structuredClone(userVariation);

    // Get user variation and remove the settings for the given property.
    const userVariationWithoutProperties = removePropertiesFromObject(clonedUserVariation, properties);
    userVariationWithoutProperties.title = (0, _i18n.__)('Default');
    const variationsWithPropertiesAndBase = variationsFromTheme.filter(variation => {
      return isVariationWithProperties(variation, properties);
    }).map(variation => {
      return mergeBaseAndUserConfigs(userVariationWithoutProperties, variation);
    });
    const variationsByProperties = [userVariationWithoutProperties, ...variationsWithPropertiesAndBase];

    /*
     * Filter out variations with no settings or styles.
     */
    return variationsByProperties?.length ? variationsByProperties.filter(hasThemeVariation) : [];
  }, [propertiesAsString, userVariation, variationsFromTheme]);
}

/**
 * Returns a new object, with properties specified in `properties` array.,
 * maintain the original object tree structure.
 * The function is recursive, so it will perform a deep search for the given properties.
 * E.g., the function will return `{ a: { b: { c: { test: 1 } } } }` if the properties are  `[ 'test' ]`.
 *
 * @param {Object}   object     The object to filter
 * @param {string[]} properties The properties to filter by
 * @return {Object} The merged object.
 */
const filterObjectByProperties = (object, properties) => {
  if (!object || !properties?.length) {
    return {};
  }
  const newObject = {};
  Object.keys(object).forEach(key => {
    if (properties.includes(key)) {
      newObject[key] = object[key];
    } else if (typeof object[key] === 'object') {
      const newFilter = filterObjectByProperties(object[key], properties);
      if (Object.keys(newFilter).length) {
        newObject[key] = newFilter;
      }
    }
  });
  return newObject;
};

/**
 * Compares a style variation to the same variation filtered by the specified properties.
 * Returns true if the variation contains only the properties specified.
 *
 * @param {Object}   variation  The variation to compare.
 * @param {string[]} properties The properties to compare.
 * @return {boolean} Whether the variation contains only the specified properties.
 */
exports.filterObjectByProperties = filterObjectByProperties;
function isVariationWithProperties(variation, properties) {
  const variationWithProperties = filterObjectByProperties(structuredClone(variation), properties);
  return areGlobalStyleConfigsEqual(variationWithProperties, variation);
}
//# sourceMappingURL=use-theme-style-variations-by-property.js.map