<template>
  <svg class="connection-svg" ref="svgRef">
    <defs>
      <!-- 跨层箭头（灰） -->
      <marker id="arrow-gray" markerWidth="10" markerHeight="7" refX="8" refY="3.5" orient="auto">
        <polygon points="0,0 10,3.5 0,7" fill="#909399" />
      </marker>
      <!-- 正向连线箭头（蓝） -->
      <marker id="arrow-blue" markerWidth="10" markerHeight="7" refX="8" refY="3.5" orient="auto">
        <polygon points="0,0 10,3.5 0,7" fill="#409eff" />
      </marker>
      <!-- 负向连线箭头（红） -->
      <marker id="arrow-red" markerWidth="10" markerHeight="7" refX="8" refY="3.5" orient="auto">
        <polygon points="0,0 10,3.5 0,7" fill="#f56c6c" />
      </marker>
      <!-- 因果链高亮箭头（橙） -->
      <marker id="arrow-orange" markerWidth="10" markerHeight="7" refX="8" refY="3.5" orient="auto">
        <polygon points="0,0 10,3.5 0,7" fill="#e6a23c" />
      </marker>
      <!-- 绘制中临时线箭头（橙） -->
      <marker id="arrow-warn" markerWidth="10" markerHeight="7" refX="8" refY="3.5" orient="auto">
        <polygon points="0,0 10,3.5 0,7" fill="#e6a23c" />
      </marker>
    </defs>

    <!-- 跨层固定箭头：贝塞尔曲线（每层底部 → 下层顶部 / 因果视角反向） -->
    <path
      v-for="(line, idx) in crossLayerLines"
      :key="'cross-' + idx"
      :d="line.path"
      fill="none"
      stroke="#909399"
      stroke-width="2"
      stroke-dasharray="6,3"
      marker-end="url(#arrow-gray)"
      :class="['cross-layer-line', { 'cross-dim': chainActive }]"
    />

    <!-- 用户自定义连线（贝塞尔曲线 + 语义样式 + 因果链高亮） -->
    <g v-for="(conn, idx) in layerConnections" :key="'conn-' + idx">
      <path
        :d="conn.path"
        :class="[
          'conn-line',
          { 'conn-selected': selectedIdx === idx },
          { 'conn-chain': chainActive && inChain(idx) },
          { 'conn-dim': chainActive && !inChain(idx) },
        ]"
        :stroke="strokeFor(conn, idx)"
        :stroke-width="widthFor(conn, idx)"
        :stroke-dasharray="dashFor(conn, idx)"
        fill="none"
        :marker-end="markerFor(conn, idx)"
        @click.stop="$emit('select-connection', idx)"
        @mouseenter="onConnEnter(idx)"
        @mouseleave="onConnLeave"
      />
      <!-- 连线语义标签（正向/负向） -->
      <g
        v-if="conn.label"
        :class="['conn-label', { 'conn-label-dim': chainActive && !inChain(idx) }]"
        :style="{ '--label-color': labelColor(conn) }"
        @click.stop="$emit('select-connection', idx)"
      >
        <rect
          :x="conn.mx - labelW(conn) / 2"
          :y="conn.my - 22"
          :width="labelW(conn)"
          height="16"
          rx="8"
          class="conn-label-bg"
        />
        <text
          :x="conn.mx"
          :y="conn.my - 10.5"
          text-anchor="middle"
          font-size="10.5"
          class="conn-label-text"
        >{{ conn.label }}</text>
      </g>

      <!-- hover提示 -->
      <g v-if="hoverIdx === idx && selectedIdx !== idx">
        <rect :x="conn.mx - 50" :y="conn.my - 10" width="100" height="20" rx="4" fill="rgba(0,0,0,0.65)" class="conn-hover-bg" />
        <text :x="conn.mx" :y="conn.my + 4" text-anchor="middle" fill="#fff" font-size="11" class="conn-hover-text">点击选中连线</text>
      </g>

      <!-- 选中操作区：正/负向 + 标签 + 删除 -->
      <g v-if="selectedIdx === idx" class="conn-actions">
        <rect
          :x="conn.mx - 96" :y="conn.my - 44" width="192" height="26" rx="13"
          fill="rgba(255,255,255,0.95)" stroke="#dcdfe6" class="conn-actions-bg"
        />
        <rect
          :x="conn.mx - 90" :y="conn.my - 39" width="54" height="16" rx="8"
          :fill="conn.effect === 'negative' ? '#f56c6c' : '#409eff'" class="act-btn"
          @click.stop="toggleEffect(idx)"
        />
        <text
          :x="conn.mx - 63" :y="conn.my - 27.5" text-anchor="middle" fill="#fff" font-size="11" font-weight="600"
          class="act-btn-text" @click.stop="toggleEffect(idx)"
        >{{ conn.effect === 'negative' ? '负向' : '正向' }}</text>
        <rect
          :x="conn.mx - 30" :y="conn.my - 39" width="54" height="16" rx="8" fill="#ecf5ff" class="act-btn"
          @click.stop="editLabel(idx)"
        />
        <text
          :x="conn.mx - 3" :y="conn.my - 27.5" text-anchor="middle" fill="#409eff" font-size="11" font-weight="600"
          class="act-btn-text" @click.stop="editLabel(idx)"
        >标签 ✎</text>
        <rect
          :x="conn.mx + 30" :y="conn.my - 39" width="54" height="16" rx="8" fill="#f56c6c" class="act-btn"
          @click.stop="$emit('delete-connection', idx)"
        />
        <text
          :x="conn.mx + 57" :y="conn.my - 27.5" text-anchor="middle" fill="#fff" font-size="11" font-weight="600"
          class="act-btn-text" @click.stop="$emit('delete-connection', idx)"
        >删除</text>
      </g>
    </g>

    <!-- 绘制中的临时线 -->
    <path
      v-if="tempLine"
      :d="tempLinePath"
      fill="none"
      stroke="#e6a23c"
      stroke-width="2"
      stroke-dasharray="6,3"
      marker-end="url(#arrow-warn)"
    />
  </svg>
