////
/// @group cssvar
////
/// Provides support for custom-properties implementations

@use "sass:meta";
@use "sass:map";
@use "sass:string";
@use "sass:list";

@use "utils";
@use "breakpoint";

/// Module Settings
/// @type Map
/// @prop {String} prefix [""] Default prefix, will be added to all custom properties when using mixin or functions, unless overridden, set to empty quotes to disable

$config: (
  "prefix" : ""
) !default;

/// Change modules $config
/// @example scss Setting the prefix to 'ulu'
///   @include ulu.cssvar-set(( "prefix" : "ulu" ));
/// @param {Map} $changes Map of changes

@mixin set($changes) {
  $config: map.merge($config, $changes) !global;
}

/// Get a config option
/// @example scss Getting the config value for prefix
///   $prefix: ulu.cssvar-get("prefix");
/// @param {String} $name Name of property
/// @return {*} Map property value

@function get($name) {
  @return utils.require-map-get($config, $name, "css-vars [config]");
}

/// Get a custom property name (with optional prefix)
/// @example scss Getting a custom property name
///   #{ ulu.cssvar-name("base-color") } { ... }
/// @param {String} $name Name of custom property 
/// @param {String} $prefix [$config.prefix] Override default prefix
/// @return {String} The formatted property name (unquoted string)

@function name($name, $prefix: get("prefix")) {
  @if (string.length($prefix) > 0) {
    @return string.unquote("--#{ $prefix }-#{ $name }");
  } @else {
    @return string.unquote("--#{ $name }");
  }
}

/// Function to use a custom property within a declaration value 
/// @example scss Print an custom property as a value
///   .test {
///     color: ulu.cssvar-use("base-color");
///   }
/// @param {String} $name Name of custom property 
/// @param {String} $default-value [null] Provide a default value for var()
/// @param {String} $prefix [$config.prefix] Override default prefix
/// @return {String} Formatted custom property for use in property value (ie. var(...))

@function use($name, $default-value: null, $prefix: get("prefix")) {
  @if ($default-value) {
    @return var(name($name, $prefix), $default-value);
  } @else {
    @return var(name($name, $prefix));
  }
}

/// Outputs a single custom property declaration
/// @example scss Declare a custom property
///   :root {
///     @include ulu.cssvar-declare("base-color", red);
///   }
/// @param {String} $name Name of property
/// @param {*} $value The properties value to declare. Can be a literal or a configuration map with `value` and `breakpoints` keys.
/// @param {String} $prefix [$config.prefix] Override default prefix

@mixin declare($name, $value, $prefix: get("prefix")) {
  @if meta.type-of($value) == "map" {
    $base-value: map.get($value, "value");
    $breakpoints: map.get($value, "breakpoints");
    
    @if $base-value != null {
      #{ name($name, $prefix) } : #{ $base-value };
    }
    
    @if $breakpoints and meta.type-of($breakpoints) == "map" {
      @include breakpoint.from-each($breakpoints) using ($b-props) {
        $b-value: map.get($b-props, "value");
        @if $b-value != null {
          #{ name($name, $prefix) } : #{ $b-value };
        }
      }
    }
  } @else {
    #{ name($name, $prefix) } : #{ $value };
  }
}

/// Outputs a map as custom properties. Supports mapping values to breakpoints.
/// @example scss Declare each property in a map as a custom property
///   :root {
///     @include ulu.cssvar-declare-all((
///       "base-color" : red,
///       "responsive-color" : (
///         "value" : blue,
///         "breakpoints" : (
///           "medium" : (
///             "direction" : "up",
///             "value" : green
///           )
///         )
///       )
///     ));
///   }
/// @param {Map} $props Properties to declare. Values can be literal or a configuration map with `value` and `breakpoints` keys.
/// @param {String} $prefix [$config.prefix] Override default prefix

@mixin declare-all($props, $prefix: get("prefix")) {
  @each $name, $value in $props {
    @if $value != null {
      @include declare($name, $value, $prefix);
    }
  }
}

/// Declare a custom property for current breakpoint
/// @example scss Declare each property in a map as a custom property
///   :root {
///     @include ulu.cssvar-declare-breakpoint();
///   }
/// @param {Map} $breakpoints [breakpoint.get-sizes()] Breakpoints to declare
/// @param {String} $name ["breakpoint"] Name to use for custom property
/// @param {Map} $initial [breakpoint.get("null-name")] The value for the custom property when not within breakpoint
/// @param {String} $prefix [$config.prefix] Override default prefix

