<template>
  <div
    :style="{
      ...styleMap?.mainStyle
    }"
    class="relative w-full h-full box-border overflow-visible"
  >
    <ImageBg
      v-if="styleMap?.mainStyle?.imageSrc"
      :src="styleMap?.mainStyle?.imageSrc"
      :imageStyle="styleMap?.mainStyle?.imageStyle"
    />

    <div class="absolute inset-0 w-full h-full overflow-visible">
      <svg
        :viewBox="`0 0 ${width} ${height}`"
        preserveAspectRatio="xMidYMid meet"
        class="w-full h-full block overflow-visible"
        xmlns="http://www.w3.org/2000/svg"
      >
        <defs></defs>
        <!-- 刻度线：所有刻度，按 area 使用对应直线颜色 -->
        <line
          v-for="(pos, idx) in tickPositions"
          :key="'tick-' + idx"
          :x1="pos.x1"
          :y1="pos.y1"
          :x2="pos.x2"
          :y2="pos.y2"
          :stroke="pos.stroke"
          :stroke-width="itemsLineWidth"
          stroke-linecap="butt"
        />
        <!-- 主轴直线：按 area 分段绘制，每段使用对应范围的直线颜色 -->
        <line
          v-for="(seg, segIdx) in axisSegments"
          :key="'axis-' + segIdx"
          :x1="segIdx === 0 ? seg.x1 - itemsLineWidth / 2 : seg.x1"
          :y1="seg.y1"
          :x2="segIdx === axisSegments.length - 1 ? seg.x2 + itemsLineWidth / 2 : seg.x2"
          :y2="seg.y2"
          :stroke="seg.stroke"
          :stroke-width="mainLineWidth"
          stroke-linecap="butt"
        />
      </svg>
      <!-- 标签用 HTML 渲染在 SVG 外，避免被 viewBox 裁剪，全部展示 -->
      <div
        v-if="enableLabels && labelList.length"
        class="absolute inset-0 pointer-events-none overflow-visible"
      >
        <span
          v-for="(lb, idx) in labelList"
          :key="'label-' + idx"
          class="absolute whitespace-nowrap"
          :style="labelSpanStyle(lb)"
        >
          {{ lb.text }}
        </span>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import { useWidgetStyle } from '../hooks/useWidgetStyle'

import ImageBg from '../ImageBg.vue'
import type { AreaSection } from './Config.vue'

const props = defineProps<{
  width: number
  height: number
  styles: any
  state?: string
  part?: string
  mode: 'horizontal_top' | 'horizontal_bottom' | 'vertical_left' | 'vertical_right'
  tick: number
  mainTick: number
  rangeStart: number
  rangeEnd: number
  enableLabels: boolean
  labels: string
  area: AreaSection[]
}>()

const styleMap = useWidgetStyle({
  widget: 'lv_scale',
  props
})

// 归一化后的 area 列表（start/end 为数值范围，颜色为 #hex 字符串）
const normalizedAreas = computed(() => {
  const list = props.area || []
  return list.map(
    (s: AreaSection & { textColor?: string | number; lineColor?: string | number }) => ({
      start: Number(s.start),
      end: Number(s.end),
      textColor: s.textColor,
      lineColor: s.lineColor
    })
  )
})

// 根据归一化位置 t∈[0,1] 得到数值
const valueAt = (t: number) => props.rangeStart + t * (props.rangeEnd - props.rangeStart)

// 根据数值取所在 area 的直线颜色，否则用默认
const getLineColorForValue = (value: number): string => {
  for (const s of normalizedAreas.value) {
    if (value >= s.start && value <= s.end) return s.lineColor
  }
  return itemsLineColor.value
}

// 根据数值取所在 area 的文本颜色，否则用默认
const getTextColorForValue = (value: number): string => {
  for (const s of normalizedAreas.value) {
    if (value >= s.start && value <= s.end) return s.textColor
  }
  return mainTextColor.value
}

// 从 styles 中读取 items 的 other.length（刻度线长度）
const itemsStyleConfig = computed(() =>
  props.styles?.find(
    (s: any) =>
      s.part?.name === 'items' && (s.part?.state === props.state || s.part?.state === 'default')
  )
)
// 从 styles 中读取 indicator 的 other.length（主刻度线长度）
const indicatorStyleConfig = computed(() =>
  props.styles?.find(
    (s: any) =>
      s.part?.name === 'indicator' && (s.part?.state === props.state || s.part?.state === 'default')
  )
)
const tickLength = computed(() => itemsStyleConfig.value?.other?.length ?? 5)
const mainTickLength = computed(
  () =>
    indicatorStyleConfig.value?.other?.length ??
    Math.max(tickLength.value * 1.5, tickLength.value + 4)
)

const mainLineColor = computed(() => styleMap.value?.mainStyle?.line?.color ?? '#212121')
const mainLineWidth = computed(() => styleMap.value?.mainStyle?.line?.width ?? 2)
const itemsLineColor = computed(() => styleMap.value?.itemsStyle?.line?.color ?? '#212121')
const itemsLineWidth = computed(() => styleMap.value?.itemsStyle?.line?.width ?? 2)

