<template>
  <div class="base-progress">
    <div v-if="showPerText">
      <span class="text">{{ `${currDurationInfo.minute}:${currDurationInfo.second}` }}</span>
    </div>
    <div ref="progressRef" class="base-progress-outer" :style="progressBgStyle" @click="handleClick">
      <div class="base-progress-inner" :style="progressStyle"></div>
      <div
        v-if="showSlider"
        ref="sliderRef"
        class="base-progress-slider"
        :style="sliderStyle"
        @mousedown="handleMouseDown"
        @touchstart="handleTouchStart"
      ></div>
    </div>
    <div v-if="showPerText">
      <span class="text"> {{ `${totalDurationInfo.minute}:${totalDurationInfo.second}` }}</span>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { formatCountDown2Minute } from '@/utils/time';
  import { useThrottleFn } from '@vueuse/core';

  const props = defineProps({
    /** 总时长 （单位为毫秒） */
    duration: { type: Number, default: 0 },
    /** 是否显示滑块 */
    showSlider: { type: Boolean, default: true },
    /** 是否显示百分比文本 */
    showPerText: { type: Boolean, default: true },
    /** 进度条的宽度 */
    width: { type: Number, default: 300 },
    /** 进度条颜色 */
    progressColor: { type: String, default: '#409EFF' },
    /** 滑块的宽度 */
    sliderWidth: { type: Number, default: 20 },
    /** 当前播放时长（单位毫秒） */
    currDuration: { type: Number, default: 0 },
  });

  const emit = defineEmits<{
    (e: 'percentChange', percent: number): void;
  }>();

  /** 总时长 */
  const totalDurationInfo = computed(() => formatCountDown2Minute(props.duration));
  /** 当前到哪里（时长） */
  const currDurationInfo = computed(() => formatCountDown2Minute(props.currDuration));

  const sliderLeft = ref(0);
  /** 进度条宽度 */
  const progressWidth = ref(0);
  /** 当前进度 */
  const tempPercent = ref(0);
  /** 是否正在拖动 */
  const isDragging = ref(false);
  const progressRef = useTemplateRef('progressRef');
  const sliderRef = useTemplateRef('sliderRef');

  /** 得到进度条样式 */
  const progressBgStyle = computed(() => ({ width: `${props.width + props.sliderWidth}px` }));

  /** 得到进度条内层样式 */
  const progressStyle = computed(() => {
    return { width: `${progressWidth.value}px`, background: props.progressColor };
  });

  /** 获取滑块样式  */
  const sliderStyle = computed(() => {
    return { border: `1px solid ${props.progressColor}`, width: `${props.sliderWidth}px`, height: `${props.sliderWidth}px`, left: `${sliderLeft.value}px` };
  });

  /** 根据百分比更新滑块位置 */
  function updateSliderPositionByPercent(percent: number) {
    sliderLeft.value = (props.width / 100) * percent;
    progressWidth.value = sliderLeft.value + props.sliderWidth;
    tempPercent.value = percent;
  }

  /**
   * 计算当前百分比
   * @param isSelfChange - 是否是内部改变
   */
  function countCurPercent(isSelfChange: boolean) {
    tempPercent.value = Math.floor((sliderLeft.value / props.width) * 10000) / 100;
    progressWidth.value = sliderLeft.value + props.sliderWidth;

    // 取整的时候宽度可能不为0，所以在0和100的时候也将宽度取整
    if (tempPercent.value <= 0) {
      progressWidth.value = 0;
      sliderLeft.value = 0;
    }
    if (tempPercent.value >= 100) {
      progressWidth.value = props.width + props.sliderWidth;
      sliderLeft.value = props.width;
    }
    if (isSelfChange) {
      emit('percentChange', tempPercent.value);
    }
  }

  /**
   * 更新滑块位置
   * @param clientX - 鼠标位置
   * @param isSelfChange - 是否是内部改变
   */
  const updateSliderPosition = useThrottleFn(
    (clientX: number, isSelfChange: boolean) => {
      const rect = progressRef.value!.getBoundingClientRect();
      const offsetX = clientX - rect.left;

      // 计算滑块位置
      sliderLeft.value = offsetX - props.sliderWidth / 2;

      // 边界处理
      if (sliderLeft.value <= 0) {
        sliderLeft.value = 0;
      }
      if (sliderLeft.value >= props.width) {
        sliderLeft.value = props.width;
      }

      countCurPercent(isSelfChange);
    },
    Math.floor(1000 / 36)
  );

  /**
   * 点击事件处理
   */
  function handleClick(e: MouseEvent | TouchEvent) {
    // 如果正在拖拽，不处理点击事件
    if (isDragging.value) {
      return;
    }

    // 阻止事件冒泡（点击滑块时不触发）
    if (e.target === sliderRef.value) {
      return;
    }

    updateSliderPosition('clientX' in e ? e.clientX : e.touches[0]!.clientX, true);
  }

  /**
   * 移动处理
   */
  function handleMove(e: MouseEvent | TouchEvent) {
    if (!isDragging.value) {
      return;
    }
    e.preventDefault();

    updateSliderPosition('clientX' in e ? e.clientX : e.touches[0]!.clientX, true);
  }

  /**
   * 鼠标释放处理
   */
  function handleMouseUp(e: MouseEvent | TouchEvent) {
    if (!isDragging.value) {
      return;
    }
    e.preventDefault();
    isDragging.value = false;

    // 移除全局事件监听
    document.removeEventListener('mousemove', handleMove);
    document.removeEventListener('mouseup', handleMouseUp);
  }

  /**
   * 触摸结束处理
   */
  function handleTouchEnd(e: MouseEvent | TouchEvent) {
    if (!isDragging.value) {
      return;
    }
    e.preventDefault();
    isDragging.value = false;

    // 移除全局事件监听
    document.removeEventListener('touchmove', handleMove);
    document.removeEventListener('touchend', handleTouchEnd);
    document.removeEventListener('touchcancel', handleTouchEnd);
  }

  // PC端鼠标事件处理
  const handleMouseDown = (e: MouseEvent | TouchEvent) => {
    e.preventDefault();
    isDragging.value = true;
    // 阻止默认行为，防止拖拽时选中文本

    // 添加全局事件监听
    document.addEventListener('mousemove', handleMove);
    document.addEventListener('mouseup', handleMouseUp);
  };

  // 移动端触摸事件处理
  const handleTouchStart = (e: MouseEvent | TouchEvent) => {
    e.preventDefault();
    isDragging.value = true;

    // 添加全局事件监听
    document.addEventListener('touchmove', handleMove, { passive: false });
    document.addEventListener('touchend', handleTouchEnd);
    document.addEventListener('touchcancel', handleTouchEnd);
  };

  watch(
    () => props.currDuration,
    (newPercent) => {
      updateSliderPositionByPercent((newPercent / props.duration) * 100);
    }
  );

  // 监听 width 变化并调整进度
  watch(
    () => props.width,
    (_newWidth, oldWidth) => {
      // 计算进度比例
      const progressRatio = oldWidth ? tempPercent.value / 100 : 0;
      // 更新滑块位置
      updateSliderPositionByPercent(progressRatio * 100);
    }
  );

  // 组件挂载时初始化
  onMounted(() => {
    updateSliderPositionByPercent((props.currDuration / props.duration) * 100);
  });

  // 组件卸载前清理事件监听器
  onBeforeUnmount(() => {
    // 移除所有可能的事件监听器
    document.removeEventListener('mousemove', handleMove);
    document.removeEventListener('mouseup', handleMouseUp);
    document.removeEventListener('touchmove', handleMove);
    document.removeEventListener('touchend', handleTouchEnd);
    document.removeEventListener('touchcancel', handleTouchEnd);
  });
</script>

<style scoped lang="scss">
  $width: 300px;
  $radius: 5px;

  .base-progress {
    display: flex;
    align-items: center;
    user-select: none; // 防止拖拽时选中文本

    .text {
      font-size: 14px;
      color: #666666;
    }

    .base-progress-outer {
      position: relative;
      display: flex;
      flex: 1;
      align-items: center;
      width: $width;
      height: 10px;
      margin: 0 8px;
      cursor: pointer;
      background: #ebeef5;
      border-radius: $radius;

      .base-progress-inner {
        position: relative;
        z-index: 2;
        width: 100px;
        height: 10px;
        background: #409eff;
        border-radius: $radius;
      }

      .base-progress-slider {
        position: absolute;
        left: 10px;
        z-index: 10;
        width: 20px;
        height: 20px;
        touch-action: none; // 阻止触摸时的默认行为
        cursor: grab;
        background: #ffffff;
        border: 1px solid #f1e1dc;
        border-radius: 50%;

        &:active {
          cursor: grabbing;
        }
      }
    }
  }
</style>
