import { makeSizeProps, Sizes } from '@/composables/size'
import { makeVariantProps } from '@/composables/variant'
import { makeComponentProps } from '@/composables/component'
import { makeTagProps } from '@/composables/tag'
import { useEventListener } from '@vueuse/core'
import { UIcon } from '@/components/UIcon/UIcon'
import { USpinner } from '@/components/USpinner/USpinner'

import { genericComponent, propsFactory, useRender } from '@/utils'
import ColorName from '@/types/colors'

import {
  setColorVolume,
  changeColorName,
  getNextColor,
  getColorVolume,
} from '@/utils/useColors'
import { computed, ref } from 'vue'
import { makeRouterProps, useLink } from '@/composables/router'

export const makeUBaseButtonProps = propsFactory(
  {
    backgroundColor: { type: String, default: 'primary-600' as ColorName },
    borderColor: { type: String, default: 'primary-600' as ColorName },
    disabled: { type: Boolean, default: false },
    destructive: { type: Boolean, default: false },
    prependIcon: String,
    appendIcon: String,
    active: {
      type: Boolean,
      default: undefined,
    },
    loading: {
      type: Boolean,
      default: false,
    },

    ...makeVariantProps(),
    ...makeSizeProps({ size: Sizes.sm }),
    ...makeComponentProps(),
    ...makeTagProps(),
    ...makeRouterProps(),
  },
  'UBaseButton'
)

export type UBaseButtonSlots = {
  default: never
}

