<template>
  <view :class="rootClass" :style="customStyle" :id="sliderId">
    <!-- #ifdef MP-DINGTALK -->
    <view :id="sliderId" style="flex: 1" :class="rootClass">
      <!-- #endif -->
      <view :class="`wd-slider__label-min ${customMinClass}`" v-if="!hideMinMax">
        {{ minValue }}
      </view>
      <view class="wd-slider__bar-wrapper" :style="barWrapperStyle">
        <view class="wd-slider__bar" :style="barCustomStyle"></view>
        <!-- 左边 -->
        <view
          class="wd-slider__button-wrapper"
          :style="buttonLeftStyle"
          @touchstart="onTouchStart"
          @touchmove="onTouchMove"
          @touchend="onTouchEnd"
          @touchcancel="onTouchEnd"
        >
          <view class="wd-slider__label" v-if="!hideLabel">{{ leftNewValue }}</view>
          <view class="wd-slider__button" />
        </view>
        <!-- 右边 -->
        <view
          v-if="showRight"
          class="wd-slider__button-wrapper"
          :style="buttonRightStyle"
          @touchstart="onTouchStartRight"
          @touchmove="onTouchMoveRight"
          @touchend="onTouchEndRight"
          @touchcancel="onTouchEndRight"
        >
          <view class="wd-slider__label" v-if="!hideLabel">{{ rightNewValue }}</view>
          <view class="wd-slider__button" />
        </view>
      </view>
      <view :class="`wd-slider__label-max ${customMaxClass}`" v-if="!hideMinMax">
        {{ maxValue }}
      </view>
      <!-- #ifdef MP-DINGTALK -->
    </view>
    <!-- #endif -->
  </view>
</template>

<script lang="ts">
export default {
  name: 'wd-slider',
  options: {
    addGlobalClass: true,
    virtualHost: true,
    styleIsolation: 'shared'
  }
}
</script>

<script lang="ts" setup>
import { computed, getCurrentInstance, onMounted, ref } from 'vue'
import { getRect, isArray, isDef, isNumber, uuid } from '../common/util'
import { useTouch } from '../composables/useTouch'
import { watch } from 'vue'
import { sliderProps, type SliderExpose } from './types'

const props = defineProps(sliderProps)
const emit = defineEmits(['dragstart', 'dragmove', 'dragend', 'update:modelValue'])

// 存放右滑轮中的所有属性
const rightSlider = {
  startValue: 0,
  deltaX: 0,
  newValue: 0
}
const sliderId = ref<string>(`sliderId${uuid()}`)

const touchLeft = useTouch()
const touchRight = useTouch()

const showRight = ref<boolean>(false)
const barStyle = ref<string>('')
const leftNewValue = ref<number>(0)
const rightNewValue = ref<number>(0)
const rightBarPercent = ref<number>(0)
const leftBarPercent = ref<number>(0)
const trackWidth = ref<number>(0)
const trackLeft = ref<number>(0)
const startValue = ref<number>(0)
const currentValue = ref<number | number[]>(0)
const newValue = ref<number>(0)

const maxValue = ref<number>(100) // 最大值
const minValue = ref<number>(0) // 最小值
const stepValue = ref<number>(1) // 步长

watch(
  () => props.max,
  (newValue) => {
    if (newValue <= props.min) {
      maxValue.value = props.min // 交换最大值和最小值
      minValue.value = newValue
      console.warn('[wot design] warning(wd-slider): max value must be greater than min value')
    } else {
      maxValue.value = newValue // 更新最大值
    }
    calcBarPercent()
  },
  { immediate: true }
)

watch(
  () => props.min,
  (newValue) => {
    if (newValue >= props.max) {
      minValue.value = props.max // 交换最小值和最大值
      maxValue.value = newValue
      console.warn('[wot design] warning(wd-slider): min value must be less than max value')
    } else {
      minValue.value = newValue // 更新最小值
    }
    calcBarPercent()
  },
  { immediate: true }
)

