<template>
  <div class="grassland-ndvi-timeline" :class="{ 'is-dense': denseMode }">
    <div class="grassland-ndvi-timeline-head">
      <span>草地健康/退化</span>
      <strong>{{ activeItem?.label || statusText }}</strong>
    </div>

    <div v-if="loading" class="grassland-ndvi-timeline-state">加载月份数据</div>
    <div v-else-if="!items.length" class="grassland-ndvi-timeline-state">暂无月份数据</div>
    <div v-else class="grassland-ndvi-timeline-body">
      <button class="grassland-ndvi-timeline-nav" type="button" title="上一期" @click="step(-1)">‹</button>
      <div
        class="grassland-ndvi-timeline-track"
        :style="{
          '--timeline-active-left': activePosition,
          '--timeline-fill-width': timelineFillWidth,
          '--timeline-edge-offset': `${timelineEdgeOffset}px`,
        }"
        role="radiogroup"
        aria-label="草地健康退化月份"
      >
        <div class="grassland-ndvi-timeline-line"></div>
        <div class="grassland-ndvi-timeline-fill"></div>
        <div class="grassland-ndvi-timeline-thumb">
          <i></i>
        </div>
        <button
          v-for="(item, index) in items"
          :key="item.key"
          class="grassland-ndvi-timeline-mark"
          :class="{
            'is-active': item.key === activeItem?.key,
            'is-past': index <= currentIndex,
            'is-staggered': denseMode && index % 2 === 1,
          }"
          type="button"
          role="radio"
          :aria-checked="item.key === activeItem?.key"
          :style="{ left: getItemPosition(index) }"
          :title="item.date"
          @click="selectItem(item)"
        >
          <span class="grassland-ndvi-timeline-tick"></span>
          <span class="grassland-ndvi-timeline-label">{{ item.label }}</span>
        </button>
      </div>
      <button class="grassland-ndvi-timeline-nav" type="button" title="下一期" @click="step(1)">›</button>
    </div>
  </div>
</template>

<script setup>
import { computed } from "vue"

const props = defineProps({
  items: {
    type: Array,
    default: () => [],
  },
  modelValue: {
    type: String,
    default: "",
  },
  loading: {
    type: Boolean,
    default: false,
  },
})

const emit = defineEmits(["update:modelValue"])
const TIMELINE_EDGE_OFFSET = 42
const DENSE_TIMELINE_EDGE_OFFSET = 28

const activeIndex = computed(() => props.items.findIndex((item) => item.key === props.modelValue))
const currentIndex = computed(() => activeIndex.value === -1 ? Math.max(0, props.items.length - 1) : activeIndex.value)
const activeItem = computed(() => props.items[currentIndex.value] || null)
const statusText = computed(() => (props.loading ? "加载中" : "未选择"))
const denseMode = computed(() => props.items.length > 7)
const timelineEdgeOffset = computed(() => denseMode.value ? DENSE_TIMELINE_EDGE_OFFSET : TIMELINE_EDGE_OFFSET)
const activePosition = computed(() => getItemPosition(currentIndex.value))
const timelineFillWidth = computed(() => getTimelineFillWidth(currentIndex.value))

function step(direction) {
  if (!props.items.length) return
  const nextIndex = Math.min(props.items.length - 1, Math.max(0, currentIndex.value + direction))
  emit("update:modelValue", props.items[nextIndex].key)
}

function selectItem(item) {
  if (!item || item.key === props.modelValue) return
  emit("update:modelValue", item.key)
}

function getItemRatio(index) {
  if (!props.items.length || index < 0) return 0
  if (props.items.length === 1) return 0.5
  return index / (props.items.length - 1)
}

function getItemPosition(index) {
  const ratio = getItemRatio(index)
  const percent = formatCssNumber(ratio * 100)
  const offset = timelineEdgeOffset.value * (1 - ratio * 2)
  return `calc(${percent}% ${offset < 0 ? "-" : "+"} ${formatCssNumber(Math.abs(offset))}px)`
}

function getTimelineFillWidth(index) {
  const ratio = getItemRatio(index)
  if (ratio <= 0) return "0px"
  return `calc(${formatCssNumber(ratio * 100)}% - ${formatCssNumber(timelineEdgeOffset.value * 2 * ratio)}px)`
}

function formatCssNumber(value) {
  return Number(value.toFixed(4)).toString()
}
</script>

<style lang="scss" scoped>
.grassland-ndvi-timeline {
  width: 680px;
  height: 68px;
  box-sizing: border-box;
  padding: 8px 12px 9px;
  border: 1px solid rgba(72, 226, 255, 0.28);
  background:
    linear-gradient(180deg, rgba(6, 36, 50, 0.76), rgba(3, 18, 30, 0.66)),
    linear-gradient(90deg, rgba(28, 191, 255, 0.08), rgba(38, 246, 198, 0.12), rgba(28, 191, 255, 0.08));
  backdrop-filter: blur(6px);
  box-shadow:
    inset 0 1px 0 rgba(187, 250, 255, 0.12),
    inset 0 0 18px rgba(28, 191, 255, 0.08),
    0 10px 28px rgba(0, 12, 26, 0.34);
  color: #efffff;
  pointer-events: all;

  &.is-dense {
    width: 740px;
    height: 86px;
    padding: 8px 14px 10px;

    .grassland-ndvi-timeline-head {
      margin-bottom: 5px;
    }

    .grassland-ndvi-timeline-body {
      align-items: start;
    }

    .grassland-ndvi-timeline-track {
      height: 52px;
      margin: 0 4px;
    }

    .grassland-ndvi-timeline-line,
    .grassland-ndvi-timeline-fill,
    .grassland-ndvi-timeline-thumb {
      top: 13px;
    }

    .grassland-ndvi-timeline-mark {
      width: 70px;
      height: 52px;

      .grassland-ndvi-timeline-tick {
        top: 8px;
        height: 12px;
      }

      .grassland-ndvi-timeline-label {
        top: 24px;
        width: 68px;
        font-size: 11px;
        line-height: 15px;
      }

      &.is-staggered {
        .grassland-ndvi-timeline-label {
          top: 38px;
        }
      }

      &.is-active {
        z-index: 4;

        .grassland-ndvi-timeline-label {
          width: 72px;
          font-size: 12px;
        }
      }
    }
  }
}

