<template>
  <div class="vue-calendar-component" :class="{ theme2: theme !== 1 }">
    <template v-for="(item, index) in dateArray" :key="index">
      <rest-page-base :item="item" :itemT="dateArrayT[index]!" :isAnimate="isAnimate[index]!" @animate-end="onAnimateEnd(index)"></rest-page-base>
      <div v-if="isDateUnitShow(index)" :key="`unit-${index}`" class="time-unit">
        {{ setDateUnit(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 {
    /** 日期精度 6/5/4/3/2/1，分别表示显示年月日时分秒/月日时分秒/日时分秒/时分秒/分秒/秒，默认为6 */
    type?: 1 | 2 | 3 | 4 | 5 | 6;
    /** 主题样式，1为默认样式，其他值为另一种样式，默认为1 */
    theme?: number;
    /** 日期单位文字数组，分别对应年、月、日、时、分、秒的单位显示文字，默认为空数组 */
    dateUnit?: string[];
  }

  /**
   * 组件属性定义
   */
  const props = withDefaults(defineProps<Props>(), {
    type: 6,
    theme: 2,
    dateUnit: () => ['年', '月', '日', '时', '分', '秒'],
  });

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

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

  /**
   * 日期数组临时值，用于动画过渡
   */
  const dateArrayT: 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>();

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

  /**
   * 计算日期单位索引数组
   */
  const arr = computed(() => {
    const length = dateArray.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(dateArray, (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) => {
        dateArrayT.value[item.index] = item.value;
      });
    }, 350);
  });

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

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

  /**
   * 更新日期时间显示
   */
  function updateDateTime() {
    clearInterval(timer.value);
    setTime();
    // 每秒更新一次时间
    timer.value = setInterval(() => {
      setTime();
    }, 1000);
  }

  /**
   * 设置时间
   */
  function setTime() {
    const currentDateTime = new Date(); // 获取当前日期时间
    const year = currentDateTime.getFullYear(); // 年
    const month = currentDateTime.getMonth() + 1; // 月 (月份从0开始，所以需要+1)
    const day = currentDateTime.getDate(); // 日
    const hour = currentDateTime.getHours(); // 时
    const minute = currentDateTime.getMinutes(); // 分
    const second = currentDateTime.getSeconds(); // 秒

    const type = Number(props.type);

    const ar: string[] = [];
    if (Number(props.theme) === 1) {
      // 不分开
      type >= 6 && ar.push(String(year).padStart(4, '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(minute).padStart(2, '0'));
      ar.push(String(second).padStart(2, '0'));
    } else {
      // 分开
      type >= 6 && ar.push(...String(year).padStart(4, '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(minute).padStart(2, '0').split(''));
      ar.push(...String(second).padStart(2, '0').split(''));
    }

    dateArray.value = ar;

    emit('update');
  }

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

  /**
   * 判断是否显示日期单位文字
   * @param index - 日期单位索引
   * @returns 是否显示日期单位
   */
  const isDateUnitShow = (index: number) => {
    if (arr.value.includes(index)) {
      // 对于秒单位，如果没有设置单位文字，则不显示
      if (index === dateArray.value.length - 1 && !props.dateUnit[5]) {
        return false;
      }
      return true;
    }
    return false;
  };

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

<style lang="scss" scoped>
  .vue-calendar-component {
    --bg: rgb(3, 152, 252);
    --bg2: rgba(93, 166, 250, 0.8);
    --color: #ffffff;
    --height: 26px;
    --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);
      color: #222222;
      white-space: nowrap;
    }
  }
</style>
