@use 'sass:map';
@use './config';
@use './normalize';
@use '../tokens/api';
@use '../tokens/inspect';
@use '../tokens/vars';

/// # Fonts » CSS Variables
/// @group type-font-vars

// Font Var Prefix
// ----------------
/// Since CSS variables are often defined in a global space,
/// it can be useful to prefix different variable types.
/// Accoutrement maps help solve that problem in Sass,
/// so we wanted to carry that over into our CSS variable support.
/// Set a prefix for all font-tokens,
/// and we'll apply it when setting or calling CSS variables
/// based on your font maps.
/// Set to `null` or `''` (empty string) for no prefix.
///
/// @since 2.0.0 -
/// - NEW: Initial release
///
/// @group type-font-vars
///
/// @example scss
/// tools.$fonts: (
///   'my-font': (
///     'stack': (arial, helvetica, sans-serif),
///    )
/// );
/// $font-var-prefix: 'prefix_';
/// html { @include tools.font--('my-font') }
///
/// @type string
$font-var-prefix: 'font-' !default;

// Fonts--
// --------
/// Convert any font-map into CSS variables,
/// using the global `$font-var-prefix`.
///
/// - Font names that start with `_` or `-` are considered "private"
///   and will not be output as variables
/// - Fonts that contain a simple alias with no adjustments
///   will be output with a `var()` value,
///   keeping the alias relationship intact
///
/// @since 2.0.0 -
/// - NEW: Initial release
///
/// @group type-font-vars
/// @example scss
///   $fonts: (
///     '_TestFont': (
///       'name': 'My Test Font',
///       'stack': ('Georgia', 'serif'),
///      ),
///     'text': '#_TestFont',
///     'sans': '#text',
///   );
///   html {
///     @include tools.fonts--;
///   }
///
/// @param {map} $source [$fonts] -
///   Optionally use a custom
///   map of fonts to set as CSS variables
/// @param {string | list} $use ['stack'] -
///   Font-map value (such as 'name' or 'stack')
///   to use as custom-property values
/// @output
///   Custom properties for all public fonts in the map
@mixin fonts--($source: config.$fonts, $use: 'stack') {
  $source: font-values($source, $use);

  @include vars.tokens--($source, $font-var-prefix);
}

// Font--
// -------
/// Set a single custom property based on a map-font,
/// with optional alias and fallback
///
/// @since 2.0.0 -
/// - NEW: Initial release
///
/// @group type-font-vars
/// @example scss
///   tools.$fonts: (
///     '_TestFont': (
///       'name': 'My Test Font',
///       'stack': ('Georgia', 'serif'),
///      ),
///     'text': '#_TestFont',
///     'sans': '#text',
///   );
///   .example {
///     @include tools.font--('text');
///     @include tools.font--('headline', 'sans', sans-serif);
///   }
///
/// @param {*} $font -
///   Font name available in the `$source` map,
///   or alias to use in naming the CSS variable
/// @param {string | null} $value [null] -
///   Optional value for the variable,
///   if different from the given `$font`
/// @param {*} $fallback [true] -
///   The optional fallback value for a `var()` function:
///   - `true` will generate a fallback based on the token value
///   - A token name will fallback to the value of that
///     CSS variable and then to the token's calculated value
///   - Any other fallback will be passed through unchanged
/// @param {string | list} $use ['stack'] -
///   Font-map value (such as 'name' or 'stack')
///   to use as custom-property values
/// @param {map} $source [$fonts] -
///   Optional map of fonts to use as a palette source
@mixin font--(
  $font,
  $value: null,
  $fallback: true,
  $source: config.$fonts,
  $use: 'stack'
) {
  $source: font-values($source, $use);

  @include vars.token--($source, $font, $value, $fallback, $font-var-prefix);
}

// Var Font
// ---------
/// Access the CSS variable associated with a given token,
/// along with a fallback value based on the token itself
///
/// @since 2.0.0 -
/// - NEW: Initial release
///
/// @group type-font-vars
/// @example scss
///   $fonts: (
///     '_TestFont': (
///       'name': 'My Test Font',
///       'stack': ('Georgia', 'serif'),
///      ),
///     'text': '#_TestFont',
///     'sans': '#text',
///   );
///   .example {
///     font-family: tools.var-font('sans', sans-serif);
///   }
///
/// @param {*} $font -
///   Font name available in the `$source` map
/// @param {*} $fallback [true] -
///   The optional fallback value for a `var()` function:
///   - `true` will generate a fallback based on the token value
///   - A token name will fallback to the value of that
///     CSS variable and then to the token's calculated value
///   - Any other fallback will be passed through unchanged
/// @param {map} $source [$fonts] -
///   Optional Accoutrement map of fonts to use as source
/// @param {string | list} $use ['stack'] -
///   Font-map value (such as 'name' or 'stack')
///   to use as custom-property values
/// @return {string} -
///   CSS variable call, in the format:
///   `var(--<token>, <fallback>)`
@function var-font(
  $font,
  $fallback: true,
  $source: config.$fonts,
  $use: 'stack'
) {
  $source: font-values($source, $use);

  @return vars.var-token($source, $font, $fallback, $font-var-prefix);
}

// Font Values
// -----------
/// Filter a complex map of fonts
/// into a simple map
/// based on one specific font value.
///
/// @access private
///
/// @param {map} $source [$fonts] -
///   Accoutrement map of fonts to use as source
/// @param {string | list} $value ['stack'] -
///   Font-map value to use as filter
/// @return {map} -
///   A map of font-keys that contain the requested value,
///   and aliases that refer to those fonts.
@function font-values($source: config.$fonts, $value: 'stack') {
  $return: ();

  @each $key, $raw in $source {
    $normal: api.get($source, $key);
    $normal: normalize.font($normal, $key, $source);
    $has: map.get($normal, $value);

    @if $has {
      @if inspect.is-alias-for($source, $key) {
        $return: map.merge(
          $return,
          (
            $key: $raw,
          )
        );
      } @else {
        $return: map.merge(
          $return,
          (
            $key: $has,
          )
        );
      }
    }
  }

  @return $return;
}