</template>

<script setup>
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
import { ElMessageBox } from 'element-plus'
import { getChainForConnection, getChainForNode } from '../../utils/chainGraph'

const props = defineProps({
  /** 各层DOM元素引用，格式：{ financial: HTMLElement, customer: HTMLElement, ... } */
  layerRefs: { type: Object, required: true },
  /** 节点DOM元素引用，格式：{ 'financial-0': HTMLElement, ... } */
  nodeRefs: { type: Object, default: () => ({}) },
  /** 层顺序列表 */
  layerKeys: { type: Array, default: () => ['financial', 'customer', 'process', 'learning'] },
  /** 同层连线数据 [{from: 'financial-0', to: 'financial-1', effect, label}] */
  connections: { type: Array, default: () => [] },
  /** 临时连线 */
  tempLine: { type: Object, default: null },
  /** 容器尺寸变化触发重算 */
  containerKey: { type: Number, default: 0 },
  /** 视角方向：cascade(级联,上层→下层) / causal(因果,下层→上层) */
  viewDirection: { type: String, default: 'cascade' },
  /** 田字格 2×2 模式：跨层箭头只画同列上下层对（财务↔流程、客户↔学习） */
  gridMode: { type: Boolean, default: false },
  /** 因果链查看模式是否开启 */
  chainMode: { type: Boolean, default: false },
  /** 因果链查看模式下聚焦的节点 key（点击节点触发） */
  chainFocusKey: { type: String, default: null },
})

const emit = defineEmits([
  'select-connection',
  'delete-connection',
  'chain-hover',
  'chain-focus',
  'update-connection',
])

const svgRef = ref(null)
const hoverIdx = ref(null)
const selectedIdx = ref(null)
const layerConnections = ref([])
const crossLayerLines = ref([])

// ── 因果链高亮状态 ──
const hoverChain = ref(null) // { nodes: [], connIdxs: [] } | null
const focusChain = ref(null)

const chainActive = computed(() => hoverChain.value !== null || focusChain.value !== null)

function inChain(idx) {
  if (hoverChain.value?.connIdxs.includes(idx)) return true
  if (focusChain.value?.connIdxs.includes(idx)) return true
  return false
}

function onConnEnter(idx) {
  hoverIdx.value = idx
  const chain = getChainForConnection(props.connections, idx)
  hoverChain.value = chain
  emit('chain-hover', chain ? chain.nodes : null)
}

function onConnLeave() {
  hoverIdx.value = null
  hoverChain.value = null
  emit('chain-hover', null)
}

/** chainFocusKey 变化 → 重算聚焦因果链并通知父级（用于节点变淡） */
function recomputeFocusChain() {
  let chain = null
  if (props.chainMode && props.chainFocusKey) {
    chain = getChainForNode(props.connections, props.chainFocusKey)
  }
  focusChain.value = chain
  emit('chain-focus', chain ? chain.nodes : null)
}

