<template>
  <!-- biome-ignore lint/a11y/useSemanticElements: <can be button or link> -->
  <component
    :is="tag"
    :aria-disabled="isButtonDisabled"
    class="cpButton"
    :class="dynamicClasses"
    :disabled="isButtonDisabled"
    :href="hrefValue"
    role="button"
    tabindex="0"
    :type="type"
  >
    <span class="cpButton__body">
      <span v-if="isLoading" class="cpButton__loader">
        <cp-loader color="neutral" />
      </span>
      <span v-if="hasIconBefore" class="cpButton__icon cpButton__icon--isBefore">
        <slot name="leading-icon" />
      </span>
      <span v-if="hasLabel" class="cpButton__label">
        <slot />
      </span>
      <span v-if="hasIconAfter" class="cpButton__icon cpButton__icon--isAfter">
        <slot name="trailing-icon" />
      </span>
    </span>
  </component>
</template>

<script setup lang="ts">
import type { Slots } from 'vue'
import { computed, useSlots } from 'vue'

import CpLoader from '@/components/CpLoader.vue'

import { capitalizeFirstLetter } from '@/helpers'

export interface Props {
  appearance?: 'primary' | 'secondary' | 'tertiary'
  color?: 'neutral' | 'accent' | 'error' | 'warning' | 'success'
  disabled?: boolean
  href?: string
  isLoading?: boolean
  isSquare?: boolean
  size?: '2xs' | 'xs' | 'sm' | 'md' | 'lg'
  tag?: 'a' | 'button' | 'label'
  type?: 'button' | 'submit' | 'reset'
}

const props = withDefaults(defineProps<Props>(), {
  appearance: 'primary',
  color: 'neutral',
  href: undefined,
  tag: 'button',
  type: 'button',
  size: 'md',
})

const slots: Slots = useSlots()

const capitalizedAppearance = computed(() => capitalizeFirstLetter(props.appearance))

const capitalizedColor = computed(() => capitalizeFirstLetter(props.color))

const hasLabel = computed(() => !!slots.default)

const hasIconBefore = computed(() => !!slots['leading-icon'])

const hasIconAfter = computed(() => !!slots['trailing-icon'])

const isButtonDisabled = computed(() => props.disabled || props.isLoading)

const hrefValue = computed(() => (props.tag === 'a' ? props.href : undefined))

const dynamicClasses = computed(() => {
  return [
    `cpButton--is${capitalizedAppearance.value}`,
    `cpButton--${props.size}`,
    {
      [`cpButton--is${capitalizedColor.value}`]: !!capitalizedColor.value,
      'cpButton--isDisabled': isButtonDisabled.value,
      'cpButton--isIcon': !hasLabel.value,
      'cpButton--isLoading': props.isLoading,
      'cpButton--isSquare': props.isSquare,
    },
  ]
})

</script>

<style lang="scss">
@mixin cp-button-primary($className, $color, $backgroundColor, $hoverBackgroundColor) {
  &--isPrimary#{&}--is#{$className} {
    &:not(:disabled) {
      background-color: $backgroundColor;
      box-shadow: var(--cp-shadows-3xs);
      color: $color;

      &:hover {
        background-color: $hoverBackgroundColor;
      }

      &:focus-visible,
      &:active {
        background-color: $hoverBackgroundColor;
      }

      &:focus-visible {
        outline: fn.px-to-rem(2) solid $hoverBackgroundColor;
      }
    }
  }
}

@mixin cp-button-secondary($className, $color) {
  &--isSecondary#{&}--is#{$className} {
    &:not(:disabled) {
      background-color: var(--cp-background-primary);
      box-shadow:
        var(--cp-shadows-3xs-inset),
        0 0 0 var(--cp-dimensions-0_25) var(--cp-border-soft);
      color: $color;

      &:hover {
        background-color: var(--cp-background-primary-hover);
        box-shadow:
          var(--cp-shadows-3xs-inset),
          0 0 0 var(--cp-dimensions-0_25) var(--cp-border-soft-hover);
      }

      &:focus-visible,
      &:active {
        background-color: var(--cp-background-primary-hover);
      }

      &:focus-visible {
        outline: fn.px-to-rem(2) solid var(--cp-background-primary-hover);
      }
    }
  }
}

@mixin cp-button-tertiary($className, $color, $hoverColor, $hoverBackgroundColor) {
  &--isTertiary#{&}--is#{$className} {
    &:not(:disabled) {
      background-color: transparent;
      color: $color;

      &:hover {
        background-color: $hoverBackgroundColor;
        color: $hoverColor;
      }

      &:focus-visible,
      &:active {
        color: $hoverColor;
      }

      &:focus-visible {
        outline: fn.px-to-rem(2) solid $color;
      }
    }
  }
}