const mainTextColor = computed(() => styleMap.value?.mainStyle?.color ?? '#000000')
const mainTextSize = computed(() => styleMap.value?.mainStyle?.fontSize ?? '16px')
const mainFontFamily = computed(() => styleMap.value?.mainStyle?.fontFamily ?? 'sans-serif')

const isHorizontal = computed(
  () => props.mode === 'horizontal_top' || props.mode === 'horizontal_bottom'
)
const isTopOrLeft = computed(
  () => props.mode === 'horizontal_top' || props.mode === 'vertical_left'
)

// 刻度数量、主刻度间隔（空格数）
const tickCount = computed(() => Math.max(2, props.tick))
// 主刻度间隔：表示两个主刻度之间包含的“空格”数量（即小刻度间的间隔数）
const majorTickGap = computed(() => Math.max(1, Math.min(props.mainTick || 1, tickCount.value - 1)))

// 主刻度在刻度数组中的索引集合：始终从 0 开始，按 mainTick 作为“空格数”间隔
// 若最后剩余的间隔不足 mainTick，则末尾不再补主刻度（“下个大刻度不出来就不展示标签”）
const majorIndices = computed(() => {
  const set = new Set<number>()
  const n = tickCount.value
  const gap = majorTickGap.value
  if (n <= 1) {
    set.add(0)
    return set
  }

  const lastIndex = n - 1
  const maxStep = Math.floor(lastIndex / gap)
  for (let step = 0; step <= maxStep; step++) {
    const idx = step * gap
    if (idx <= lastIndex) set.add(idx)
  }
  return set
})

// 归一化位置 [0..1] 的刻度列表
const tickNormPositions = computed(() => {
  const n = tickCount.value
  if (n <= 1) return [0.5]
  const list: number[] = []
  for (let i = 0; i < n; i++) list.push(i / (n - 1))
  return list
})

// 根据 mode 计算轴线坐标（主轴直线）
// horizontal_top: 主轴贴在最底部；horizontal_bottom: 主轴贴在最顶部
// vertical_left: 主轴贴在最右侧；vertical_right: 主轴贴在最左侧
const axisLine = computed(() => {
  const { width, height } = props
  const halfLine = mainLineWidth.value / 2
  if (isHorizontal.value) {
    const y = isTopOrLeft.value ? height - halfLine : halfLine
    return { x1: 0, y1: y, x2: width, y2: y }
  }
  // 垂直模式下：根据模式贴边放置主轴
  const x =
    props.mode === 'vertical_left'
      ? width - halfLine // 主轴靠最右
      : halfLine // 主轴靠最左（vertical_right）
  return { x1: x, y1: 0, x2: x, y2: height }
})

// 主轴按 area 分段：根据范围边界拆成多段，每段使用对应 area 的直线颜色
//  两端需要处理刻度线的宽度
const axisSegments = computed(() => {
  const { width, height } = props
  const start = props.rangeStart
  const end = props.rangeEnd
  const span = end - start
  const axis = axisLine.value

  const boundaries = new Set<number>([0, 1])
  normalizedAreas.value.forEach((s) => {
    if (span !== 0) {
      const t0 = Math.max(0, Math.min(1, (s.start - start) / span))
      const t1 = Math.max(0, Math.min(1, (s.end - start) / span))
      boundaries.add(t0)
      boundaries.add(t1)
    }
  })
  const sorted = Array.from(boundaries).sort((a, b) => a - b)

  const segments: { x1: number; y1: number; x2: number; y2: number; stroke: string }[] = []
  for (let i = 0; i < sorted.length - 1; i++) {
    const t0 = sorted[i]
    const t1 = sorted[i + 1]
    const midValue = valueAt((t0 + t1) / 2)
    const stroke = getLineColorForValue(midValue)
    if (isHorizontal.value) {
      segments.push({
        x1: t0 * width,
        y1: axis.y1,
        x2: t1 * width,
        y2: axis.y2,
        stroke
      })
    } else {
      segments.push({
        x1: axis.x1,
        y1: t0 * height,
        x2: axis.x2,
        y2: t1 * height,
        stroke
      })
    }
  }
  if (segments.length === 0) {
    segments.push({
      x1: axis.x1,
      y1: axis.y1,
      x2: axis.x2,
      y2: axis.y2,
      stroke: mainLineColor.value
    })
  }
  return segments
})

// 刻度线端点：与主轴共线；每根刻度按数值取 area 直线颜色
const tickPositions = computed(() => {
  const { width, height } = props
  const axis = axisLine.value
  const norm = tickNormPositions.value
  const major = majorIndices.value
  const list: { x1: number; y1: number; x2: number; y2: number; stroke: string }[] = []

  if (isHorizontal.value) {
    const yAxis = axis.y1
    // horizontal_top: 刻度朝上；horizontal_bottom: 刻度朝下
    const sign = isTopOrLeft.value ? -1 : 1
    norm.forEach((t, idx) => {
      const x = t * width
      const len = major.has(idx) ? mainTickLength.value : tickLength.value
      const value = valueAt(t)
      list.push({
        x1: x,
        y1: yAxis,
        x2: x,
        y2: yAxis + sign * len,
        stroke: getLineColorForValue(value)
      })
    })
  } else {
    // 垂直模式下，刻度与主轴共线：vertical_left 主轴在右侧，刻度向左伸展；vertical_right 相反
    const xAxis = axis.x1
    const sign = props.mode === 'vertical_left' ? -1 : 1
    norm.forEach((t, idx) => {
      const y = t * height
      const len = major.has(idx) ? mainTickLength.value : tickLength.value
      const value = valueAt(t)
      list.push({
        x1: xAxis,
        y1: y,
        x2: xAxis + sign * len,
        y2: y,
        stroke: getLineColorForValue(value)
      })
    })
  }
  return list
})