.grassland-ndvi-timeline-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 18px;
  margin-bottom: 8px;

  span {
    position: relative;
    padding-left: 10px;
    color: rgba(232, 252, 255, 0.86);
    font-size: 13px;
    font-weight: 700;

    &::before {
      content: "";
      position: absolute;
      left: 0;
      top: 4px;
      width: 4px;
      height: 10px;
      background: #26f6c6;
      box-shadow: 0 0 10px rgba(38, 246, 198, 0.72);
    }
  }

  strong {
    color: #29ffcf;
    font-family: D-DIN, Arial, sans-serif;
    font-size: 20px;
    line-height: 18px;
    letter-spacing: 0;
    text-shadow: 0 0 12px rgba(41, 255, 207, 0.42);
  }
}

.grassland-ndvi-timeline-state {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 30px;
  color: rgba(228, 251, 255, 0.68);
  font-size: 13px;
}

.grassland-ndvi-timeline-body {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) 28px;
  align-items: center;
  gap: 12px;
}

.grassland-ndvi-timeline-nav {
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid rgba(72, 226, 255, 0.34);
  background: rgba(7, 35, 48, 0.68);
  color: rgba(230, 253, 255, 0.88);
  font-size: 22px;
  line-height: 20px;
  cursor: pointer;
  outline: none;

  &:hover,
  &:focus-visible {
    color: #ffffff;
    border-color: rgba(48, 220, 255, 0.78);
    box-shadow: 0 0 12px rgba(48, 220, 255, 0.26);
  }
}

.grassland-ndvi-timeline-track {
  position: relative;
  height: 34px;
  margin: 0 8px;
}

.grassland-ndvi-timeline-line,
.grassland-ndvi-timeline-fill {
  position: absolute;
  left: var(--timeline-edge-offset);
  top: 8px;
  height: 3px;
  border-radius: 999px;
}

.grassland-ndvi-timeline-line {
  right: var(--timeline-edge-offset);
  background: rgba(90, 199, 228, 0.28);
  box-shadow: inset 0 0 8px rgba(0, 7, 18, 0.28);
}

.grassland-ndvi-timeline-fill {
  right: auto;
  width: var(--timeline-fill-width);
  background: linear-gradient(90deg, #35bff8, #29ffcf);
  box-shadow: 0 0 12px rgba(41, 255, 207, 0.36);
}

.grassland-ndvi-timeline-thumb {
  position: absolute;
  z-index: 3;
  left: var(--timeline-active-left);
  top: 8px;
  width: 0;
  height: 0;
  pointer-events: none;
  transform: translateX(-50%);

  i {
    content: "";
    position: absolute;
    left: -9px;
    top: -9px;
    width: 18px;
    height: 18px;
    box-sizing: border-box;
    border: 2px solid rgba(240, 255, 255, 0.92);
    border-radius: 50%;
    background: #29ffcf;
    box-shadow:
      0 0 0 5px rgba(41, 255, 207, 0.12),
      0 0 18px rgba(41, 255, 207, 0.66);
  }
}

.grassland-ndvi-timeline-mark {
  position: absolute;
  z-index: 2;
  top: 0;
  display: block;
  width: 84px;
  height: 34px;
  padding: 0;
  border: 0;
  background: transparent;
  color: rgba(232, 250, 255, 0.76);
  cursor: pointer;
  outline: none;
  transform: translateX(-50%);

  .grassland-ndvi-timeline-tick {
    position: absolute;
    left: 50%;
    top: 4px;
    width: 2px;
    height: 11px;
    border-radius: 999px;
    background: rgba(148, 234, 255, 0.74);
    box-shadow: 0 0 8px rgba(48, 220, 255, 0.24);
    transform: translateX(-50%);
  }

  .grassland-ndvi-timeline-label {
    position: absolute;
    left: 50%;
    top: 18px;
    width: 78px;
    box-sizing: border-box;
    overflow: visible;
    padding: 0 3px;
    color: rgba(232, 250, 255, 0.62);
    font-size: 12px;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
    text-overflow: clip;
    white-space: nowrap;
    transform: translateX(-50%);
  }

  &:hover,
  &:focus-visible,
  &.is-active {
    color: #ffffff;

    .grassland-ndvi-timeline-tick {
      background: #29ffcf;
      box-shadow: 0 0 12px rgba(41, 255, 207, 0.58);
    }

    .grassland-ndvi-timeline-label {
      color: #ffffff;
    }
  }

  &.is-active {
    .grassland-ndvi-timeline-label {
      width: 78px;
      padding: 0 8px;
      border: 1px solid rgba(41, 255, 207, 0.34);
      background: rgba(4, 39, 48, 0.78);
      box-shadow: 0 0 14px rgba(41, 255, 207, 0.16);
    }
  }
}
</style>