watch(() => props.chainFocusKey, recomputeFocusChain)
watch(() => props.chainMode, (v) => {
  if (!v) {
    focusChain.value = null
    emit('chain-focus', null)
  } else {
    recomputeFocusChain()
  }
})
// 连线增删时保持聚焦链最新
watch(() => props.connections, recomputeFocusChain, { deep: true })

// ── 连线语义样式 ──
function isNegative(conn) {
  return conn && conn.effect === 'negative'
}

function strokeFor(conn, idx) {
  if (selectedIdx.value === idx) return '#f56c6c'
  if (chainActive.value && inChain(idx)) return '#e6a23c'
  if (chainActive.value) return '#c0c4cc'
  return isNegative(conn) ? '#f56c6c' : '#409eff'
}

function widthFor(conn, idx) {
  if (selectedIdx.value === idx) return 3
  if (chainActive.value && inChain(idx)) return 3.5
  if (chainActive.value) return 1.5
  return isNegative(conn) ? 2 : 2
}

function dashFor(conn, idx) {
  if (chainActive.value && inChain(idx)) return 'none'
  if (chainActive.value) return '4,3'
  return isNegative(conn) ? '7,4' : 'none'
}

function markerFor(conn, idx) {
  if (selectedIdx.value === idx) return 'url(#arrow-red)'
  if (chainActive.value && inChain(idx)) return 'url(#arrow-orange)'
  if (chainActive.value) return 'url(#arrow-gray)'
  return isNegative(conn) ? 'url(#arrow-red)' : 'url(#arrow-blue)'
}

function labelColor(conn) {
  return isNegative(conn) ? '#f56c6c' : '#409eff'
}

function labelW(conn) {
  return (conn.label ? conn.label.length : 0) * 12 + 16
}

// ── 连线语义编辑 ──
function toggleEffect(idx) {
  const conn = props.connections[idx]
  if (!conn) return
  emit('update-connection', idx, {
    effect: conn.effect === 'negative' ? 'positive' : 'negative',
  })
}

function editLabel(idx) {
  const conn = props.connections[idx]
  if (!conn) return
  ElMessageBox.prompt(
    '输入连线语义标签（如 "↑降低成本" / "↓提升利润"），留空则清除',
    '连线语义标签',
    {
      inputValue: conn.label || '',
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      inputPlaceholder: '如：↑降低成本',
    }
  ).then(({ value }) => {
    emit('update-connection', idx, { label: (value || '').trim() })
  }).catch(() => { /* 取消 */ })
}

// ── 跨层箭头：贝塞尔曲线（田字格模式只画同列上下层对） ──
function calcCrossLayerLines() {
  const lines = []
  const keys = props.layerKeys
  const isCausal = props.viewDirection === 'causal'
  const svg = svgRef.value
  if (!svg) return
  const svgRect = svg.getBoundingClientRect()
  // 田字格 2×2：财务(0)↔流程(2)、客户(1)↔学习(3) 同列上下；纵向泳道：相邻层全画
  const pairs = props.gridMode
    ? [[0, 2], [1, 3]]
    : keys.map((_, i) => [i, i + 1]).slice(0, -1)
  for (const [ai, bi] of pairs) {
    const aEl = props.layerRefs[keys[ai]]
    const bEl = props.layerRefs[keys[bi]]
    if (!aEl || !bEl) continue

    const ar = aEl.getBoundingClientRect()
    const br = bEl.getBoundingClientRect()
    const upper = ar.top <= br.top ? ar : br
    const lower = ar.top <= br.top ? br : ar
    const cx = (upper.left + upper.width / 2 - svgRect.left + lower.left + lower.width / 2 - svgRect.left) / 2
    const y1 = (isCausal ? lower.top : upper.bottom) - svgRect.top
    const y2 = (isCausal ? upper.bottom : lower.top) - svgRect.top
    const bend = 28 // 曲线横向弯曲幅度
    // 三次贝塞尔：垂直方向柔和 S 曲线
    const path = `M ${cx} ${y1} C ${cx + bend} ${y1}, ${cx + bend} ${y2}, ${cx} ${y2}`
    lines.push({ path })
  }
  crossLayerLines.value = lines
}

