@use "sass:list";
@use "sass:math";
@use "sass:map";

@use "../abstracts/functions" as *;
@use "../abstracts/config" as VAR;

$generated-keyframes: ();

@mixin ensure-keyframes($name) {
  @if not list.index($generated-keyframes, $name) {
    $generated-keyframes: list.append($generated-keyframes, $name) !global;

    @keyframes #{$name} {
      @content;
    }
  }
}

/**
 * @mixin animate-bounce
 * @description Creates a bouncing animation
 * @param {Map} $props - Animation properties
 */
@mixin animate-bounce($props: ()) {
  $defaults: (
    vertical: 0.5rem,
    horizontal: 0,
    duration: 1s,
    timing: ease-in-out,
    iteration: infinite,
  );

  // Merge with defaults
  $props: map.merge($defaults, $props);
  $x: map.get($props, "horizontal");
  $y: map.get($props, "vertical");
  $duration: map.get($props, "duration");
  $easing: map.get($props, "timing");
  $iteration: map.get($props, "iteration");

  // Handle units
  $x-unit: if($x, safe-unit-name(math.unit($x)), "-");
  $y-unit: if($y, safe-unit-name(math.unit($y)), "-");

  // Clean values (remove units)
  $clean-x: if($x, strip-unit($x), 0);
  $clean-y: if($y, strip-unit($y), 0);

  $animation-name: anim-bounce-#{$clean-x}#{$x-unit}-#{$clean-y}#{$y-unit};

  // Apply the animation
  animation: #{$animation-name} $duration $easing $iteration;

  // Generate keyframes with ensure-keyframes pattern
  @include ensure-keyframes($animation-name) {
    0%,
    100% {
      transform: translate(0, 0);
      animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
    }

    50% {
      transform: translate($x, $y);
      animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
    }
  }
}

@mixin hover-ready {
  transition-property: all;
  transition-duration: 0.2s;
  transition-timing-function: ease-in-out;
}

/**
 * @mixin animate-pulse
 * @description Creates a pulsing opacity animation
 * @param {Map} $props - Animation properties
 */
@mixin animate-pulse($props: ()) {
  $defaults: (
    min-opacity: 0.5,
    max-opacity: 1,
    duration: 1s,
    timing: ease-in-out,
    iteration: infinite,
  );

  // Merge with defaults
  $props: map.merge($defaults, $props);
  $min-opacity: map.get($props, "min-opacity");
  $max-opacity: map.get($props, "max-opacity");
  $duration: map.get($props, "duration");
  $timing: map.get($props, "timing");
  $iteration: map.get($props, "iteration");

  $animation-name: anim-pulse-#{$min-opacity * 100}-#{$max-opacity * 100};

  animation: #{$animation-name} $duration $timing $iteration;

  @include ensure-keyframes($animation-name) {
    0%,
    100% {
      opacity: $max-opacity;
    }

    50% {
      opacity: $min-opacity;
    }
  }
}

/**
 * @mixin animate-spin
 * @description Creates a spinning animation
 * @param {Map} $props - Animation properties
 */
@mixin animate-spin($props: ()) {
  $defaults: (
    direction: normal,
    duration: 1s,
    timing: linear,
    iteration: infinite,
  );

  // Merge with defaults
  $props: map.merge($defaults, $props);
  $direction: map.get($props, "direction");
  $duration: map.get($props, "duration");
  $timing: map.get($props, "timing");
  $iteration: map.get($props, "iteration");

  $animation-name: anim-spin-#{$direction};

  animation: #{$animation-name} $duration $timing $iteration;

  @include ensure-keyframes($animation-name) {
    from {
      transform: rotate(0deg);
    }

    to {
      @if $direction == "normal" {
        transform: rotate(360deg);
      } @else {
        transform: rotate(-360deg);
      }
    }
  }
}

/**
 * @mixin animate-ping
 * @description Creates a ping animation (scale + fade)
 * @param {Map} $props - Animation properties
 */
@mixin animate-ping($props: ()) {
  $defaults: (
    scale: 2,
    duration: 1s,
    timing: cubic-bezier(0, 0, 0.2, 1),
    iteration: infinite,
  );

  // Merge with defaults
  $props: map.merge($defaults, $props);
  $scale: map.get($props, "scale");
  $duration: map.get($props, "duration");
  $timing: map.get($props, "timing");
  $iteration: map.get($props, "iteration");

  $animation-name: anim-ping-#{$scale * 10};

  animation: #{$animation-name} $duration $timing $iteration;

  @include ensure-keyframes($animation-name) {
    75%,
    100% {
      transform: scale($scale);
      opacity: 0;
    }
  }
}

