<template>
  <div class="pm-root" :style="{ width: width + 'px', height: height + 'px' }">
    <!-- 背景遮罩 -->
    <div class="bg-layer"></div>

    <!-- 顶部 header -->
    <div class="header">
      <div class="title-left">
        <!-- <span class="triangle">▶</span> -->
        <span class="title">项目里程碑</span>
      </div>
      <div class="years-right">
        <div v-for="y in headerYears" :key="y" class="year">{{ y }}</div>
      </div>
    </div>

    <!-- project row (left project name / right months labels) -->
    <div class="project-row">
      <div class="project-left">
        <div class="project-name">{{ projectName }}</div>
      </div>
      <div class="months-right">
        <div class="months-grid">
          <div
            v-for="(m) in monthLabels"
            :key="m"
            class="month-cell"
            :style="{ width: monthWidth + 'px' }"
          >
            {{ m }}
          </div>
        </div>
      </div>
    </div>

    <!-- SVG timeline -->
    <div class="svg-wrap" ref="svgWrapRef">
      <svg :width="svgWidth" :height="svgHeight" xmlns="http://www.w3.org/2000/svg">
        <defs>
          <!-- arrow marker -->
          <marker
            id="arrowHead"
            markerWidth="10"
            markerHeight="10"
            refX="5"
            refY="5"
            orient="auto"
            markerUnits="strokeWidth"
          >
            <path d="M0,0 L10,5 L0,10 z" fill="#7fd7ff" />
          </marker>

          <!-- star symbol -->
          <symbol id="starSym" viewBox="0 0 24 24">
            <path d="M12 2.6l2.4 5 5.5.8-4 3.9.9 5.5L12 16.6 6.2 18.8l.9-5.5L3 9.3l5.5-.8z" />
          </symbol>

          <!-- subtle vertical grid line -->
          <linearGradient id="barGrad" x1="0" x2="1">
            <stop offset="0" stop-color="#59baff" />
            <stop offset="1" stop-color="#86e9ff" />
          </linearGradient>
        </defs>

        <!-- vertical month lines -->
        <g v-for="(_m, i) in monthsArr" :key="'v' + i">
          <line
            :x1="leftCol + i * monthWidth + monthWidth / 2"
            y1="0"
            :x2="leftCol + i * monthWidth + monthWidth / 2"
            :y2="svgHeight"
            stroke="rgba(255,255,255,0.03)"
            stroke-width="1"
          />
        </g>

        <!-- rows: bars, nodes, labels -->
        <g v-for="(row, rIdx) in rows" :key="'row' + rIdx">
          <!-- horizontal baseline -->
          <line
            :x1="leftCol"
            :y1="rowY(rIdx)"
            :x2="svgWidth - rightPadding"
            :y2="rowY(rIdx)"
            stroke="rgba(255,255,255,0.04)"
            stroke-width="1"
          />

          <!-- duration rect -->
          <rect
            v-if="row.start !== null && row.end !== null"
            :x="xOfMonth(row.start) - monthWidth / 2 + 2"
            :y="rowY(rIdx) - barH / 2"
            :width="(row.end - row.start + 1) * monthWidth - 4"
            :height="barH"
            rx="8"
            :fill="'rgba(63,150,200,0.18)'"
            stroke="rgba(127,210,255,0.9)"
          />

          <!-- arrow polygon at end -->
          <polygon
            v-if="row.end !== null"
            :points="arrowAt(row.end, rowY(rIdx), monthWidth)"
            fill="#7fd7ff"
          />

          <!-- nodes -->
          <g
            v-for="(n, ni) in row.nodes"
            :key="'n' + rIdx + '-' + ni"
            :transform="`translate(${xOfMonth(n.month)}, ${rowY(rIdx)})`"
          >
            <template v-if="n.type === 'circle'">
              <circle r="7" :fill="'#8feaff'" stroke="#ffffff" stroke-width="1.2" />
            </template>
            <template v-else-if="n.type === 'dot'">
              <circle r="5" :fill="'#ff6b6b'" stroke="#ffffff" stroke-width="1" />
            </template>
            <template v-else-if="n.type === 'star'">
              <use
                href="#starSym"
                x="-10"
                y="-10"
                width="20"
                height="20"
                :fill="'#ffd36b'"
              />
            </template>

            <!-- node label below -->
            <text x="0" :y="labelY" text-anchor="middle" class="node-label">{{ n.label }}</text>
          </g>
        </g>

        <!-- left column labels (stage names) -->
        <g>
          <rect :x="0" :y="0" :width="leftCol" :height="svgHeight" fill="rgba(6,18,30,0.45)" />
          <g v-for="(row, rIdx) in rows" :key="'label' + rIdx">
            <text :x="leftPad" :y="rowY(rIdx) + 6" class="left-label">{{ row.label }}</text>
          </g>
        </g>
      </svg>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue';

/**
 * Props
 */
const props = defineProps({
  width: { type: Number, default: 880 },
  height: { type: Number, default: 500 },
  projectName: { type: String, default: '科技生态园' },
  monthsStart: { type: String, default: '2023-09' },
  monthsEnd: { type: String, default: '2025-05' },
});

/** Layout constants */
const leftCol = 160; // 左侧列宽
const rightPadding = 24;
const topPadding = 8;
const bottomPadding = 10;
const rowH = 72;
const barH = 14;
const leftPad = 20;
const labelY = 22;