// ── 同层用户连线：贝塞尔曲线（节点坐标缓存：同一节点只读一次 rect） ──
function calcLayerConnections() {
  const result = []
  const svg = svgRef.value
  if (!svg) return
  const svgRect = svg.getBoundingClientRect()

  // 节点坐标缓存（相对 svg）：同一节点被多条连线引用时只 getBoundingClientRect 一次
  const rectCache = {}
  const getRect = (key) => {
    if (rectCache[key]) return rectCache[key]
    const el = props.nodeRefs[key]
    if (!el) return null
    const r = el.getBoundingClientRect()
    rectCache[key] = {
      left: r.left - svgRect.left,
      top: r.top - svgRect.top,
      right: r.right - svgRect.left,
      bottom: r.bottom - svgRect.top,
      width: r.width,
      height: r.height,
    }
    return rectCache[key]
  }

  for (const conn of props.connections) {
    const fr = getRect(conn.from)
    const tr = getRect(conn.to)
    if (!fr || !tr) continue

    // 源节点右侧中点 → 目标节点左侧中点
    const x1 = fr.right
    const y1 = fr.top + fr.height / 2
    const x2 = tr.left
    const y2 = tr.top + tr.height / 2

    // 贝塞尔曲线控制点（水平拖拽，曲线柔和）
    const dx = Math.max(Math.abs(x2 - x1) * 0.5, 40)
    const cp1x = x1 + dx
    const cp1y = y1
    const cp2x = x2 - dx
    const cp2y = y2

    const path = `M ${x1} ${y1} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${x2} ${y2}`

    result.push({
      path,
      mx: (x1 + x2) / 2,
      my: (y1 + y2) / 2,
      from: conn.from,
      to: conn.to,
      effect: conn.effect || 'positive',
      label: conn.label || '',
    })
  }
  layerConnections.value = result
}

// ── 绘制中临时线（贝塞尔） ──
const tempLinePath = computed(() => {
  if (!props.tempLine) return ''
  const { x1, y1, x2, y2 } = props.tempLine
  const dx = Math.max(Math.abs(x2 - x1) * 0.5, 40)
  return `M ${x1} ${y1} C ${x1 + dx} ${y1}, ${x2 - dx} ${y2}, ${x2} ${y2}`
})

function recalcAll() {
  calcCrossLayerLines()
  calcLayerConnections()
}

// ── 重绘节流：rAF 合并连续触发（拖拽/缩放/滚动时每帧只重算一次） ──
let rafPending = false
function scheduleRecalc() {
  if (rafPending) return
  rafPending = true
  requestAnimationFrame(() => {
    rafPending = false
    recalcAll()
  })
}

watch(() => props.containerKey, () => scheduleRecalc())
watch(() => props.viewDirection, () => scheduleRecalc())
watch(() => props.gridMode, () => scheduleRecalc())
watch(() => props.connections, () => scheduleRecalc(), { deep: true })

// 暴露给父组件调用
defineExpose({ recalcAll })

onMounted(() => {
  nextTick(recalcAll)
})
</script>

<style scoped>
.connection-svg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 5;
  pointer-events: none;
  overflow: visible;
}
.cross-layer-line {
  pointer-events: none;
  transition: opacity 0.2s;
}
.cross-dim {
  opacity: 0.18;
}
.conn-line {
  cursor: pointer;
  transition: stroke 0.15s, stroke-width 0.15s, opacity 0.2s;
  pointer-events: stroke;
}
.conn-line.conn-chain {
  filter: drop-shadow(0 0 3px rgba(230, 162, 60, 0.65));
}
.conn-line.conn-dim {
  opacity: 0.25;
}
.conn-selected {
  filter: drop-shadow(0 0 3px rgba(245, 108, 108, 0.6));
}
.conn-label {
  cursor: pointer;
  pointer-events: all;
  transition: opacity 0.2s;
}
.conn-label-dim {
  opacity: 0.25;
}
.conn-label-bg {
  fill: rgba(255, 255, 255, 0.92);
  stroke: var(--label-color, #409eff);
  stroke-width: 1;
}
.conn-label-text {
  fill: #303133;
  font-weight: 600;
  pointer-events: none;
}
.conn-hover-bg,
.conn-hover-text {
  pointer-events: none;
}
.conn-actions-bg {
  pointer-events: all;
}
.act-btn {
  cursor: pointer;
  pointer-events: all;
  transition: opacity 0.15s;
}
.act-btn:hover {
  opacity: 0.8;
}
.act-btn-text {
  cursor: pointer;
  pointer-events: all;
  user-select: none;
}
</style>
