<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-header">
            <span class="drawer-title">子节点配置</span>
            <el-icon class="drawer-close" @click="emit('update:visible', false)">
              <Close />
            </el-icon>
          </div>
          <div class="drawer-body">
            <div
              v-for="(sub, index) in subNodes"
              :key="sub.id"
              class="sub-node-card"
              :class="{ active: selectedIndex === index }"
              @click="emit('select', 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">{{ getRoleName(sub.assigneeValue) || '未设置审批角色' }}</div>
              </div>
              <div class="card-actions" @click.stop>
                <el-icon class="delete-icon" @click="emit('remove', index)">
                  <Delete />
                </el-icon>
              </div>
            </div>
            <div class="sub-node-card add-card" @click="emit('add')">
              <el-icon class="add-icon"><Plus /></el-icon>
              <span>添加子节点</span>
            </div>
          </div>
        </div>
      </div>
    </Transition>

    <div class="sub-node-badge" :style="badgeStyle" @click="toggleVisible">
      <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 { Close, Plus, Delete, CaretTop, CaretBottom } from '@element-plus/icons-vue'
import type { Role } from '@/types/system'

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

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 NODE_HALF_HEIGHT = 30
const VERTICAL_GAP = 4
const BADGE_HEIGHT = 22

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

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

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

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

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 getRoleName(roleCode?: string): string {
  if (!roleCode) return ''
  const role = props.roleList.find((r) => r.roleCode === roleCode)
  return role ? role.roleName : roleCode
}

function computePosition() {
  if (!props.lf || !props.node?.id) return
  try {
    const nodeData = props.lf.getNodeData(props.node.id)
    if (!nodeData) return
    const htmlPoint = props.lf.graphModel.transformModel.CanvasPointToHtmlPoint([
      nodeData.x,
      nodeData.y,
    ])
    position.value = { x: htmlPoint[0], y: htmlPoint[1] }
  } 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: 240px;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  pointer-events: auto;
  overflow: hidden;
}

.drawer-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 12px;
  border-bottom: 1px solid #ebeef5;
  background: #fafafa;
}

.drawer-title {
  font-size: 14px;
  font-weight: 600;
  color: #303133;
}

.drawer-close {
  font-size: 14px;
  color: #909399;
  cursor: pointer;
}

.drawer-close:hover {
  color: #409eff;
}

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

.sub-node-card {
  display: flex;
  align-items: center;
  padding: 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: 2px 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%, -100%) scale(0.95);
}
</style>
