@use 'sass:meta';
@use 'sass:math';
@use 'sass:color';
@use 'sass:string';
@use 'conv-color-name' as *;

/// Converts a given color to hex notation in either 3, 6, 4, or 8 digit
/// notation based on the input. If the given `$color` has alpha-transparency,
/// the 4 or 8 digit hex color is returned as an unquoted string that cannot be
/// used with any Sass color module methods or other functions that implement
/// type checking, as Sass will consider the output to be an unquoted string,
/// not a color. If the $color value has no alpha-transparency, or an alpha
/// value of `1`, the function will return either a 3 or 6 digit hex color, and
/// this color will be correctly recognized by Sass type-checking as type
/// `'color'`.
///
/// @param {Color} $color - The color to convert.
///
/// @return {Color | String} A 3 or 6 digit hex color, or a 4 or 8 digit hex
/// color as an unquoted string.
///
/// @access public
/// @group Utilities
/// @require {function} _component-to-hex
/// @require {function} conv-color-name
///
/// @throw Invalid data type for $color error.
@function to-hex($color) {
  @if meta.type-of($color) != 'color' {
    @error 'Invalid data type passed to [ to-hex() ] function. You passed ' +
      '#{meta.inspect($color)} for the $color argument, which is not an ' +
      'accepted color as it is of type #{meta.inspect(meta.type-of($color))}.';
  }

  // Extract RGB components and alpha channel
  $red-component: color.red($color);
  $green-component: color.green($color);
  $blue-component: color.blue($color);
  $alpha-component: color.alpha($color);

  // Handle 8-digit and 4-digit hex, if the color has an alpha channel
  @if $alpha-component != 1 {
    // Convert each RGB component to hex
    $red-hex: string.to-lower-case(_component-to-hex($red-component));
    $green-hex: string.to-lower-case(_component-to-hex($green-component));
    $blue-hex: string.to-lower-case(_component-to-hex($blue-component));
    $alpha: math.round($alpha-component * 255);
    $alpha-hex: string.to-lower-case(_component-to-hex($alpha));

    // If possible, convert the 8 digit hex into a 4 digit hex.
    @if $red-component % 17 == 0 and
      $green-component % 17 == 0 and
      $blue-component % 17 == 0 and
      $alpha % 17 == 0
    {
      $red-hex: string.slice($red-hex, 1, 1);
      $green-hex: string.slice($green-hex, 1, 1);
      $blue-hex: string.slice($blue-hex, 1, 1);
      $alpha-hex: string.slice($alpha-hex, 1, 1);
    }

    @return #{string.unquote(
      '##{$red-hex}#{$green-hex}#{$blue-hex}#{$alpha-hex}'
    )};
  }

  // If the color has an alpha of 1 or no alpha value (not trapsarent), convert
  // it into either a 3 or 6 digit hex color.
  $color: color.change(
    rgb($red-component $green-component $blue-component),
    $alpha: $alpha-component
  );

  // Uses conv-color-name because the above color.change() method will convert
  // the color into a CSS color name when possible, do not want that in output.
  @return conv-color-name($color);
}

/// Converts a given color to hex notation in either 3, 6, 4, or 8 digit
/// notation based on the input. If the given `$color` has alpha-transparency,
/// the 4 or 8 digit hex color is returned as an unquoted string that cannot be
/// used with any Sass color module methods or other functions that implement
/// type checking, as Sass will consider the output to be an unquoted string,
/// not a color. If the $color value has no alpha-transparency, or an alpha
/// value of `1`, the function will return either a 3 or 6 digit hex color, and
/// this color will be correctly recognized by Sass type-checking as type
/// `'color'`.
///
/// @param {Color} $color - The color to convert.
///
/// @return {Color | String} A 3 or 6 digit hex color, or a 4 or 8 digit hex
/// color as an unquoted string.
///
/// @access public
/// @group Utilities
/// @require {function} to-hex
///
/// @throw Invalid data type for $color error.
///
/// @alias to-hex
@function color-to-hex($color) {
  @return to-hex($color);
}

/// Converts a given color to hex notation in either 3, 6, 4, or 8 digit
/// notation based on the input. If the given `$color` has alpha-transparency,
/// the 4 or 8 digit hex color is returned as an unquoted string that cannot be
/// used with any Sass color module methods or other functions that implement
/// type checking, as Sass will consider the output to be an unquoted string,
/// not a color. If the $color value has no alpha-transparency, or an alpha
/// value of `1`, the function will return either a 3 or 6 digit hex color, and
/// this color will be correctly recognized by Sass type-checking as type
/// `'color'`.
///
/// @param {Color} $color - The color to convert.
///
/// @return {Color | String} A 3 or 6 digit hex color, or a 4 or 8 digit hex
/// color as an unquoted string.
///
/// @access public
/// @group Utilities
/// @require {function} to-hex
///
/// @throw Invalid data type for $color error.
///
/// @alias to-hex
@function conv-to-hex($color) {
  @return to-hex($color);
}

/// Converts a given color to hex notation in either 3, 6, 4, or 8 digit
/// notation based on the input. If the given `$color` has alpha-transparency,
/// the 4 or 8 digit hex color is returned as an unquoted string that cannot be
/// used with any Sass color module methods or other functions that implement
/// type checking, as Sass will consider the output to be an unquoted string,
/// not a color. If the $color value has no alpha-transparency, or an alpha
/// value of `1`, the function will return either a 3 or 6 digit hex color, and
/// this color will be correctly recognized by Sass type-checking as type
/// `'color'`.
///
/// @param {Color} $color - The color to convert.
///
/// @return {Color | String} A 3 or 6 digit hex color, or a 4 or 8 digit hex
/// color as an unquoted string.
///
/// @access public
/// @group Utilities
/// @require {function} to-hex
///
/// @throw Invalid data type for $color error.
///
/// @alias to-hex
@function convert-to-hex($color) {
  @return to-hex($color);
}

/// Helper function to convert a single component to a hex string.
/// Used only by the `to-hex()` function.
///
/// @param {Number} $component - The red, green, or blue color component number.
///
/// @return {String} A string representing the red, green, or blue component as
/// a hex value.
///
/// @access private
/// @group Utilities
@function _component-to-hex($component) {
  $index1: math.ceil(math.div($component, 16));
  $index2: $component % 16 + 1;
  $hex: string.slice('0123456789ABCDEF', $index1, $index1) +
    string.slice('0123456789ABCDEF', $index2, $index2);

  @if $component < 16 and $hex == '0' {
    $hex: '0' + $hex;
  }

  @return $hex;
}
