import { PropType, defineComponent, h, ref, useId, watch, withDirectives } from 'vue'
import vCTooltip from '../../directives/v-c-tooltip'

const CRating = defineComponent({
  name: 'CRating',
  props: {
    /**
     * Enables the clearing upon clicking the selected item again.
     */
    allowClear: Boolean,
    /**
     * A function that returns the accessible label (`aria-label`) for each rating item's radio input. Receives the item's value and the total item count.
     *
     * @since 5.20.0
     */
    ariaLabel: {
      type: Function as PropType<(value: number, itemCount: number) => string>,
      default: (value: number, itemCount: number) => `${value} of ${itemCount}`,
    },
    /**
     * Toggle the disabled state for the component.
     */
    disabled: Boolean,
    /**
     * If enabled, only the currently selected icon will be visibly highlighted.
     */
    highlightOnlySelected: Boolean,
    /**
     * Specifies the total number of stars to be displayed in the star rating component. This property determines the scale of the rating, such as out of 5 stars, 10 stars, etc.
     */
    itemCount: {
      type: Number,
      default: 5,
    },
    /**
     * The default name for a value passed using v-model.
     */
    modelValue: Number,
    /**
     * The name attribute of the radio input elements.
     */
    name: String,
    /**
     * Minimum increment value change allowed.
     */
    precision: {
      type: Number,
      default: 1,
    },
    /**
     * Toggle the readonly state for the component.
     */
    readOnly: Boolean,
    /**
     * Size the component small, large, or custom if you define custom icons with custom height.
     *
     * @values 'sm', 'lg', 'custom'
     */
    size: {
      type: String,
      validator: (value: string) => {
        return ['sm', 'lg', 'custom'].includes(value)
      },
    },
    /**
     * Enable tooltips with default values or set specific labels for each icon.
     */
    tooltips: {
      type: [Boolean, Array] as PropType<boolean | string[]>,
    },
    /**
     * The `value` attribute of component.
     * */
    value: Number,
  },
  emits: [
    /**
     * Execute a function when a user changes the selected element.
     *
     * @property {number | null} value
     */
    'change',
    /**
     * Execute a function when a user hover the element.
     *
     * @property {number | null} value
     */
    'hover',
    /**
     * Emit the new value whenever there’s a change event.
     */
    'update:modelValue',
  ],
  setup(props, { slots, emit }) {
    const cleared = ref(false)
    const currentValue = ref((props.modelValue || props.value) ?? null)
    const hoverValue = ref<number | null>(null)
    const tooltipValue = ref<number | null>(null)
    const name = props.name || `name-${useId()}`
    const uid = `id-${useId()}`

    watch(
      () => props.value,
      () => {
        if (props.value !== undefined) {
          currentValue.value = props.value
        }
      }
    )

    watch(
      () => props.modelValue,
      () => {
        if (props.modelValue !== undefined) {
          currentValue.value = props.modelValue
        }
      }
    )

    const handleMouseEnter = (value: number) => {
      if (props.disabled || props.readOnly) {
        return
      }

      emit('hover', value)
      hoverValue.value = value
      tooltipValue.value = value
    }

    const handleMouseLeave = () => {
      if (props.disabled || props.readOnly) {
        return
      }

      emit('hover', null)
      hoverValue.value = null
    }

    const handleOnChange = (value: number) => {
      if (props.disabled || props.readOnly) {
        return
      }

      if (cleared.value) {
        cleared.value = false
        return
      }

      currentValue.value = value
      emit('change', value)
      emit('update:modelValue', value)
    }

    const handleOnClick = (value: number) => {
      if (props.disabled || props.readOnly) {
        return
      }

      if (props.allowClear && value === currentValue.value) {
        cleared.value = true
        currentValue.value = null
        hoverValue.value = null
        emit('change', null)
        emit('update:modelValue', null)
      }
    }

    return () =>
      h(
        'div',
        {
          class: [
            'rating',
            {
              [`rating-${props.size}`]: props.size,
              disabled: props.disabled,
              readonly: props.readOnly,
            },
          ],
          role: 'radiogroup',
        },
        [
          Array.from({ length: props.itemCount }, (_, index) => {
            const numberOfRadios = 1 / props.precision
            return withDirectives(
              h(
                'div',
                {
                  class: 'rating-item',
                },
                [
                  Array.from({ length: numberOfRadios }, (_, _index) => {
                    const isNotLastItem = _index + 1 < numberOfRadios
                    const value =
                      numberOfRadios === 1
                        ? index + 1
                        : index + (_index + 1) * (1 * props.precision)
                    const id = `${uid}${value}`

                    const isItemChecked = () => value === currentValue.value

                    const isItemActive = () => {
                      if (
                        props.highlightOnlySelected
                          ? hoverValue.value === value
                          : hoverValue.value && hoverValue.value >= value
                      ) {
                        return true
                      }

                      if (
                        hoverValue.value === null &&
                        (props.highlightOnlySelected
                          ? isItemChecked()
                          : currentValue.value && currentValue.value >= value)
                      ) {
                        return true
                      }

                      return false
                    }

                    return [
                      h(
                        'label',
                        {
                          class: [
                            'rating-item-label',
                            {
                              active: isItemActive(),
                            },
                          ],
                          for: id,
                          onClick: () => handleOnClick(value),
                          onMouseenter: () => handleMouseEnter(value),
                          onMouseleave: () => handleMouseLeave(),
                          ...(isNotLastItem && {
                            style: {
                              zIndex: 1 / props.precision - _index,
                              position: 'absolute',
                              width: `${props.precision * (_index + 1) * 100}%`,
                              overflow: 'hidden',
                              opacity: 0,
                            },
                          }),
                        },
                        {
                          default: () => [
                            slots.icon
                              ? h(
                                  'div',
                                  { class: 'rating-item-custom-icon' },
                                  slots.icon({ value: index + 1 })
                                )
                              : h('div', { class: 'rating-item-icon' }),
                            slots.activeIcon &&
                              h(
                                'div',
                                { class: 'rating-item-custom-icon-active' },
                                slots.activeIcon({ value: index + 1 })
                              ),
                          ],
                        }
                      ),
                      h('input', {
                        'aria-label': props.ariaLabel(value, props.itemCount),
                        checked: isItemChecked(),
                        class: 'rating-item-input',
                        disabled: props.disabled || props.readOnly,
                        id: id,
                        name: name,
                        onBlur: () => handleMouseLeave(),
                        onChange: () => handleOnChange(value),
                        onFocus: () => handleMouseEnter(value),
                        type: 'radio',
                        value: value,
                      }),
                    ]
                  }),
                ]
              ),
              props.tooltips
                ? [
                    [
                      vCTooltip,
                      {
                        content: Array.isArray(props.tooltips) ? props.tooltips[index] : index + 1,
                        placement: 'top',
                      },
                    ],
                  ]
                : []
            )
          }),
        ]
      )
  },
})
export { CRating }
