// Styles
import './VSlider.sass'

// Components
import { VSliderThumb } from './VSliderThumb'
import { VSliderTrack } from './VSliderTrack'
import { makeVInputProps, VInput } from '@/components/VInput/VInput'
import { VLabel } from '@/components/VLabel'

// Composables
import { makeSliderProps, useSlider, useSteps } from './slider'
import { makeFocusProps, useFocus } from '@/composables/focus'
import { forwardRefs } from '@/composables/forwardRefs'
import { useRtl } from '@/composables/locale'
import { useProxiedModel } from '@/composables/proxiedModel'

// Utilities
import { computed, ref } from 'vue'
import { filterInputAttrs, genericComponent, propsFactory, useRender } from '@/util'

// Types
import type { VSliderThumbSlots } from './VSliderThumb'
import type { VSliderTrackSlots } from './VSliderTrack'
import type { VInputSlot, VInputSlots } from '@/components/VInput/VInput'

export type VSliderSlots = VInputSlots & VSliderThumbSlots & VSliderTrackSlots & {
  label: VInputSlot
}

export const makeVSliderProps = propsFactory({
  ...makeFocusProps(),
  ...makeSliderProps(),
  ...makeVInputProps(),

  modelValue: {
    type: [Number, String],
    default: 0,
  },
}, 'VSlider')

export const VSlider = genericComponent<VSliderSlots>()({
  name: 'VSlider',

  inheritAttrs: false,

  props: makeVSliderProps(),

  emits: {
    'update:focused': (value: boolean) => true,
    'update:modelValue': (v: number) => true,
    start: (value: number) => true,
    end: (value: number) => true,
  },

  setup (props, { slots, emit, attrs }) {
    const thumbContainerRef = ref<VSliderThumb>()
    const inputRef = ref<VInput>()
    const { rtlClasses } = useRtl()

    const steps = useSteps(props)

    const model = useProxiedModel(
      props,
      'modelValue',
      undefined,
      value => {
        return steps.roundValue(value == null ? steps.min.value : value)
      },
    )

    const {
      min,
      max,
      mousePressed,
      roundValue,
      onSliderMousedown,
      onSliderTouchstart,
      trackContainerRef,
      position,
      hasLabels,
      disabled,
      readonly,
      noKeyboard,
    } = useSlider({
      props,
      steps,
      onSliderStart: () => {
        if (!disabled.value && !readonly.value) {
          emit('start', model.value)
        }
      },
      onSliderEnd: ({ value }) => {
        const roundedValue = roundValue(value)

        if (!disabled.value && !readonly.value) {
          model.value = roundedValue
        }

        emit('end', roundedValue)
      },
      onSliderMove: ({ value }) => {
        if (!disabled.value && !readonly.value) {
          model.value = roundValue(value)
        }
      },
      getActiveThumb: () => thumbContainerRef.value?.$el,
    })

    const { isFocused, focus, blur } = useFocus(props)
    const trackStop = computed(() => position(model.value))

    useRender(() => {
      const inputProps = VInput.filterProps(props)
      const [rootAttrs, inputAttrs] = filterInputAttrs(attrs)
      const hasPrepend = !!(props.label || slots.label || slots.prepend)

      return (
        <VInput
          ref={ inputRef }
          class={[
            'v-slider',
            {
              'v-slider--has-labels': !!slots['tick-label'] || hasLabels.value,
              'v-slider--focused': isFocused.value,
              'v-slider--pressed': mousePressed.value,
              'v-slider--disabled': disabled.value,
            },
            rtlClasses.value,
            props.class,
          ]}
          style={ props.style }
          { ...inputProps }
          { ...rootAttrs }
          focused={ isFocused.value }
        >
          {{
            ...slots,
            prepend: hasPrepend ? slotProps => (
              <>
                { slots.label?.(slotProps) ?? (
                  props.label
                    ? (
                      <VLabel
                        id={ slotProps.id.value }
                        class="v-slider__label"
                        text={ props.label }
                      />
                    ) : undefined
                )}

                { slots.prepend?.(slotProps) }
              </>
            ) : undefined,
            default: ({ id, messagesId }) => (
              <div
                class="v-slider__container"
                onMousedown={ !readonly.value ? onSliderMousedown : undefined }
                onTouchstartPassive={ !readonly.value ? onSliderTouchstart : undefined }
              >
                <input
                  id={ id.value }
                  name={ props.name || id.value }
                  disabled={ disabled.value }
                  readonly={ readonly.value }
                  tabindex="-1"
                  value={ model.value }
                />

                <VSliderTrack
                  ref={ trackContainerRef }
                  start={ 0 }
                  stop={ trackStop.value }
                >
                  {{ 'tick-label': slots['tick-label'] }}
                </VSliderTrack>

                <VSliderThumb
                  ref={ thumbContainerRef }
                  aria-describedby={ messagesId.value }
                  focused={ isFocused.value }
                  noKeyboard={ noKeyboard.value }
                  min={ min.value }
                  max={ max.value }
                  modelValue={ model.value }
                  onUpdate:modelValue={ v => (model.value = v) }
                  position={ trackStop.value }
                  elevation={ props.elevation }
                  onFocus={ focus }
                  onBlur={ blur }
                  ripple={ props.ripple }
                  name={ props.name }
                  { ...inputAttrs }
                >
                  {{ 'thumb-label': slots['thumb-label'] }}
                </VSliderThumb>
              </div>
            ),
          }}
        </VInput>
      )
    })

    return forwardRefs({
      focus: () => thumbContainerRef.value?.$el.focus(),
    }, inputRef)
  },
})

export type VSlider = InstanceType<typeof VSlider>
