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

// Utilities
$spacer: 1rem !default;
$font-size-base: 0.9rem !default;
$spacers: (
  0: 0,
  1: $spacer * .25,
  2: $spacer * .5,
  3: $spacer,
  4: $spacer * 1.5,
  5: $spacer * 3,
) !default;
$font-sizes: (
  1: $font-size-base * 2.5,
  2: $font-size-base * 2,
  3: $font-size-base * 1.75,
  4: $font-size-base * 1.5,
  5: $font-size-base * 1.25,
  6: $font-size-base,
  7: $font-size-base * 0.8,
) !default;
$white: oklch(100% 0 0deg) !default;
$black: oklch(0% 0 0deg) !default;
$primary: oklch(55.03% 0.118 159.16deg) !default;
$secondary: oklch(44.75% 0.0121 229.13deg) !default;
$success: oklch(55.2% 0.1234 157.01deg) !default;
$info: oklch(77.49% 0.138 218.05deg) !default;
$warning: oklch(76.45% 0.1545 50.19deg / 58.04%) !default;
$danger: oklch(54.96% 0.2235 28.33deg) !default;
$light: oklch(98.16% 0.0017 247.84deg) !default;
$dark: oklch(26.21% 0.0095 248.19deg) !default;
$border-default: oklch(91.09% 0.007 247.9deg) !default;
$theme-colors: (
  "primary":    $primary,
  "secondary":  $secondary,
  "success":    $success,
  "info":       $info,
  "warning":    $warning,
  "danger":     $danger,
  "light":      $light,
  "dark":       $dark,
  "black":      $black,
  "white":      $white,
) !default;
$utilities: () !default;
// stylelint-disable-next-line scss/dollar-variable-default
$utilities: map.merge(
  (
    "float": (
      responsive: true,
      property: float,
      values: (
        start: left,
        end: right,
      )
    ),
    "overflow": (
      property: overflow,
      values: auto hidden visible scroll,
    ),
    "display": (
      responsive: true,
      print: true,
      property: display,
      class: d,
      values: inline-block block grid flex inline-flex none
    ),
    "position": (
      property: position,
      values: relative absolute fixed
    ),
    "border": (
      property: border,
      values: (
        null: 1px solid $border-default,
        0: 0,
      )
    ),
    "border-top": (
      property: border-top,
      values: (
        null: 1px solid $border-default,
        0: 0,
      )
    ),
    "border-end": (
      property: border-right,
      class: border-end,
      values: (
        null: 1px solid $border-default,
        0: 0,
      )
    ),
    "border-bottom": (
      property: border-bottom,
      values: (
        null: 1px solid $border-default,
        0: 0,
      )
    ),
    "border-color": (
      property: border-color,
      class: border,
      values: $theme-colors
    ),
    "width": (
      property: width,
      class: w,
      values: (
        0: 0,
        5: 5%,
        10: 10%,
        15: 15%,
        20: 20%,
        25: 25%,
        30: 30%,
        35: 35%,
        40: 40%,
        50: 50%,
        60: 60%,
        75: 75%,
        100: 100%,
        auto: auto,
        fit-content: fit-content
      )
    ),
    "height": (
      property: height,
      class: h,
      values: (
        100: 100%,
        auto: auto
      )
    ),
    "flex-direction": (
      responsive: true,
      property: flex-direction,
      class: flex,
      values: row column row-reverse
    ),
    "flex-grow": (
      responsive: true,
      property: flex-grow,
      class: flex,
      values: (
        grow-0: 0,
        grow-1: 1,
        grow-2: 2,
        grow-3: 3,
      )
    ),
    "flex-shrink": (
      responsive: true,
      property: flex-shrink,
      class: flex,
      values: (
        shrink-0: 0,
        shrink-1: 1,
      )
    ),
    "flex-wrap": (
      responsive: true,
      property: flex-wrap,
      class: flex,
      values: wrap nowrap
    ),
    "gap": (
      responsive: true,
      property: gap,
      class: gap,
      values: $spacers
    ),
    "justify-content": (
      responsive: true,
      property: justify-content,
      values: (
        start: flex-start,
        end: flex-end,
        center: center,
        between: space-between,
        around: space-around,
        evenly: space-evenly,
      )
    ),
    "align-items": (
      responsive: true,
      property: align-items,
      values: (
        start: flex-start,
        end: flex-end,
        center: center,
        stretch: stretch,
      )
    ),
    "align-self": (
      responsive: true,
      property: align-self,
      values: (
        start: flex-start,
        end: flex-end,
        center: center,
        baseline: baseline,
        stretch: stretch,
      )
    ),
    "margin": (
      responsive: true,
      property: margin,
      class: m,
      values: map.merge($spacers, (auto: auto))
    ),
    "margin-x": (
      responsive: true,
      property: margin-right margin-left,
      class: mx,
      values: map.merge($spacers, (auto: auto))
    ),
    "margin-y": (
      responsive: true,
      property: margin-top margin-bottom,
      class: my,
      values: map.merge($spacers, (auto: auto))
    ),
    "margin-top": (
      responsive: true,
      property: margin-top,
      class: mt,
      values: map.merge($spacers, (auto: auto))
    ),
    "margin-end": (
      responsive: true,
      property: margin-right,
      class: me,
      values: map.merge($spacers, (auto: auto))
    ),
    "margin-bottom": (
      responsive: true,
      property: margin-bottom,
      class: mb,
      values: map.merge($spacers, (auto: auto))
    ),
    "margin-start": (
      responsive: true,
      property: margin-left,
      class: ms,
      values: map.merge($spacers, (auto: auto))
    ),
    // Padding utilities
    "padding": (
      responsive: true,
      property: padding,
      class: p,
      values: $spacers
    ),
    "padding-x": (
      responsive: true,
      property: padding-right padding-left,
      class: px,
      values: $spacers
    ),
    "padding-y": (
      responsive: true,
      property: padding-top padding-bottom,
      class: py,
      values: $spacers
    ),
    "padding-top": (
      responsive: true,
      property: padding-top,
      class: pt,
      values: $spacers
    ),
    "padding-end": (
      responsive: true,
      property: padding-right,
      class: pe,
      values: $spacers
    ),
    "padding-bottom": (
      responsive: true,
      property: padding-bottom,
      class: pb,
      values: $spacers
    ),
    "padding-start": (
      responsive: true,
      property: padding-left,
      class: ps,
      values: $spacers
    ),
    "font-weight": (
      property: font-weight,
      class: fw,
      values: (
        normal: 400,
        600: 600,
        bold: 700,
      )
    ),
    "text-align": (
      responsive: true,
      property: text-align,
      class: text,
      values: (
        start: left,
        end: right,
        center: center,
      )
    ),
    "text-decoration": (
      property: text-decoration,
      values: line-through
    ),
    "text-transform": (
      property: text-transform,
      class: text,
      values: lowercase uppercase capitalize
    ),
    "white-space": (
      property: white-space,
      class: text,
      values: (
        wrap: normal,
      )
    ),
    "word-wrap": (
      property: word-wrap word-break,
      class: text,
      values: (break: break-word),
    ),
    "color": (
      property: color,
      class: text,
      values: $theme-colors,
    ),
    "background-color": (
      property: background-color,
      class: bg,
      values: map.merge(
        $theme-colors,
        (
          "transparent": transparent
        )
      )
    ),
    "visibility": (
      property: visibility,
      class: null,
      values: (
        visible: visible,
        invisible: hidden,
      )
    )
  ),
  $utilities
);

