@use 'sass:math';
@use 'sass:meta';
@use 'sass:string';

// Json Encoding
// =============

// JSON Ecode
// ----------
/// Encode any Sass value as a JSON-ready string.
///
/// @group config_api-utilities
///
/// @param {*} $value [$herman] -
///   Data to be encoded for JSON exporting
@function encode($value) {
  $type: meta.type-of($value);

  @if ($type == 'bool') {
    @return $value;
  } @else if ($type == 'null') {
    @return 'null';
  } @else if ($type == 'list') {
    @return encode-list($value);
  } @else if ($type == 'map') {
    @return encode-map($value);
  } @else if ($type == 'number') {
    @return encode-number($value);
  }

  @return quotes($value);
}

// Encode List
// -----------
/// Encode each item in a Sass list,
/// and convert to a JSON-ready square-bracketed list.
///
/// @group config_api-utilities
///
/// @param {list} $list -
///   List to be encoded for JSON exporting
@function encode-list($list) {
  $str: '';

  @each $item in $list {
    $str: if($str != '', $str + ', ', $str);
    $str: $str + encode($item);
  }

  @return '[#{$str}]';
}

// Encode Map
// ----------
/// Encode each key/value in a Sass map,
/// and convert to a JSON-ready object.
///
/// @group config_api-utilities
///
/// @param {map} $map -
///   Map to be encoded for JSON exporting
@function encode-map($map) {
  $str: '';

  @each $key, $value in $map {
    $str: if($str != '', $str + ', ', $str);
    $str: $str + quotes($key) + ': ' + encode($value);
  }

  @return '{#{$str}}';
}

// Encode Number
// -------------
/// Encode a number for JSON,
/// adding proof-quotes for length values.
///
/// @group config_api-utilities
///
/// @param {number} $number -
///   Number to be encoded for JSON exporting
@function encode-number($number) {
  @if math.is-unitless($number) {
    @return meta.inspect($number);
  }

  @return quotes($number);
}

// Quote
// -----
/// Convert any value to a double-quoted string.
///
/// @group config_api-utilities
/// @access private
///
/// @param {*} $value -
///   The value to inspect and quote.
@function quotes($value) {
  $value: '#{$value}';
  $value: escape-backslashes($value);
  $value: escape-quotes($value);

  @return '"#{$value}"';
}

// Escape Quotes
// -------------
/// Return a string, with internal quotes escaped.
///
/// @group config_api-utilities
/// @access private
///
/// @param {string} $string -
///   The string to be manipulated
@function escape-quotes($string) {
  $return: $string;
  $old: '"';
  $new: '\\"';
  $i: string.index($string, $old);
  $n: string.length($old);

  @if ($string == $old) {
    $return: $new;
  } @else if $i {
    $a: if($i > 1, string.slice($string, 1, $i - 1), '');
    $z: string.slice($string, $i + $n);
    $z: escape-quotes($z);
    $return: $a + if($new, $new, '') + $z;
  }

  @return $return;
}

// Escape Backslashes
// ------------------
/// Return a string, with internal backslashes escaped.
///
/// @group config_api-utilities
/// @access private
///
/// @param {string} $string -
///   The string to be manipulated
@function escape-backslashes($string) {
  $return: $string;
  $old: '\\';
  $new: '\\\\';
  $i: string.index($string, $old);
  $n: string.length($old);

  @if ($string == $old) {
    $return: $new;
  } @else if $i {
    $a: if($i > 1, string.slice($string, 1, $i - 1), '');
    $z: string.slice($string, $i + $n);
    $z: escape-backslashes($z);
    $return: $a + if($new, $new, '') + $z;
  }

  @return $return;
}