const svgWidth = computed(() => props.width);
const svgHeight = computed(() => {
  // compute visual SVG height area for timeline (excluding header/subheader)
  const rowsCount = rows.length;
  return topPadding + rowsCount * rowH + bottomPadding;
});

/** build months array inclusive */
function buildMonths(start: string, end: string) {
  const [sy, sm] = start.split('-').map(Number);
  const [ey, em] = end.split('-').map(Number);
  const arr: string[] = [];
  let y = sy,
    m = sm;
  while (y < ey || (y === ey && m <= em)) {
    arr.push(`${y}-${String(m).padStart(2, '0')}`);
    m++;
    if (m > 12) {
      m = 1;
      y++;
    }
  }
  return arr;
}

const monthsArr = computed(() => buildMonths(props.monthsStart, props.monthsEnd));
const monthWidth = computed(() => {
  const usable = props.width - leftCol - rightPadding;
  return Math.max(36, Math.floor(usable / monthsArr.value.length)); // 最少宽度以防变形
});
const monthLabels = computed(() =>
  monthsArr.value.map((s) => {
    const [_y, m] = s.split('-');
    return `${Number(m)}`;
  }),
);

// header years
const headerYears = computed(() => {
  const set = new Set<number>();
  monthsArr.value.forEach((m) => set.add(Number(m.split('-')[0])));
  return Array.from(set);
});

/** rows data (造假数据，但按你图的时间与节点摆放来高度还原) */
const rows = [
  {
    label: '项目设计',
    start: 0, // month index in monthsArr
    end: 8,
    nodes: [
      { month: 0, type: 'circle', label: '招标\n2023/09' },
      { month: 2, type: 'dot', label: '招标\n2023/11' },
    ],
  },
  {
    label: '项目报建',
    start: 1,
    end: 6,
    nodes: [{ month: 1, type: 'circle', label: '招标\n2023/10' }],
  },
  {
    label: '项目采购',
    start: 0,
    end: 12,
    nodes: [
      { month: 1, type: 'circle', label: '招标\n2023/10' },
      { month: 4, type: 'dot', label: '节点\n2024/01' },
    ],
  },
  {
    label: '土建工程',
    start: 2,
    end: 15,
    nodes: [
      { month: 6, type: 'dot', label: '招标\n2024/03' },
      { month: 11, type: 'star', label: '★\n2024/11' },
    ],
  },
  {
    label: '洁净工程',
    start: 5,
    end: 20,
    nodes: [{ month: 12, type: 'star', label: '★\n2025/01' }],
  },
];

/** helpers to compute coordinates */
function xOfMonth(monthIndex: number) {
  // center of month cell
  return leftCol + monthIndex * monthWidth.value + monthWidth.value / 2;
}
function rowY(rIdx: number) {
  return topPadding + rIdx * rowH + rowH / 2;
}
function arrowAt(endMonth: number, centerY: number, mW: number) {
  // small right-pointing triangle right after end of bar
  const xEnd = leftCol + (endMonth + 1) * mW - 6;
  const cx = xEnd + 10;
  const y1 = centerY - 8;
  const y2 = centerY;
  const y3 = centerY + 8;
  return `${xEnd},${y1} ${cx},${y2} ${xEnd},${y3}`;
}

const svgWrapRef = ref<HTMLElement | null>(null);
</script>

<style scoped lang="less">
.pm-root {
  position: relative;
  font-family: 'Microsoft Yahei', Arial, sans-serif;
  color: #cfeffd;
  border-radius: 6px;
  overflow: hidden;
  background: rgba(29, 66, 110, 0.6);
}

/* faint decorative background */
.bg-layer {
  position: absolute;
  inset: 0;
  background-image: radial-gradient(rgba(80, 170, 220, 0.03) 1px, transparent 1px);
  background-size: 80px 80px;
  z-index: 0;
  pointer-events: none;
}

/* header */
.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 36px;
  z-index: 2;
}
.title-left {
  width: 145px;
}
.triangle {
  color: #39e6ff;
  font-weight: 700;
}
.title {
  font-size: 18px;
  font-weight: 700;
  color: #bff8ff;
}
.years-right {
  display: flex;
  gap: 8px;
  align-items: center;
  justify-content: space-between;
  width: calc(100% - 145px);
}
.year {
  background: linear-gradient(180deg, rgba(18, 80, 120, 0.18), rgba(6, 30, 50, 0.12));
  padding: 4px 8px;
  border-radius: 4px;
  color: #aef0ff;
  font-weight: 600;
  border: 1px solid rgba(160, 230, 255, 0.06);
}

/* project row */
.project-row {
  display: flex;
  z-index: 2;
  align-items: center;
}
.project-left {
  width: 160px;
}
.project-name {
  color: #8feaff;
  font-weight: 700;
  font-size: 15px;
  padding-top: 2px;
}
.months-right {
  flex: 1;
  overflow: hidden;
}
.months-grid {
  display: flex;
  gap: 0;
  align-items: center;
}
.month-cell {
  text-align: center;
  font-size: 12px;
  color: rgba(200, 230, 255, 0.8);
  padding: 4px 0;
  border-right: 1px solid rgba(255, 255, 255, 0.02);
}

/* svg area */
.svg-wrap {
  z-index: 1;
  box-sizing: border-box;
}
.left-label {
  fill: #8feaff;
  font-weight: 700;
  font-size: 13px;
}
.node-label {
  fill: rgba(200, 230, 255, 0.94);
  font-size: 11px;
  white-space: pre;
}

/* ensure svg text has smooth antialiasing */
svg {
  display: block;
}
</style>