// 标签文案：有设置内容用设置内容，否则用主刻度对应的数值（根据范围变化）
const labelTexts = computed(() => {
  const raw = (props.labels || '').trim()
  if (raw) {
    return raw
      .split(',')
      .map((s) => s.trim())
      .filter(Boolean)
  }
  const indices = Array.from(majorIndices.value).sort((a, b) => a - b)
  if (!indices.length) return []
  const n = tickCount.value
  const list: string[] = []
  indices.forEach((idx) => {
    const t = n <= 1 ? 0.5 : idx / (n - 1)
    const v = valueAt(t)
    list.push(String(Math.floor(v)))
  })
  return list
})

// 标签的 HTML 定位样式：根据不同模式，按主刻度长度 + 5 偏移
function labelSpanStyle(lb: {
  x: number
  y: number
  textAnchor: string
  dominantBaseline: string
  fill: string
}) {
  const style: Record<string, string> = {
    color: lb.fill as string,
    fontSize: String(mainTextSize.value),
    fontFamily: String(mainFontFamily.value)
  }

  const offset = `${mainTickLength.value + 5}px`

  switch (props.mode) {
    // horizontal_top：不设置 top，bottom 为 主刻度长度 + 5
    case 'horizontal_top': {
      style.left = props.width > 0 ? `${(lb.x / props.width) * 100}%` : '0'
      style.bottom = offset
      style.transform = 'translateX(-50%)'
      break
    }
    // horizontal_bottom：不设置 bottom，top 为 主刻度长度 + 5
    case 'horizontal_bottom': {
      style.left = props.width > 0 ? `${(lb.x / props.width) * 100}%` : '0'
      style.top = offset
      style.transform = 'translateX(-50%)'
      break
    }
    // vertical_right：top 按刻度位置动态计算，left 为 主刻度长度 + 5
    case 'vertical_right': {
      style.top = props.height > 0 ? `${(lb.y / props.height) * 100}%` : '0'
      style.left = offset
      style.transform = 'translateY(-50%)'
      break
    }
    // vertical_left：不设置 left，right 为 主刻度长度 + 5
    case 'vertical_left': {
      style.top = props.height > 0 ? `${(lb.y / props.height) * 100}%` : '0'
      style.right = offset
      style.transform = 'translateY(-50%)'
      break
    }
  }

  return style
}

// 标签位置与对齐（仅主刻度位置），按 area 使用对应文本颜色
const labelList = computed(() => {
  if (!props.enableLabels || labelTexts.value.length === 0) return []
  const { width, height } = props
  const labelGap = 4
  const indices = Array.from(majorIndices.value).sort((a, b) => a - b)
  const m = indices.length
  const texts = labelTexts.value
  const list: {
    x: number
    y: number
    text: string
    textAnchor: string
    dominantBaseline: string
    fill: string
  }[] = []

  if (isHorizontal.value) {
    const yAxis = axisLine.value.y1
    // horizontal_top: 文字在刻度上（刻度朝上，标签在刻度线顶端上方）；horizontal_bottom: 文字在刻度下
    const labelY = isTopOrLeft.value
      ? yAxis - mainTickLength.value - labelGap
      : yAxis + mainTickLength.value + labelGap
    for (let i = 0; i < m; i++) {
      const idx = indices[i]
      const t = tickCount.value <= 1 ? 0.5 : idx / (tickCount.value - 1)
      const x = t * width
      const value = valueAt(t)
      list.push({
        x,
        y: labelY,
        text: texts[i] ?? '',
        textAnchor: 'middle',
        dominantBaseline: isTopOrLeft.value ? 'auto' : 'hanging',
        fill: getTextColorForValue(value)
      })
    }
  } else {
    // 垂直模式下，标签位置基于主轴位置再偏移主刻度长度和间距
    const xAxis = axisLine.value.x1
    const labelX = isTopOrLeft.value
      ? xAxis - mainTickLength.value - labelGap
      : xAxis + mainTickLength.value + labelGap
    for (let i = 0; i < m; i++) {
      const idx = indices[i]
      const t = tickCount.value <= 1 ? 0.5 : idx / (tickCount.value - 1)
      const y = t * height
      const value = valueAt(t)
      list.push({
        x: labelX,
        y,
        text: texts[i] ?? '',
        textAnchor: isTopOrLeft.value ? 'end' : 'start',
        dominantBaseline: 'middle',
        fill: getTextColorForValue(value)
      })
    }
  }
  return list
})
</script>