watch(
  () => props.step,
  (newValue) => {
    if (newValue < 1) {
      stepValue.value = 1
      console.warn('[wot design] warning(wd-slider): step must be greater than 0')
    } else {
      stepValue.value = Math.floor(newValue)
    }
  },
  { immediate: true }
)

watch(
  () => props.modelValue,
  (newValue, oldValue) => {
    // 类型校验，支持所有值(除null、undefined。undefined建议统一写成void (0)防止全局undefined被覆盖)
    if (newValue === null || newValue === undefined) {
      emit('update:modelValue', oldValue)
      console.warn('[wot design] warning(wd-slider): value can nott be null or undefined')
    } else if (isArray(newValue) && newValue.length !== 2) {
      console.warn('[wot design] warning(wd-slider): value must be dyadic array')
    } else if (!isNumber(newValue) && !isArray(newValue)) {
      emit('update:modelValue', oldValue)
      console.warn('[wot design] warning(wd-slider): value must be dyadic array Or Number')
    }
    currentValue.value = newValue
    // 动态传值后修改
    if (isArray(newValue)) {
      if (oldValue && isArray(oldValue) && equal(newValue, oldValue)) return
      showRight.value = true
      if (leftBarPercent.value <= rightBarPercent.value) {
        leftBarSlider(newValue[0])
        rightBarSlider(newValue[1])
      } else {
        leftBarSlider(newValue[1])
        rightBarSlider(newValue[0])
      }
    } else {
      if (newValue === oldValue) return
      leftBarSlider(newValue)
    }
  },
  { deep: true, immediate: true }
)

const { proxy } = getCurrentInstance() as any

const rootClass = computed(() => {
  const rootClass = `wd-slider ${!props.hideLabel ? 'wd-slider__has-label' : ''} ${props.disabled ? 'wd-slider--disabled' : ''} ${props.customClass}`
  return rootClass
})

const barWrapperStyle = computed(() => {
  const barWrapperStyle = `${props.inactiveColor ? 'background:' + props.inactiveColor : ''}`
  return barWrapperStyle
})

const barCustomStyle = computed(() => {
  const barWrapperStyle = `${barStyle.value};${props.activeColor ? 'background:' + props.activeColor : ''}`
  return barWrapperStyle
})

const buttonLeftStyle = computed(() => {
  const buttonLeftStyle = `left: ${leftBarPercent.value}%; visibility: ${!props.disabled ? 'show' : 'hidden'};`
  return buttonLeftStyle
})

const buttonRightStyle = computed(() => {
  const buttonRightStyle = `left: ${rightBarPercent.value}%; visibility: ${!props.disabled ? 'show' : 'hidden'}`
  return buttonRightStyle
})

onMounted(() => {
  initSlider()
})

/**
 * 初始化slider宽度
 */
function initSlider() {
  getRect(`#${sliderId.value}`, false, proxy).then((data) => {
    // trackWidth: 轨道全长
    trackWidth.value = Number(data.width)
    // trackLeft: 轨道距离左侧的距离
    trackLeft.value = Number(data.left)
  })
}

