@use 'sass:map';
@use 'sass:math';
@use '../utils/tokens.scss' as tokens;
@use './spinner.vars.scss' as vars;

@function get-var($suffix, $spinnerVarPrefix: vars.$spinnerVarPrefix) {
  @return var(#{$spinnerVarPrefix}#{vars.$spinnerBgStrokeVarSuffix});
}

@function getRadius($side-length, $stroke-width) {
  // NOTE: Leave for future refactor. Svg Circle sizing:
  // 2 * radius + stroke width = browser computet square area side length (sideLength)
  // (2r + width = sideLength)
  // so
  // 2r = sideLength - width

  $radius: math.div($side-length - $stroke-width, 2);
  @return $radius;
}

@function get-size-prop-var-name(
  $size,
  $suffix,
  $spinnerVarPrefix: vars.$spinnerVarPrefix
) {
  @return '#{$spinnerVarPrefix}-#{$size}-#{$suffix}';
}

@function get-size-prop-var(
  $size,
  $suffix,
  $spinnerVarPrefix: vars.$spinnerVarPrefix
) {
  @return var(#{$spinnerVarPrefix}-#{$size}-#{$suffix});
}

@mixin square($size) {
  width: $size;
  height: $size;
}

%spinnerShared {
  position: relative;
  margin: 0 auto;

  @include square(var(#{vars.$spinnerSideLengthVar}));
}

%circlesShared {
  r: calc(
    (var(#{vars.$spinnerSideLengthVar}) - var(#{vars.$spinnerStrokeWidth})) / 2
  );
  stroke-width: var(#{vars.$spinnerStrokeWidth});
}

%spinnerSvgElement {
  position: relative;
  transform: rotate(#{vars.$initialRotation}deg) translate3d(0, 0, 0);

  @include square(calc(var(#{vars.$spinnerSideLengthVar})));
}

%spinnerCircleElement {
  @extend %circlesShared;
  stroke: tokens.$bm-comp-spinner-color-indicator;

  stroke-linecap: round;
  transform-origin: center;
}

%spinnerCircleBgElement {
  @extend %circlesShared;
  stroke: tokens.$bm-comp-spinner-color-track;
}

%indeterminateSpinnerWrap {
  $muchLargerThanCircumference: 500;
  $asSmallThatItLooksLikeDot: 1;

  @function stroke-dash($multiplier) {
    @return calc(
      (var(#{vars.$spinnerSideLengthVar}) - var(#{vars.$spinnerStrokeWidth})) * #{math.$pi} *
        #{$multiplier}
    );
  }

  @keyframes dash {
    0% {
      stroke-dashoffset: 0;
      stroke-dasharray: 1, stroke-dash(1.6);
    }

    50% {
      stroke-dasharray: stroke-dash(0.712), $muchLargerThanCircumference;
      stroke-dashoffset: stroke-dash(-0.278);
    }

    100% {
      stroke-dasharray: stroke-dash(0.708), $muchLargerThanCircumference;
      stroke-dashoffset: stroke-dash(-0.992);
    }
  }
}

.#{vars.$spinnerClassBase} {
  @extend %spinnerShared;

  &__#{vars.$svgEl} {
    @extend %spinnerSvgElement;
  }

  &__#{vars.$circleEl} {
    @extend %spinnerCircleElement;
  }

  &__#{vars.$circleBgEl} {
    @extend %spinnerCircleBgElement;
  }

  &__content-wrapper {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(#{vars.$spinnerLabelToDescriptionSpacingVar});
  }

  &__heading {
    display: inline-flex;
    color: tokens.$bm-comp-spinner-color-heading;
    font: tokens.$bm-comp-spinner-typo-heading;
  }

  &__percentage-indicator {
    &:before {
      content: '\a0'; // non-breaking space, works with rtl too (nbsp in html won't)
    }
  }

  &__body {
    margin: 0;
    color: tokens.$bm-comp-spinner-color-body;
    font: tokens.$bm-comp-spinner-typo-body;
    text-align: center;
  }

  @keyframes rotation {
    0% {
      transform: rotate(#{vars.$initialRotation}deg);
    }

    100% {
      transform: rotate(#{360 + vars.$initialRotation}deg);
    }
  }
}

.#{vars.$spinnerIndeterminate} {
  @extend %indeterminateSpinnerWrap;

  .#{vars.$spinnerClassBase}__#{vars.$svgEl} {
    animation: rotation calc(var(#{vars.$spinnerAnimationSpeedVar}) / 2) linear
      infinite;
  }

  .#{vars.$spinnerClassBase}__#{vars.$circleEl} {
    animation: dash calc(var(#{vars.$spinnerAnimationSpeedVar}) / 2) ease infinite;
  }
}

.#{vars.$spinnerDeterminate} {
  .#{vars.$spinnerClassBase}__#{vars.$circleEl} {
    transition: stroke-dasharray 0.5s ease;

    stroke-dasharray: calc(
        (
            (var(#{vars.$spinnerSideLengthVar}) - var(#{vars.$spinnerStrokeWidth})) *
              #{math.$pi}
          ) * (var(#{vars.$spinnerValueVar}) / 100)
      )
      300px;
    stroke-dashoffset: 0;
  }
}

.#{vars.$spinnerWrapClassBase} {
  #{vars.$spinnerAnimationSpeedVar}: 3s;
  max-width: tokens.$bm-comp-spinner-size-width;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(#{vars.$spinnerLabelToSpinnerSpacingVar});

  progress {
    -webkit-appearance: none;
    appearance: none;
    border: none;
    position: absolute;
    visibility: hidden;
    width: 0;
  }

  ::-moz-progress-bar,
  ::-webkit-progress-bar,
  ::-webkit-progress-value,
  ::-webkit-progress-inner-element {
    -webkit-appearance: none;
    appearance: none;
  }

  &--xs,
  &--xxs {
    display: inline-flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    max-width: unset;

    .#{vars.$spinnerClassBase} {
      box-sizing: content-box;
      margin-bottom: 0;
      padding: tokens.$bm-comp-spinner-space-nudge-y 0;

      &__content-wrapper {
        display: inline-flex;
        flex-direction: row;
      }

      &__body {
        text-align: left;
      }
    }
  }

  &--xxs {
    .#{vars.$spinnerClassBase} {
      &__heading {
        font: tokens.$bm-sem-typo-label-sm;
      }

      &__body {
        font: tokens.$bm-sem-typo-body-sm;
      }
    }
  }

  @each $size, $token-values in vars.$fromTokens {
    &--#{$size} {
      #{vars.$spinnerSideLengthVar}: map.get($token-values, 'side-length');
      #{vars.$spinnerStrokeWidth}: map.get($token-values, 'stroke-width');
      #{vars.$spinnerLabelToSpinnerSpacingVar}: map.get(
        $token-values,
        'label-to-spinner-spacing'
      );
      #{vars.$spinnerLabelToDescriptionSpacingVar}: map.get(
        $token-values,
        'label-to-description-spacing'
      );
    }
  }
}