/**
 * @mixin animate-fade
 * @description Creates fade in/out animation
 * @param {Map} $props - Animation properties
 */
@mixin animate-fade($props: ()) {
  $defaults: (
    type: in,
    duration: 0.5s,
    timing: ease-in-out,
    iteration: 1 forwards,
  );

  // Merge with defaults
  $props: map.merge($defaults, $props);
  $type: map.get($props, "type");
  $duration: map.get($props, "duration");
  $timing: map.get($props, "timing");
  $iteration: map.get($props, "iteration");

  $animation-name: anim-fade-#{$type};

  animation: #{$animation-name} $duration $timing $iteration;

  @include ensure-keyframes($animation-name) {
    @if $type == "in" {
      from {
        opacity: 0;
      }

      to {
        opacity: 1;
      }
    } @else if $type == "out" {
      from {
        opacity: 1;
      }

      to {
        opacity: 0;
      }
    } @else if $type == "in-up" {
      from {
        opacity: 0;
        transform: translateY(20px);
      }

      to {
        opacity: 1;
        transform: translateY(0);
      }
    } @else if $type == "in-down" {
      from {
        opacity: 0;
        transform: translateY(-20px);
      }

      to {
        opacity: 1;
        transform: translateY(0);
      }
    }
  }
}

/**
 * @mixin animate-shake
 * @description Creates a shake animation
 * @param {Map} $props - Animation properties
 */
@mixin animate-shake($props: ()) {
  $defaults: (
    intensity: 5px,
    duration: 0.5s,
    timing: ease-in-out,
    iteration: 1,
  );

  // Merge with defaults
  $props: map.merge($defaults, $props);
  $intensity: map.get($props, "intensity");
  $duration: map.get($props, "duration");
  $timing: map.get($props, "timing");
  $iteration: map.get($props, "iteration");

  $intensity-val: strip-unit($intensity);
  $animation-name: anim-shake-#{$intensity-val};

  animation: #{$animation-name} $duration $timing $iteration;

  @include ensure-keyframes($animation-name) {
    0%,
    100% {
      transform: translateX(0);
    }

    10%,
    30%,
    50%,
    70%,
    90% {
      transform: translateX(-$intensity);
    }

    20%,
    40%,
    60%,
    80% {
      transform: translateX($intensity);
    }
  }
}

@if VAR.$generate-utility-classes {
  // Add to your existing animation utilities

  #{VAR.$parent-selector} .animate-pulse {
    @include animate-pulse;
  }

  #{VAR.$parent-selector} .animate-spin {
    @include animate-spin;
  }

  #{VAR.$parent-selector} .animate-ping {
    @include animate-ping;
  }

  #{VAR.$parent-selector} .animate-fade-in {
    @include animate-fade(
      (
        type: in,
      )
    );
  }

  #{VAR.$parent-selector} .animate-fade-out {
    @include animate-fade(
      (
        type: out,
      )
    );
  }

  #{VAR.$parent-selector} .animate-fade-in-up {
    @include animate-fade(
      (
        type: in-up,
      )
    );
  }

  #{VAR.$parent-selector} .animate-fade-in-down {
    @include animate-fade(
      (
        type: in-down,
      )
    );
  }

  #{VAR.$parent-selector} .animate-shake {
    @include animate-shake;
  }

  // Add responsive variants if needed
  @each $breakpoint, $width in VAR.$breakpoints {
    @media (min-width: #{$width}) {
      #{VAR.$parent-selector} .animate-pulse\@#{$breakpoint} {
        @include animate-pulse;
      }

      #{VAR.$parent-selector} .animate-spin\@#{$breakpoint} {
        @include animate-spin;
      }

      #{VAR.$parent-selector} .animate-ping\@#{$breakpoint} {
        @include animate-ping;
      }

      #{VAR.$parent-selector} .animate-fade-in\@#{$breakpoint} {
        @include animate-fade(
          (
            type: in,
          )
        );
      }

      #{VAR.$parent-selector} .animate-fade-out\@#{$breakpoint} {
        @include animate-fade(
          (
            type: out,
          )
        );
      }

      #{VAR.$parent-selector} .animate-fade-in-up\@#{$breakpoint} {
        @include animate-fade(
          (
            type: in-up,
          )
        );
      }

      #{VAR.$parent-selector} .animate-fade-in-down\@#{$breakpoint} {
        @include animate-fade(
          (
            type: in-down,
          )
        );
      }

      #{VAR.$parent-selector} .animate-shake\@#{$breakpoint} {
        @include animate-shake;
      }
    }
  }

  #{VAR.$parent-selector} .hover-ready {
    @include hover-ready;
  }
}
