<template>
  <Component
    :is="as"
    :class="[
      cn.b(),
      cn.m(type),
      cn.m(`${type}_${color}`),
      cn.m(size !== 'medium' ? size : ''),
      cn.is('one-icon-only', oneIconOnly),
      cn.has('icon', hasIcon),
      cn.is('disabled', disabled),
      cn.is('hover', state === 'hover'),
      cn.is('active', state === 'active'),
    ]"
    :disabled="disabled"
    v-bind="attributes"
    @click="handleClick"
  >
    <span
      v-if="hasLeftIcon && displayIcon"
      :class="[cn.e('icon'), cn.em('icon', 'left')]"
    >
      <slot name="left-icon">
        <Component :is="resolveComponent(`ui-icon-${leftIcon}`)" />
      </slot>
    </span>

    <span
      v-if="!iconOnly"
      :class="[cn.e('content')]"
    >
      <slot />
    </span>

    <span
      v-if="hasRightIcon && displayIcon"
      :class="[cn.e('icon'), cn.em('icon', 'right')]"
    >
      <slot name="right-icon">
        <Component :is="resolveComponent(`ui-icon-${rightIcon}`)" />
      </slot>
    </span>

    <span
      v-if="loading && !disabled"
      :class="[cn.e('loader')]"
    >
      <slot name="loader">
        <UiSpinner
          :delay="typeof loading === 'number' ? loading : undefined"
          @done="$emit('update:loading', false)"
        />
      </slot>
    </span>
  </Component>
</template>

<script setup lang="ts">
import type { ButtonHTMLAttributes } from 'vue'
import type { UiIcon } from '#build/types/ui/icons'

export interface Props {
  type?: 'filled' | 'outlined' | 'ghost' | 'link'
  color?: 'primary' | 'secondary'
  size?: 'small' | 'medium' | 'large'
  nativeType?: ButtonHTMLAttributes['type']
  icon?: UiIcon
  leftIcon?: UiIcon
  rightIcon?: UiIcon
  disabled?: boolean
  loading?: boolean | number
  state?: 'hover' | 'active'
}

defineOptions({
  name: 'UiButton',
})

const props = withDefaults(defineProps<Props>(), {
  type: 'filled',
  color: 'primary',
  size: 'medium',
  nativeType: 'button',
  disabled: false,
  loading: false,
})

const emit = defineEmits<{
  click: [e: Event]
  'update:loading': []
}>()

const slots = useSlots()
const attrs = useAttrs()

const linkComponent = useGlobalConfig('LINK_COMPONENT', 'a')

const cn = useClassname('ui-button')

const as = computed(() => (attrs.href ? linkComponent.value : 'button'))
const attributes = computed(() => {
  if (as.value === 'button') {
    return {
      type: props.nativeType,
      ...attrs,
    }
  }

  return attrs
})

const leftIcon = computed(() => props.leftIcon ?? props.icon)

const hasLeftIcon = computed(() => !!leftIcon.value || !!slots.leftIcon)
const hasRightIcon = computed(() => !!props.rightIcon || !!slots.rightIcon)
const hasIcon = computed(() => hasLeftIcon.value || hasRightIcon.value)
const oneIconOnly = computed(
  () =>
    ((hasLeftIcon.value && !hasRightIcon.value)
    || (!hasLeftIcon.value && hasRightIcon.value))
    && !slots.default,
)

const iconOnly = computed(
  () => (hasIcon.value || props.loading) && !slots.default,
)

const displayIcon = computed(
  () => hasIcon.value && ((props.loading && props.disabled) || !props.loading),
)

function handleClick(event: Event) {
  if (props.disabled || props.loading) {
    event.stopPropagation()
    event.preventDefault()
  }
  else {
    emit('click', event)
  }
}
</script>

<style lang="scss">
@use 'styles';
</style>
