<template>
  <div v-if="showOverlay" class="sub-node-overlay">
    <Transition name="drawer">
      <div
        v-show="visible"
        class="drawer-position"
        :style="drawerPositionStyle"
      >
        <div class="sub-node-drawer">
          <div class="drawer-body">
            <div
              v-for="(sub, index) in subNodes"
              :key="sub.id || index"
              class="sub-node-card"
              :class="{ active: selectedIndex === index }"
              @click="onSelectSubNode(index)"
            >
              <div class="card-index">{{ index + 1 }}</div>
              <div class="card-info">
                <div class="card-name">{{ sub.name || `子节点 ${index + 1}` }}</div>
                <div class="card-role">{{ getSubNodeAssigneeDisplay(sub) }}</div>
              </div>
              <div class="card-actions" @click.stop>
                <el-icon class="delete-icon" @click="onRemove(index)">
                  <Delete />
                </el-icon>
              </div>
            </div>
            <div class="sub-node-card add-card" @click="onAdd">
              <el-icon class="add-icon"><Plus /></el-icon>
              <span>添加子节点</span>
            </div>
          </div>
        </div>
      </div>
    </Transition>

    <div class="sub-node-badge" :style="badgeStyle" @click="toggleVisible">
      <el-icon><Document /></el-icon>
      <span>子节点 {{ subNodes.length }}</span>
      <el-icon class="badge-arrow" :class="{ open: visible }">
        <CaretBottom v-if="visible" />
        <CaretTop v-else />
      </el-icon>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
import { Plus, Delete, CaretTop, CaretBottom, Document } from '@element-plus/icons-vue'
import type { Role } from '@/types/system'

const props = defineProps<{
  lf: any
  node: any
  nodeProps: Record<string, any>
  roleList: Role[]
  selectedIndex: number | null
  visible: boolean
}>()

const emit = defineEmits<{
  (e: 'update:visible', visible: boolean): void
  (e: 'select', index: number): void
  (e: 'add'): void
  (e: 'remove', index: number): void
}>()

const showOverlay = computed(() => {
  return props.node && props.node.type === 'approval-node' && props.nodeProps.subNodesEnabled
})

const subNodes = computed(() => {
  return props.nodeProps.subNodes || []
})

const CARD_HEIGHT = 58
const CARD_GAP = 8
const BADGE_HEIGHT = 26
const VERTICAL_GAP = 6
const NODE_HALF_HEIGHT = 30

const position = ref({ x: 0, y: 0 })

const drawerHeight = computed(() => {
  const count = subNodes.value.length + 1 // +1 for add-card
  return count * CARD_HEIGHT + Math.max(0, count - 1) * CARD_GAP + 16 // padding
})

const badgeTop = computed(() => {
  return position.value.y - NODE_HALF_HEIGHT - BADGE_HEIGHT - VERTICAL_GAP
})

const drawerTop = computed(() => {
  return badgeTop.value - VERTICAL_GAP - drawerHeight.value
})

const drawerPositionStyle = computed(() => ({
  position: 'absolute' as const,
  left: `${position.value.x}px`,
  top: `${drawerTop.value}px`,
  transform: 'translate(-50%, 0)',
}))

const badgeStyle = computed(() => ({
  position: 'absolute' as const,
  left: `${position.value.x}px`,
  top: `${badgeTop.value}px`,
  transform: 'translate(-50%, 0)',
}))

function toggleVisible() {
  emit('update:visible', !props.visible)
}

function onSelectSubNode(index: number) {
  emit('select', index)
}

function onAdd() {
  emit('add')
}

function onRemove(index: number) {
  emit('remove', index)
}

function getRoleName(roleCode?: string): string {
  if (!roleCode) return ''
  const role = props.roleList.find((r) => r.roleCode === roleCode)
  return role ? role.roleName : roleCode
}

function getSubNodeAssigneeDisplay(sub: any): string {
  const type = sub.assigneeType || 'ROLE'
  if (type === 'SELF') return '发起人自己'
  if (type === 'USER') return sub.assigneeValue ? `指定用户：${sub.assigneeValue}` : '未设置指定用户'
  return getRoleName(sub.assigneeValue) || '未设置审批角色'
}