function onTouchStart(event: any) {
  const { disabled, modelValue } = props
  if (disabled) return
  touchLeft.touchStart(event)
  startValue.value = !isArray(modelValue)
    ? format(modelValue)
    : leftBarPercent.value < rightBarPercent.value
    ? format(modelValue[0])
    : format(modelValue[1])
  emit('dragstart', {
    value: currentValue.value
  })
}
function onTouchMove(event: any) {
  const { disabled } = props
  if (disabled) return
  touchLeft.touchMove(event)
  // 移动间距 deltaX 就是向左(-)向右(+)
  const diff = (touchLeft.deltaX.value / trackWidth.value) * (maxValue.value - minValue.value)
  newValue.value = startValue.value + diff

  // 左滑轮滑动控制
  leftBarSlider(newValue.value)
  emit('dragmove', {
    value: currentValue.value
  })
}
function onTouchEnd() {
  if (props.disabled) return
  emit('dragend', {
    value: currentValue.value
  })
}
// 右边滑轮滑动状态监听
function onTouchStartRight(event: any) {
  if (props.disabled) return
  const { modelValue } = props
  // 右滑轮移动时数据绑定
  touchRight.touchStart(event)
  // 记录开始数据值
  rightSlider.startValue = leftBarPercent.value < rightBarPercent.value ? format((modelValue as number[])[1]) : format((modelValue as number[])[0])
  emit('dragstart', {
    value: currentValue.value
  })
}
function onTouchMoveRight(event: any) {
  if (props.disabled) return
  touchRight.touchMove(event)
  // 移动间距 deltaX 就是向左向右
  const diff = (touchRight.deltaX.value / trackWidth.value) * (maxValue.value - minValue.value)
  rightSlider.newValue = format(rightSlider.startValue + diff)
  // 右滑轮滑动控制
  rightBarSlider(rightSlider.newValue)
  emit('dragmove', {
    value: currentValue.value
  })
}
function onTouchEndRight() {
  if (props.disabled) return
  emit('dragend', {
    value: currentValue.value
  })
}
/**
 * 控制右侧滑轮滑动， value校验
 * @param {Number} value 当前滑轮绑定值
 */
function rightBarSlider(value: number) {
  value = format(value)
  rightNewValue.value = value
  emit('update:modelValue', [Math.min(leftNewValue.value, rightNewValue.value), Math.max(leftNewValue.value, rightNewValue.value)])
  rightBarPercent.value = formatPercent(value)
  styleControl()
}
/**
 * 控制左滑轮滑动，更新渲染数据，对 value 进行校验取整
 * @param {Number} value 当前滑轮绑定值
 */
function leftBarSlider(value: number) {
  value = format(value)

  // 把 value 转换成百分比
  const percent = formatPercent(value)
  if (!showRight.value) {
    emit('update:modelValue', value)
    leftNewValue.value = value
    leftBarPercent.value = percent
    barStyle.value = `width: ${percent}%; `
  } else {
    leftNewValue.value = value
    leftBarPercent.value = percent
    emit('update:modelValue', [Math.min(leftNewValue.value, rightNewValue.value), Math.max(leftNewValue.value, rightNewValue.value)])
    styleControl()
  }
}
// 样式控制
function styleControl() {
  // 左右滑轮距离左边最短为当前激活条所处位置
  const barLeft =
    leftBarPercent.value < rightBarPercent.value ? [leftBarPercent.value, rightBarPercent.value] : [rightBarPercent.value, leftBarPercent.value]
  // 通过左右滑轮的间距控制 激活条宽度 barLeft[1] - barLeft[0]
  const barStyleTemp = `width: ${barLeft[1] - barLeft[0]}%;  left: ${barLeft[0]}%`
  currentValue.value =
    leftNewValue.value < rightNewValue.value ? [leftNewValue.value, rightNewValue.value] : [rightNewValue.value, leftNewValue.value]
  barStyle.value = barStyleTemp
}

function equal(arr1: number[], arr2: number[]) {
  if (!isDef(arr1) || !isDef(arr2)) {
    return false
  }
  let i = 0
  arr1.forEach((item, index) => {
    item === arr2[index] && i++
  })
  return i === 2
}
function format(value: number) {
  const formatValue = Math.round(Math.max(minValue.value, Math.min(value, maxValue.value)) / stepValue.value) * stepValue.value
  return formatValue
}

// 计算占比
function formatPercent(value: number) {
  const percentage = ((value - minValue.value) / (maxValue.value - minValue.value)) * 100
  return percentage
}
// 计算滑块位置和进度长度
function calcBarPercent() {
  const { modelValue } = props
  let value = !isArray(modelValue) ? format(modelValue) : leftBarPercent.value < rightBarPercent.value ? format(modelValue[0]) : format(modelValue[1])
  value = format(value)
  // 把 value 转换成百分比
  const percent = formatPercent(value)
  leftBarPercent.value = percent
  barStyle.value = `width: ${percent}%; `
}

defineExpose<SliderExpose>({
  initSlider
})
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>
