<template>
  <div class="vue-countdown-component" :class="{ theme2: theme !== 1 }">
    <template v-for="(item, index) in timeArray" :key="index">
      <rest-page-base :item="item" :itemT="timeArrayT[index]!" :isAnimate="isAnimate[index]!" @animate-end="onAnimateEnd(index)"></rest-page-base>
      <!-- 文字 -->
      <div v-if="isTimeUnitShow(index)" :key="`unit-${index}`" class="time-unit">
        {{ setTimeUnit(index) }}
      </div>
    </template>
  </div>
</template>

<script setup lang="ts">
  import { type Ref, computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
  import RestPageBase from '@/components/rest/rest-page-base/rest-page-base.vue';

  /**
   * 翻页计时器组件的属性接口
   */
  interface Props {
    /** 开始时间，可以是Date对象、时间戳数字或可转换为时间戳的字符串 */
    startDate?: Date | number | string;
    /** 时间精度 6/5/4/3/2/1，分别表示显示年月日时分秒/月日时分秒/日时分秒/时分秒/分秒/秒，默认为4 */
    type?: 1 | 2 | 3 | 4 | 5 | 6;
    /** 主题样式，1为默认样式，其他值为另一种样式，默认为1 */
    theme?: number;
    /** 时间单位文字数组，分别对应年、月、日、时、分、秒的单位显示文字，默认为空数组 */
    timeUnit?: string[];
  }

  /**
   * 组件属性定义
   */
  const props = withDefaults(defineProps<Props>(), {
    startDate: 0,
    type: 4,
    theme: 2,
    timeUnit: () => ['年', '月', '天', ':', ':', ''],
  });

  /**
   * 组件事件发射器
   */
  const emit = defineEmits<{
    /** 计时器更新时触发的事件 */
    (e: 'update'): void;
  }>();

  /**
   * 当前时间数组，根据主题不同格式存储时间值
   */
  const timeArray: Ref<string[]> = ref(props.theme === 2 ? new Array(props.type * 2).fill('0') : new Array(props.type).fill('00'));

  /**
   * 时间数组临时值，用于动画过渡
   */
  const timeArrayT: Ref<string[]> = ref(props.theme === 2 ? new Array(props.type * 2).fill('0') : new Array(props.type).fill('00'));

  /**
   * 动画状态数组，标记每个时间单位是否正在进行动画
   */
  const isAnimate: Ref<boolean[]> = ref(props.theme === 2 ? new Array(props.type * 2).fill(false) : new Array(props.type).fill(false));

  /**
   * 计时器定时器
   */
  const timer = ref<NodeJS.Timeout>();

  /**
   * 计算开始时间的时间戳
   */
  const startTime = computed(() => {
    if (props.startDate instanceof Date) {
      return props.startDate.getTime();
    }
    return Number(props.startDate) > 0 ? Number(props.startDate) : Date.now();
  });

  /**
   * 计算步长，主题1为1，其他为2
   */
  const step = computed(() => (props.theme === 1 ? 1 : 2));

  /**
   * 计算时间单位索引数组
   */
  const arr = computed(() => {
    const length = timeArray.value.length;
    const temp = [
      length - 1,
      length - step.value - 1,
      length - step.value * 2 - 1,
      length - step.value * 3 - 1,
      length - step.value * 4 - 1,
      length - step.value * 5 - 1,
    ];
    temp.length = props.type > 1 ? props.type : 1;
    return temp;
  });

  /**
   * 监听时间数组变化，触发动画效果
   * @param newV - 新的时间数组
   * @param oldV - 旧的时间数组
   */
  watch(timeArray, (newV, oldV) => {
    const diff: { value: string; index: number }[] = [];
    newV.forEach((value, index) => {
      if (value !== oldV[index]) {
        diff.push({ value, index });
        isAnimate.value[index] = true;
      }
    });
    setTimeout(() => {
      diff.forEach((item) => {
        timeArrayT.value[item.index] = item.value;
      });
    }, 350);
  });

  /**
   * 监听开始时间变化，如果时间有效则开始计时
   * @param newV - 新的开始时间戳
   */
  watch(startTime, (newV) => {
    if (newV > 0) {
      start();
    }
  });

  /**
   * 组件挂载完成后初始化
   */
  onMounted(() => {
    start(0);
  });

  /**
   * 组件卸载前清理定时器
   */
  onBeforeUnmount(() => {
    clearTimeout(timer.value);
  });

  /**
   * 开始计时
   * @param st - 重复执行的间隔时间，默认为1000ms
   */
  function start(st = 1000) {
    clearTimeout(timer.value);
    timer.value = setTimeout(() => {
      let t = new Date().getTime() - startTime.value; // 已过去的毫秒数
      t = t < 0 ? 0 : t;
      let year = 0; // 已过去的年
      let month = 0; // 已过去的月
      let day = 0; // 已过去的天
      let hour = 0; // 已过去的小时
      let min = 0; // 已过去的分钟
      let second = 0; // 已过去的秒
      const type = Number(props.type);

      // 简化的年月日计算（实际项目中可能需要更复杂的处理）
      if (type >= 6) {
        year = Math.floor(t / (365 * 24 * 3600 * 1000)); // 已过去的年
        t -= year * (365 * 24 * 3600 * 1000);
      }

      if (type >= 5) {
        month = Math.floor(t / (30 * 24 * 3600 * 1000)); // 已过去的月
        t -= month * (30 * 24 * 3600 * 1000);
      }

      if (type >= 4) {
        day = Math.floor(t / 86400000); // 已过去的天
        hour = Math.floor(t / 3600000 - day * 24); // 已过去的小时 已排除天
        min = Math.floor(t / 60000 - day * 1440 - hour * 60); // 已过去的分钟 已排除天和小时
        second = Math.floor(t / 1000 - day * 86400 - hour * 3600 - min * 60); // 已过去的秒
      } else if (type >= 3) {
        hour = Math.floor(t / 3600000); // 已过去的小时
        min = Math.floor(t / 60000 - hour * 60); // 已过去的分钟 已排小时
        second = Math.floor(t / 1000 - hour * 3600 - min * 60); // 已过去的秒
      } else if (type >= 2) {
        min = Math.floor(t / 60000); // 已过去的分钟
        second = Math.floor(t / 1000 - min * 60); // 已过去的秒
      } else {
        second = Math.floor(t / 1000); // 已过去的秒
      }

      const ar: string[] = [];
      if (Number(props.theme) === 1) {
        // 不分开
        type >= 6 && ar.push(String(year).padStart(2, '0'));
        type >= 5 && ar.push(String(month).padStart(2, '0'));
        type >= 4 && ar.push(String(day).padStart(2, '0'));
        type >= 3 && ar.push(String(hour).padStart(2, '0'));
        type >= 2 && ar.push(String(min).padStart(2, '0'));
        ar.push(String(second).padStart(2, '0'));
      } else {
        // 分开
        type >= 6 && ar.push(...String(year).padStart(2, '0').split(''));
        type >= 5 && ar.push(...String(month).padStart(2, '0').split(''));
        type >= 4 && ar.push(...String(day).padStart(2, '0').split(''));
        type >= 3 && ar.push(...String(hour).padStart(2, '0').split(''));
        type >= 2 && ar.push(...String(min).padStart(2, '0').split(''));
        ar.push(...String(second).padStart(2, '0').split(''));
      }
      timeArray.value = ar;

      emit('update');
      start();
    }, st);
  }

  /**
   * 动画完毕后，去掉对应的class, 为下次动画做准备
   * @param index - 时间单位索引
   */
  const onAnimateEnd = (index: number) => {
    isAnimate.value[index] = false;
  };

  /**
   * 判断是否显示时间单位文字
   * @param index - 时间单位索引
   * @returns 是否显示时间单位
   */
  const isTimeUnitShow = (index: number) => {
    if (arr.value.includes(index)) {
      if (index === timeArray.value.length - 1 && !props.timeUnit[5]) {
        return false;
      }
      return true;
    }
    return false;
  };

  /**
   * 设置时间单位文字
   * @param index - 时间单位索引
   * @returns 对应的时间单位文字
   */
  const setTimeUnit = (index: number) => {
    switch (index) {
      case timeArray.value.length - 1:
        return props.timeUnit[5] || ''; // 秒
      case timeArray.value.length - step.value - 1:
        return props.timeUnit[4] || ''; // 分
      case timeArray.value.length - step.value * 2 - 1:
        return props.timeUnit[3] || ''; // 时
      case timeArray.value.length - step.value * 3 - 1:
        return props.timeUnit[2] || ''; // 天
      case timeArray.value.length - step.value * 4 - 1:
        return props.timeUnit[1] || ''; // 月
      default:
        return props.timeUnit[0] || ''; // 年
    }
  };
</script>

<style lang="scss" scoped>
  .vue-countdown-component {
    --bg: rgb(3, 152, 252);
    --bg2: rgba(93, 166, 250, 0.8);
    --color: #ffffff;
    --height: 26px;
    --padding: 3px;
    --font-size: 20px;
    --unit-font-size: 16px;
    --unit-padding: 3px;
    display: flex;
    align-items: center;
    justify-content: center;

    &.theme2 .time-box + .time-box {
      margin-left: 1px;
    }

    .time-unit {
      padding: 0 var(--unit-padding);
      font-size: var(--unit-font-size);
      line-height: 30px;
      color: #222222;
      white-space: nowrap;
    }
  }
</style>