export const UBaseButton = genericComponent<UBaseButtonSlots>()({
  name: 'UBaseButton',

  props: makeUBaseButtonProps(),

  emits: {
    click: (e: MouseEvent) => true,
    'keydown:enter': () => true,
  },

  setup(props, { emit, slots, attrs }) {
    const link = useLink(props, attrs)
    const isActive = computed(() => {
      if (props.active !== undefined) {
        return props.active
      }

      if (link.isLink.value) {
        return link.isActive?.value
      }

      return false
    })
    const isDisabled = computed(() => props.disabled)

    const handleKeyDown = (event: KeyboardEvent) => {
      if (isFocused.value && event.key === 'Enter' && !props.disabled) {
        emit('keydown:enter')
        link.navigate
      }
    }

    useEventListener('keydown', handleKeyDown)

    function onClick(e: MouseEvent) {
      if (isDisabled.value) return

      emit('click', e)
      link.navigate?.(e)
    }

    const isFocused = ref(false)
    const isHovered = ref(false)
    const isActivated = ref(false)

    const hasPrepend = computed(() => !!props.prependIcon)
    const hasAppend = computed(() => !!props.appendIcon)

    const prependIcon = computed(() => {
      if (props.prependIcon) {
        return props.prependIcon
      }
      return ''
    })

    const appendIcon = computed(() => {
      if (props.appendIcon) {
        return props.appendIcon
      }
      return ''
    })

    //computed colors to watch destructive mode:

    const backgroundColor = computed<ColorName>(() => {
      let backgroundColor = props.backgroundColor
      if (props.destructive) {
        backgroundColor = setColorVolume(
          'error-100',
          getColorVolume(backgroundColor as ColorName)
        )
      }

      return backgroundColor as ColorName
    })

    const borderColor = computed<ColorName>(() => {
      let borderColor = props.borderColor
      if (props.destructive) {
        borderColor = setColorVolume(
          'error-100',
          getColorVolume(borderColor as ColorName)
        )
      }

      return borderColor as ColorName
    })

    const textColor = computed<ColorName>(() => {
      let color = props.color

      if (props.variant === 'outline') {
        color =
          props.color === 'white'
            ? changeColorName(props.borderColor as ColorName, 400)
            : props.color
      } else if (props.variant === 'text' || props.variant === 'plain') {
        color =
          props.color === 'white'
            ? changeColorName(props.borderColor as ColorName, 200)
            : props.color
      }

      if (props.destructive) {
        if (color === 'black' || color === 'white') {
          return color
        }
        color = setColorVolume('error-600', getColorVolume(color as ColorName))
      }

      return color as ColorName
    })

    //computed classes for each variant:

    const defaultClassesButton = computed<string>(
      () =>
        `bg-${backgroundColor.value} text-${textColor.value} border-${
          backgroundColor.value
        } hover:bg-${getNextColor(
          backgroundColor.value
        )} hover:border-${getNextColor(backgroundColor.value)} active:bg-${
          backgroundColor.value
        } active:shadow-xs-btn active:shadow-${setColorVolume(
          backgroundColor.value,
          200
        )} focus-visible:shadow-xs-btn focus-visible:shadow-${setColorVolume(
          backgroundColor.value,
          200
        )}`
    )

    const DisabledDefaultClassesButton = computed<string>(
      () =>
        `bg-${backgroundColor.value} text-${textColor.value} border-${backgroundColor.value}
    opacity-20 cursor-not-allowed`
    )

    const outlineClassesButton = computed<string>(
      () =>
        `bg-transparent text-${textColor.value} border-${
          borderColor.value
        } hover:bg-${
          textColor.value === 'black'
            ? 'gray-50'
            : setColorVolume(textColor.value, 50)
        } hover:text-${changeColorName(
          textColor.value,
          100
        )} active:bg-transparent active:shadow-xs-btn active:shadow-${setColorVolume(
          borderColor.value,
          200
        )} focus-visible:bg-transparent focus-visible:shadow-xs-btn 
        focus-visible:shadow-${setColorVolume(borderColor.value, 200)}`
    )

    const DisabledOutlineClassesButton = computed<string>(
      () =>
        `bg-transparent text-${textColor.value} border-${borderColor.value}
    opacity-20 cursor-not-allowed`
    )

    const textClassesButton = computed<string>(
      () =>
        `bg-transparent text-${
          textColor.value
        } border-transparent hover:text-${changeColorName(
          textColor.value,
          100
        )} active:text-${textColor.value} focus-visible:text-${textColor.value}`
    )
    const plainClassesButton = computed<string>(
      () =>
        `bg-transparent text-${
          textColor.value
        } border-transparent hover:text-${changeColorName(
          textColor.value,
          100
        )} hover:bg-${
          textColor.value === 'black'
            ? 'gray-50'
            : setColorVolume(textColor.value, 50)
        } active:text-${textColor.value} active:bg-transparent
        focus-visible:text-${textColor.value} focus-visible:bg-transparent`
    )

    const DisabledTextClassesButton = computed<string>(
      () =>
        `bg-transparent text-${textColor.value} border-transparent opacity-20 cursor-not-allowed`
    )

    useRender(() => {
      const Tag = link.isLink.value ? 'a' : props.tag
      const hasSlot = computed(() => !!slots.default?.()[0].children)

      const classes = computed(() => ({
        //general styling
        // eslint-disable-next-line max-len
        'box-border flex items-center justify-center gap-2 h-fit w-full font-semibold rounded-md border select-none focus-visible:outline-none':
          true,
        ...(props.disabled === false && {
          'cursor-pointer': true,
        }),
        //variants
        ...(props.variant === 'default' &&
          props.disabled === false && {
            [defaultClassesButton.value]: true,
          }),

        ...(props.variant === 'outline' &&
          props.disabled === false && {
            [outlineClassesButton.value]: true,
          }),
        ...(props.variant === 'text' &&
          props.disabled === false && {
            [textClassesButton.value]: true,
          }),
        ...(props.variant === 'plain' &&
          props.disabled === false && {
            [plainClassesButton.value]: true,
          }),
        //sizes
        [props.variant === 'text' ? '' : 'min-h-sm px-3.5']:
          props.size === 'sm' && hasSlot.value,
        [props.variant === 'text' ? '' : 'min-h-md px-4']:
          props.size === 'md' && hasSlot.value,
        [props.variant === 'text' ? '' : 'min-h-lg px-4']:
          props.size === 'lg' && hasSlot.value,
        [props.variant === 'text' ? '' : 'min-h-xl px-5']:
          props.size === 'xl' && hasSlot.value,
        [props.variant === 'text' ? '' : 'min-h-xxl px-7']:
          props.size === 'xxl' && hasSlot.value,
        [props.variant === 'text' ? 'max-h-5 px-3.5' : '']:
          props.size === 'sm' && hasSlot.value,
        [props.variant === 'text' ? 'max-h-5 px-4' : '']:
          props.size === 'md' && hasSlot.value,
        [props.variant === 'text' ? 'max-h-6 px-4' : '']:
          props.size === 'lg' && hasSlot.value,
        [props.variant === 'text' ? 'max-h-6 px-5' : '']:
          props.size === 'xl' && hasSlot.value,
        [props.variant === 'text' ? 'max-h-7 px-7' : '']:
          props.size === 'xxl' && hasSlot.value,
        ['text-text-sm']: props.size === 'sm' || props.size === 'md',
        ['text-text-md']: props.size === 'lg' || props.size === 'xl',
        ['text-text-lg']: props.size === 'xxl',
        //sizes fot buttons with icon
        ['p-2']: !hasSlot.value && props.size === 'sm',
        ['p-2.5']: !hasSlot.value && props.size === 'md',
        ['p-3']: !hasSlot.value && props.size === 'lg',
        ['p-3.5']: !hasSlot.value && props.size === 'xl',
        ['p-4']: !hasSlot.value && props.size === 'xxl',
        //disabled states
        ...(props.disabled === true &&
          props.variant === 'default' && {
            [DisabledDefaultClassesButton.value]: true,
          }),
        ...(props.disabled === true &&
          props.variant === 'outline' && {
            [DisabledOutlineClassesButton.value]: true,
          }),
        ...(props.disabled === true &&
          (props.variant === 'text' || props.variant === 'plain') && {
            [DisabledTextClassesButton.value]: true,
          }),
      }))

      return (
        <Tag
          class={[props.class, classes.value]}
          href={link.href.value}
          onClick={onClick}
          onMouseenter={() => (isHovered.value = true)}
          onMouseleave={() => (isHovered.value = false)}
          onMousedown={() => (isActivated.value = true)}
          onMouseup={() => (isActivated.value = false)}
          onFocus={() => (isFocused.value = true)}
          onBlur={() => (isFocused.value = false)}
        >
          {hasPrepend.value ? (
            <UIcon
              color={textColor.value}
              size="17"
              icon={`$${prependIcon.value}`}
              isActive={isActivated.value}
              isHover={isHovered.value}
            ></UIcon>
          ) : (
            ''
          )}
          {slots.default?.()}
          {hasAppend.value ? (
            <UIcon
              color={textColor.value}
              size="17"
              icon={`$${appendIcon.value}`}
              isActive={isActivated.value}
              isHover={isHovered.value}
            ></UIcon>
          ) : (
            ''
          )}
          {props.loading ? <USpinner size="17"></USpinner> : null}
        </Tag>
      )
    })

    return {}
  },
})

export type UBaseButton = InstanceType<typeof UBaseButton>