@mixin generate-utility($utility, $infix) {
  $values: map.get($utility, values);

  // If the values are a list or string, convert it into a map
  @if meta.type-of($values) == "string" or meta.type-of(list.nth($values, 1)) != "list" {
    $values: list.zip($values, $values);
  }

  @each $key, $value in $values {
    $properties: map.get($utility, property);

    // Multiple properties are possible, for example with vertical or horizontal margins or paddings
    @if meta.type-of($properties) == "string" {
      $properties: list.append((), $properties);
    }

    // Use custom class if present
    $property-class: if(map.has-key($utility, class), map.get($utility, class), list.nth($properties, 1));
    $property-class: if($property-class == null, "", $property-class);

    // Use custom CSS variable name if present, otherwise default to `class`
    $css-variable-name: if(map.has-key($utility, css-variable-name), map.get($utility, css-variable-name), map.get($utility, class));

    // State params to generate pseudo-classes
    $state: if(map.has-key($utility, state), map.get($utility, state), ());
    $infix: if($property-class == "" and string.slice($infix, 1, 1) == "-", string.slice($infix, 2), $infix);

    // Don't prefix if value key is null (eg. with shadow class)
    $property-class-modifier: if($key, if($property-class == "" and $infix == "", "", "-") + $key, "");
    $is-css-var: map.get($utility, css-var);
    $is-rtl: map.get($utility, rtl);

    @if $value {
      @if $is-rtl == false {
        /* rtl:begin:remove */
      }

      @if $is-css-var {
        .#{$property-class + $infix + $property-class-modifier} {
          #{$css-variable-name}: #{$value};
        }

        @each $pseudo in $state {
          .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} {
            #{$css-variable-name}: #{$value};
          }
        }
      } @else {
        .#{$property-class + $infix + $property-class-modifier} {
          @each $property in $properties {
            #{$property}: $value if(true, !important, null);
          }
        }

        @each $pseudo in $state {
          .#{$property-class + $infix + $property-class-modifier}-#{$pseudo}:#{$pseudo} {
            @each $property in $properties {
              #{$property}: $value if(true, !important, null);
            }
          }
        }
      }

      @if $is-rtl == false {
        /* rtl:end:remove */
      }
    }
  }
}

@each $key, $utility in $utilities {
  // The utility can be disabled with `false`, thus check if the utility is a map first
  // Then check if the utility needs print styles
  @if meta.type-of($utility) == "map" {
    @include generate-utility($utility, "");
  }
}