@mixin declare-breakpoint(
  $breakpoints: breakpoint.get-sizes(), 
  $name: "breakpoint",
  $initial: breakpoint.get("null-name"),
  $prefix: get("prefix")
) {
  @include declare($name, $initial, $prefix);
  @each $size, $value in $breakpoints {
    @include breakpoint.min($size) {
      @include declare($name, $size, $prefix);
    }
  }
}

/// Declare a custom property for each breakpoint size
/// @example scss Declare each property in a map as a custom property
///   :root {
///     @include ulu.cssvar-declare-breakpoint-sizes();
///   }
/// @param {Map} $breakpoints [breakpoint.get-sizes()] Breakpoints to declare
/// @param {String} $name ["breakpoint-size-"] Name to use for custom property (prefixes size name)
/// @param {String} $prefix [$config.prefix] Override default prefix

@mixin declare-breakpoint-sizes(
  $breakpoints: breakpoint.get-sizes(), 
  $name: "breakpoint",
  $prefix: get("prefix"),
) {
  @each $size, $value in $breakpoints {
    $min: breakpoint.get-size-value($size);
    $max: breakpoint.get-size-value($size, true);
    @include declare("#{ $name }-#{ $size }-min", $min, $prefix);
    @include declare("#{ $name }-#{ $size }-max", $max, $prefix);
  }
}

/// Outputs css vars for a specific type from a theme map
/// @deprecated Moved to themes core module (`themes.declare-values()`). This will be removed in a future version.
/// @param {Map} $theme The map containing the values. Example (
/// @param {String} $key The key used to retrieve values from the map.
/// @param {String} $prefix The optional prefix for CSS variables.
@mixin declare-theme-values($theme, $key, $prefix: get("prefix")) {
  @warn "ulu.cssvar-declare-theme-values() is deprecated. Please use ulu.themes-declare() instead.";
  @each $name, $definition in $theme {
    $value: map.get($definition, $key);
    @if ($value) {
      @include declare($name, $value, $prefix);
    }
  }
}

/// Joins a list of cssvar names
/// - Use to "+", "-" and then use in calc
/// - Accepts standard names (applies prefix) or raw "var(...)" / "calc(...)" strings
/// - Accepts raw values (numbers like 100vh) so they can be used in calculations
/// @param {List} $names list of names (string) or raw custom properties or calc (chaining support or custom math)
/// @param {String} $separator Separator to use when joining (+, -)
/// @return {String} For example if separator was "+" would result in "var(--some-prop) + var(--another-prop)"

@function join($names, $separator) {
  $values: ();

  @each $name in $names {
    // Passthrough with out prefix/use if already contains a raw CSS function
    $is-already-var: utils.is-string($name) and (string.index($name, "var(") or string.index($name, "calc("));

    // Passthrough as-is if already custom property or calc, or if it's a number
    @if ($is-already-var or utils.is-number($name)) {
      $values: list.append($values, $name);
    // Else pass it through use so it has prefix/var
    } @else {
      $values: list.append($values, use($name));
    }
  }

  @return utils.list-join($values, $separator);
}

/// For any names passed will join them with "+" and wrap in calc
/// @param {String} $name Name string (pass multiple comma separated)
/// @return {String} A string like "calc(var(--some-prop) + var(--another-prop))"

@function add($names...) {
  @return calc(join($names, "+"));
}

/// For any names passed will join them with "-" and wrap in calc
/// @param {String} $name Name string (pass multiple comma separated)
/// @return {String} A string like "calc(var(--some-prop) - var(--another-prop))"

@function subtract($names...) {
  @return calc(join($names, "-"));
}

/// Convenience method that maps to "name" function, sets "ulu" as prefix. 
/// - Used mostly internally

@function name-ulu($name) {
  @return name($name, "ulu");
}

/// Convenience method that maps to "use" function, sets "ulu" as prefix. 
/// - Used mostly internally

@function use-ulu($name, $default-value: null) {
  @return use($name, $default-value, "ulu");
}

/// Convenience method that maps to "declare" mixin, sets "ulu" as prefix. 
/// - Used mostly internally

@mixin declare-ulu($name, $value) {
  @include declare($name, $value, "ulu");
}