function computePosition() {
  if (!props.node?.id) return
  try {
    // 通过选中的 DOM 元素计算节点中心位置（最可靠）
    const nodeEl = document.querySelector('.lf-node-selected')
    if (!nodeEl) return
    const shapeEl = nodeEl.querySelector('.lf-basic-shape') || nodeEl
    const rect = shapeEl.getBoundingClientRect()
    const canvasEl = document.querySelector('.canvas-area')
    const canvasRect = canvasEl ? canvasEl.getBoundingClientRect() : { left: 0, top: 0 }
    position.value = {
      x: rect.left + rect.width / 2 - canvasRect.left,
      y: rect.top + rect.height / 2 - canvasRect.top,
    }
  } catch {
    // ignore
  }
}

function registerListeners() {
  if (!props.lf) return
  removeListeners()
  props.lf.on('graph:transform', computePosition)
  props.lf.on('node:drag', computePosition)
  props.lf.on('node:dragstop', computePosition)
}

function removeListeners() {
  if (!props.lf) return
  props.lf.off('graph:transform', computePosition)
  props.lf.off('node:drag', computePosition)
  props.lf.off('node:dragstop', computePosition)
}

let registeredLf: any = null

watch(
  () => [props.lf, props.node?.id, props.visible],
  () => {
    if (props.lf !== registeredLf) {
      if (registeredLf) {
        registeredLf.off('graph:transform', computePosition)
        registeredLf.off('node:drag', computePosition)
        registeredLf.off('node:dragstop', computePosition)
      }
      registeredLf = props.lf
      registerListeners()
    }
    if (props.visible && showOverlay.value) {
      nextTick(computePosition)
    }
  },
  { immediate: true }
)

onMounted(() => {
  window.addEventListener('resize', computePosition)
  if (props.visible && showOverlay.value) {
    nextTick(computePosition)
  }
})

onUnmounted(() => {
  window.removeEventListener('resize', computePosition)
  removeListeners()
})
</script>

<style scoped>
.sub-node-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 100;
}

.drawer-position {
  z-index: 101;
}

.sub-node-drawer {
  width: 220px;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  pointer-events: auto;
  overflow: hidden;
}

.drawer-body {
  padding: 12px;
  max-height: 320px;
  overflow-y: auto;
}

.sub-node-card {
  display: flex;
  align-items: center;
  padding: 10px 12px;
  margin-bottom: 8px;
  border-radius: 8px;
  border: 1px solid #ebeef5;
  background: #fff;
  cursor: pointer;
  transition: all 0.2s;
  pointer-events: auto;
}

.sub-node-card:last-child {
  margin-bottom: 0;
}

.sub-node-card:hover {
  border-color: #c6e2ff;
  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.12);
}

.sub-node-card.active {
  border-color: #409eff;
  background: #ecf5ff;
}

.card-index {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: #409eff;
  color: #fff;
  font-size: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-right: 10px;
  flex-shrink: 0;
}

.card-info {
  flex: 1;
  min-width: 0;
}

.card-name {
  font-size: 14px;
  font-weight: 500;
  color: #303133;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.card-role {
  font-size: 12px;
  color: #909399;
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.card-actions {
  flex-shrink: 0;
  margin-left: 8px;
}

.delete-icon {
  color: #f56c6c;
  font-size: 14px;
  cursor: pointer;
}

.delete-icon:hover {
  color: #ff4d4f;
}

.add-card {
  justify-content: center;
  color: #409eff;
  border-style: dashed;
  border-color: #a0cfff;
  background: #f5f7fa;
}

.add-icon {
  margin-right: 6px;
}

.sub-node-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  border-radius: 12px;
  background: #409eff;
  color: #fff;
  font-size: 12px;
  pointer-events: auto;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
  z-index: 101;
}

.badge-arrow {
  transition: transform 0.2s;
  font-size: 10px;
}

.badge-arrow.open {
  transform: rotate(180deg);
}

.drawer-enter-active,
.drawer-leave-active {
  transition: opacity 0.2s, transform 0.2s;
}

.drawer-enter-from,
.drawer-leave-to {
  opacity: 0;
  transform: translate(-50%, 10px) scale(0.95);
}
</style>
