// Map Utilities
// =============

@use 'config';
@use 'json-api' as *;
@use 'sass:list';
@use 'sass:map';
@use 'sass:meta';

// Herman Add
// ----------
/// Add a map of colors, fonts, sizes, ratios, etc
/// to the `$herman` map for JSON-export,
/// converting values to JSON-ready strings.
///
/// @group api_json-export
/// @since 4.0.0
///   Name changed from `herman-add`
/// @since 4.1.0
///   Data supplied to `utilities.add()`
///   is deeply merged instead of overridden
/// @since 5.0.0
///   No longer accepts map-compile arguments,
///   since there is not an obvious default between
///   running compilation on keys vs values.
///   Use `each-value` or `each-key` to handle compilations manually.
///
/// @param {String} $type -
///   The type of map being added,
///   e.g. `colors`, `fonts`,
///   `sizes`, or `ratios`.
/// @param {String} $key -
///   A key name for accessing this data in JSON --
///   should match the variable name,
///   unless `key` is otherwise set in the `@font`/`@colors`/`@ratios`/`@sizes` annotation
/// @param {Map} $map -
///   A map of name/value pairs
/// @param {Arglist} $args… -
///   A function to use for compiling values before export,
///   and any additional arguments for the function
/// @output
///   Updated `$herman` map, ready for JSON export
/// @example scss
///   @use 'sass:meta';
///   @use 'pkg:accoutrement' as tools;
///   // To import from Herman installed via npm,
///   // replace the following 'utilities' import with:
///   // @use 'pkg:sassdoc-theme-herman' as herman;
///   @use 'utilities' as herman;
///
///   herman.$herman: ();
///   $brand-colors: (
///     'brand-blue': hsl(195, 85%, 35%),
///     'light-gray': '#brand-blue' ('tint': 80%, 'adjust': ('saturation': -80%, 'space': hsl),),
///   );
///   $brand-compiled: herman.each-key(
///     $brand-colors,
///     meta.get-function('color', $module: 'tools'),
///   );
///   @include herman.add('colors', 'brand-colors', $brand-compiled);
///
///   @each $key, $value in herman.$herman {
///   /* #{$key}: #{meta.inspect($value)} */
///   }
@mixin add($type, $key, $map) {
  // pluralize type
  $type: map.get(config.$plural-types, $type) or $type;

  // Update nested map by type
  $nested: map.get($herman, $type) or ();
  $nested: map.merge(
    $nested,
    (
      $key: $map,
    )
  );
  $nested: (
    $type: $nested,
  );

  // Merge nested map back into $herman
  $herman: map.deep-merge($herman, $nested) !global;
}

// Each Value
// ----------
/// Pass the values of any map through a given function
/// (with optional arguments)
/// and return the compiled map.
/// This is used by `add`,
/// but can also be accessed directly.
///
/// @group api_json-export
/// @since 4.0.0
///   Name changed from `herman-map-compile` to `compile`
/// @since 5.0.0
///   Name changed from `compile` to `each-value`
///
/// @param {Map} $map -
///   A sass map with values that need to be compiled,
///   by running each value through a given function.
/// @param {String | Function} $function -
///   The function (or function name) to use in compiling values,
///   such as Accoutrement `color` and `size` functions
/// @param {Arglist} $args… -
///   Pass in any additional arguments for the function
/// @return {String | Any} -
///   An updated map,
///   with values compiled by a third-party function,
///   and converted to json-ready strings
@function each-value($map, $function, $args...) {
  $output: ();

  @each $key, $value in $map {
    $output: map.merge(
      $output,
      (
        $key: meta.call($function, $value, $args...),
      )
    );
  }

  @return $output;
}

// Each Key
// --------
/// Pass the keys of any map through a given function
/// (with optional arguments)
/// and return the compiled map.
/// This is used by `add`,
/// but can also be accessed directly.
///
/// @group api_json-export
/// @since 4.0.0
///   Name changed from `herman-map-compile` to `compile`
/// @since 5.0.0
///   Name changed from `compile` to `each-value`
///
/// @param {Map} $map -
///   A sass map with values that need to be compiled,
///   by running each key through a given function.
/// @param {String | Function} $function -
///   The function (or function name) to use in compiling values,
///   such as Accoutrement `color` and `size` functions
/// @param {Arglist} $args… -
///   Pass in any additional arguments for the function
/// @return {String | Any} -
///   An updated map,
///   with values compiled by a third-party function,
///   and converted to json-ready strings
/// @example scss
///   @use 'sass:meta';
///   @use 'pkg:accoutrement' as tools;
///   // To import from Herman installed via npm,
///   // replace the following 'utilities' import with:
///   // @use 'pkg:sassdoc-theme-herman' as herman;
///   @use 'utilities' as herman;
///
///   $brand-colors: (
///     'brand-orange': hsl(24, 100%, 39%),
///     'brand-blue': hsl(195, 85%, 35%),
///     'light-gray': 'brand-blue' ('tint': 80%, 'adjust': ('saturation': -80%, 'space': hsl),),
///   );
///   $compiled: herman.each-key($brand-colors, meta.get-function('color', $module: 'tools'));
///
///   @each $key, $value in $compiled {
///   /* #{$key}: #{$value} */
///   }
@function each-key($map, $function, $args...) {
  $output: ();

  @each $key in map.keys($map) {
    $output: map.merge(
      $output,
      (
        $key: meta.call($function, $key, $args...),
      )
    );
  }

  @return $output;
}