.cpButton {
  padding: var(--cp-spacing-md) var(--cp-spacing-lg);
  border-radius: var(--cp-radius-full);
  cursor: pointer;
  font-weight: 500;
  line-height: var(--cp-line-height-md);
  outline-offset: fn.px-to-rem(2);
  text-decoration: none;
  transition-duration: 250ms;
  transition-property: box-shadow, background-color, transform, width;
  transition-timing-function: ease;

  &,
  &__body {
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  &,
  &:visited {
    color: var(--cp-text-primary);
  }

  &:active {
    transform: scale3d(0.97, 0.97, 1);
  }

  // Primary appearance
  @include cp-button-primary(
    'Neutral',
    var(--cp-text-primary),
    var(--cp-background-primary),
    var(--cp-background-primary-hover)
  );
  @include cp-button-primary(
    'Accent',
    var(--cp-text-white),
    var(--cp-background-accent-solid),
    var(--cp-background-accent-solid-hover)
  );
  @include cp-button-primary(
    'Success',
    var(--cp-text-white),
    var(--cp-background-success-solid),
    var(--cp-background-success-solid-hover)
  );
  @include cp-button-primary(
    'Warning',
    var(--cp-text-white),
    var(--cp-background-warning-solid),
    var(--cp-background-warning-solid-hover)
  );
  @include cp-button-primary(
    'Error',
    var(--cp-text-white),
    var(--cp-background-error-solid),
    var(--cp-background-error-solid-hover)
  );

  // Secondary appearance
  @include cp-button-secondary('Neutral', var(--cp-text-primary));
  @include cp-button-secondary('Accent', var(--cp-text-accent-primary));
  @include cp-button-secondary('Success', var(--cp-text-success-primary));
  @include cp-button-secondary('Warning', var(--cp-text-warning-primary));
  @include cp-button-secondary('Error', var(--cp-text-error-primary));

  // Tertiary appearance
  @include cp-button-tertiary(
    'Neutral',
    var(--cp-text-primary),
    var(--cp-text-primary-hover),
    var(--cp-background-primary-hover)
  );
  @include cp-button-tertiary(
    'Accent',
    var(--cp-text-accent-primary),
    var(--cp-text-accent-primary-hover),
    var(--cp-background-accent-primary-hover)
  );
  @include cp-button-tertiary(
    'Success',
    var(--cp-text-success-primary),
    var(--cp-text-success-primary-hover),
    var(--cp-background-success-primary-hover)
  );
  @include cp-button-tertiary(
    'Warning',
    var(--cp-text-warning-primary),
    var(--cp-text-warning-primary-hover),
    var(--cp-background-warning-primary-hover)
  );
  @include cp-button-tertiary(
    'Error',
    var(--cp-text-error-primary),
    var(--cp-text-error-primary-hover),
    var(--cp-background-error-primary-hover)
  );

  &--isDisabled {
    background-color: var(--cp-background-disabled);
    box-shadow: none;
    color: var(--cp-text-disabled);
    user-select: none;

    &:hover,
    &:active {
      cursor: not-allowed;
      transform: none;
    }
  }

  &__body {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--cp-spacing-sm-md);
    text-align: center;
    transition: padding-left 250ms var(--cp-easing-elastic);
  }

  &__label {
    padding-inline: var(--cp-spacing-xs);
  }

  &__icon,
  &__loader {
    display: flex;
    align-items: center;
    justify-content: center;

    > svg,
    > i,
    > img {
      width: 100%;
      height: 100%;
    }
  }

  &__icon {
    flex-shrink: 0;
  }

  &--isLoading .cpButton__body {
    padding-left: calc(#{var(--cp-spacing-2xl)} + #{var(--cp-spacing-md)});
  }

  &__loader {
    @include mx.square-sizing(24);

    position: absolute;
    top: 50%;
    left: 0;
    transform: translateY(-50%);
  }

  &--isSquare {
    &.cpButton--2xs {
      border-radius: var(--cp-radius-sm-md);
    }

    &.cpButton--xs,
    &.cpButton--sm,
    &.cpButton--md {
      border-radius: var(--cp-radius-md);
    }

    &.cpButton--lg {
      border-radius: var(--cp-radius-md-lg);
    }
  }

  &--2xs,
  &--xs,
  &--sm {
    .cpButton__icon,
    .cpButton__loader {
      @include mx.square-sizing(16);
    }
  }

  &--md,
  &--lg {
    .cpButton__icon,
    .cpButton__loader {
      @include mx.square-sizing(20);
    }
  }

  &--2xs {
    padding: var(--cp-spacing-sm) var(--cp-spacing-md);
    font-size: var(--cp-text-size-xs);
    line-height: var(--cp-line-height-xs);

    &.cpButton--isIcon {
      padding: var(--cp-spacing-sm);
    }
  }

  &--xs {
    padding: var(--cp-spacing-sm) var(--cp-spacing-md);
    font-size: var(--cp-text-size-sm);
    line-height: var(--cp-line-height-sm);

    &.cpButton--isIcon {
      padding: var(--cp-spacing-sm-md);
    }
  }

  &--sm {
    padding: var(--cp-spacing-sm-md) var(--cp-spacing-md);
    font-size: var(--cp-text-size-sm);
    line-height: var(--cp-line-height-sm);

    &.cpButton--isIcon {
      padding: var(--cp-spacing-md);
    }
  }

  &--md {
    padding: var(--cp-spacing-md) var(--cp-spacing-lg);
    font-size: var(--cp-text-size-md);
    line-height: var(--cp-line-height-md);

    &.cpButton--isIcon {
      padding: fn.px-to-rem(10);
    }
  }

  &--lg {
    padding: var(--cp-spacing-lg) var(--cp-spacing-xl);
    font-size: var(--cp-text-size-md);
    line-height: var(--cp-line-height-md);

    &.cpButton--isIcon {
      padding: fn.px-to-rem(14);
    }
  }
}
</style>
