<script setup>
import { ref, computed, watch, onMounted, onUnmounted, nextTick, markRaw } from 'vue'
import { VueFlow, useVueFlow } from '@vue-flow/core'
import { Background } from '@vue-flow/background'
import { Controls } from '@vue-flow/controls'
import { MiniMap } from '@vue-flow/minimap'
import { ElMessage, ElMessageBox } from 'element-plus'
import '@vue-flow/core/dist/style.css'
import '@vue-flow/core/dist/theme-default.css'
import '@vue-flow/controls/dist/style.css'
import '@vue-flow/minimap/dist/style.css'

import SourceNode from './nodes/SourceNode.vue'
import ActionNode from './nodes/ActionNode.vue'
import ElementNode from './nodes/ElementNode.vue'
import NodePanel from './panels/NodePanel.vue'
import PropertyPanel from './panels/PropertyPanel.vue'

const props = defineProps({
  projectId: { type: Number, required: true },
  attachments: { type: Array, default: () => [] },
  elements: { type: Array, default: () => [] },
  rules: { type: Array, default: () => [] },
  targetRule: { type: Object, default: null },  // 编辑的目标规则
  targetElement: { type: Object, default: null }  // 目标要素（单要素模式）
})

const emit = defineEmits(['save', 'close'])

// 使用 markRaw 避免组件被响应式化
const nodeTypes = {
  source: markRaw(SourceNode),
  action: markRaw(ActionNode),
  element: markRaw(ElementNode)
}

const nodes = ref([])
const edges = ref([])
const selectedNode = ref(null)
const selectedEdge = ref(null)
const selectedNodes = ref([])

// 撤销重做
const historyStack = ref([])
const historyIndex = ref(-1)
const maxHistory = 50

// 右键菜单
const contextMenu = ref({ visible: false, x: 0, y: 0, type: '', target: null })

// 工作流验证
const validationErrors = ref([])
const showValidation = ref(false)

// 复制粘贴
const clipboard = ref(null)

// 规则预览
const showPreview = ref(false)
const previewRules = ref([])
const expandedRuleIdx = ref(null)

const { 
  onConnect, 
  addEdges, 
  onNodesChange, 
  onEdgesChange,
  onNodeClick,
  onEdgeClick,
  onPaneClick,
  onNodeContextMenu,
  onEdgeContextMenu,
  onPaneContextMenu,
  project,
  fitView,
  getSelectedNodes,
  removeNodes,
  removeEdges
} = useVueFlow()

// 历史记录
function saveHistory() {
  const state = {
    nodes: JSON.parse(JSON.stringify(nodes.value)),
    edges: JSON.parse(JSON.stringify(edges.value))
  }
  
  if (historyIndex.value < historyStack.value.length - 1) {
    historyStack.value = historyStack.value.slice(0, historyIndex.value + 1)
  }
  
  historyStack.value.push(state)
  if (historyStack.value.length > maxHistory) {
    historyStack.value.shift()
  } else {
    historyIndex.value++
  }
}

function undo() {
  if (historyIndex.value > 0) {
    historyIndex.value--
    const state = historyStack.value[historyIndex.value]
    nodes.value = JSON.parse(JSON.stringify(state.nodes))
    edges.value = JSON.parse(JSON.stringify(state.edges))
    selectedNode.value = null
    selectedEdge.value = null
    ElMessage.info('已撤销')
  }
}

function redo() {
  if (historyIndex.value < historyStack.value.length - 1) {
    historyIndex.value++
    const state = historyStack.value[historyIndex.value]
    nodes.value = JSON.parse(JSON.stringify(state.nodes))
    edges.value = JSON.parse(JSON.stringify(state.edges))
    selectedNode.value = null
    selectedEdge.value = null
    ElMessage.info('已重做')
  }
}

const canUndo = computed(() => historyIndex.value > 0)
const canRedo = computed(() => historyIndex.value < historyStack.value.length - 1)

// 连接验证
onConnect((params) => {
  if (validateConnection(params)) {
    saveHistory()
    addEdges([{
      ...params,
      id: `edge-${Date.now()}`,
      animated: true,
      style: { stroke: '#409eff', strokeWidth: 2 }
    }])
  } else {
    ElMessage.warning('无效的连接：请检查节点类型')
  }
})

onNodeClick(({ node }) => {
  selectedNode.value = node
  selectedEdge.value = null
  hideContextMenu()
})

onEdgeClick(({ edge }) => {
  selectedEdge.value = edge
  selectedNode.value = null
  hideContextMenu()
})

onPaneClick(() => {
  selectedNode.value = null
  selectedEdge.value = null
  hideContextMenu()
})

// 右键菜单
onNodeContextMenu(({ event, node }) => {
  event.preventDefault()
  selectedNode.value = node
  showContextMenu(event.clientX, event.clientY, 'node', node)
})

onEdgeContextMenu(({ event, edge }) => {
  event.preventDefault()
  selectedEdge.value = edge
  showContextMenu(event.clientX, event.clientY, 'edge', edge)
})

onPaneContextMenu(({ event }) => {
  event.preventDefault()
  showContextMenu(event.clientX, event.clientY, 'pane', null)
})

function showContextMenu(x, y, type, target) {
  contextMenu.value = { visible: true, x, y, type, target }
}

function hideContextMenu() {
  contextMenu.value.visible = false
}

function handleContextMenuAction(action) {
  const { type, target } = contextMenu.value
  hideContextMenu()
  
  switch (action) {
    case 'delete':
      if (type === 'node') handleDeleteNode(target.id)
      else if (type === 'edge') handleDeleteEdge(target.id)
      break
    case 'copy':
      if (type === 'node') copyNode(target)
      break
    case 'paste':
      pasteNode()
      break
    case 'duplicate':
      if (type === 'node') duplicateNode(target)
      break
  }
}

function validateConnection(params) {
  const sourceNode = nodes.value.find(n => n.id === params.source)
  const targetNode = nodes.value.find(n => n.id === params.target)
  
  if (!sourceNode || !targetNode) return false
  if (params.source === params.target) return false
  
  // 检查是否已存在连接
  const existingEdge = edges.value.find(e => 
    e.source === params.source && e.target === params.target
  )
  if (existingEdge) return false
  
  // 验证连接规则
  if (sourceNode.type === 'source' && targetNode.type === 'element') return true
  if (sourceNode.type === 'source' && targetNode.type === 'action') return true
  if (sourceNode.type === 'action' && targetNode.type === 'element') return true
  if (sourceNode.type === 'action' && targetNode.type === 'action') return true
  
  return false
}

function onDragOver(event) {
  event.preventDefault()
  event.dataTransfer.dropEffect = 'move'
}

function onDrop(event) {
  event.preventDefault()
  
  const dataStr = event.dataTransfer.getData('application/vueflow')
  if (!dataStr) return
  
  saveHistory()
  
  const data = JSON.parse(dataStr)
  const position = project({ x: event.clientX - 220, y: event.clientY - 60 })
  
  const newNode = {
    id: `node-${Date.now()}`,
    type: data.nodeType,
    position,
    data: {
      ...data,
      label: data.label || data.nodeType
    }
  }
  
  nodes.value.push(newNode)
  
  setTimeout(() => {
    selectedNode.value = newNode
  }, 50)
}

function handleNodeUpdate(nodeId, newData) {
  saveHistory()
  const node = nodes.value.find(n => n.id === nodeId)
  if (node) {
    node.data = { ...node.data, ...newData }
  }
}

function handleDeleteNode(nodeId) {
  saveHistory()
  nodes.value = nodes.value.filter(n => n.id !== nodeId)
  edges.value = edges.value.filter(e => e.source !== nodeId && e.target !== nodeId)
  selectedNode.value = null
}

function handleDeleteEdge(edgeId) {
  saveHistory()
  edges.value = edges.value.filter(e => e.id !== edgeId)
  selectedEdge.value = null
}

// 复制粘贴
function copyNode(node) {
  clipboard.value = JSON.parse(JSON.stringify(node))
  ElMessage.success('已复制节点')
}

function pasteNode() {
  if (!clipboard.value) {
    ElMessage.warning('剪贴板为空')
    return
  }
  
  saveHistory()
  const newNode = {
    ...clipboard.value,
    id: `node-${Date.now()}`,
    position: {
      x: clipboard.value.position.x + 50,
      y: clipboard.value.position.y + 50
    }
  }
  nodes.value.push(newNode)
  selectedNode.value = newNode
  ElMessage.success('已粘贴节点')
}

function duplicateNode(node) {
  saveHistory()
  const newNode = {
    ...JSON.parse(JSON.stringify(node)),
    id: `node-${Date.now()}`,
    position: {
      x: node.position.x + 50,
      y: node.position.y + 50
    }
  }
  nodes.value.push(newNode)
  selectedNode.value = newNode
}

// 工作流验证
function validateWorkflow() {
  const errors = []
  
  // 检查孤立节点
  const connectedNodeIds = new Set()
  edges.value.forEach(e => {
    connectedNodeIds.add(e.source)
    connectedNodeIds.add(e.target)
  })
  
  nodes.value.forEach(node => {
    if (!connectedNodeIds.has(node.id)) {
      errors.push({ type: 'warning', nodeId: node.id, message: `节点 "${node.data.label}" 未连接` })
    }
  })
  
  // 检查来源节点配置
  nodes.value.filter(n => n.type === 'source').forEach(node => {
    if (node.data.subType === 'attachment' && !node.data.sourceNodeId) {
      errors.push({ type: 'error', nodeId: node.id, message: `来源节点 "${node.data.label}" 未选择附件` })
    }
  })
  
  // 检查输出节点配置
  nodes.value.filter(n => n.type === 'element').forEach(node => {
    if (!node.data.elementKey) {
      errors.push({ type: 'error', nodeId: node.id, message: `输出节点 "${node.data.label}" 未选择要素` })
    }
  })
  
  // 检查动作节点配置
  nodes.value.filter(n => n.type === 'action').forEach(node => {
    const actionType = node.data.actionType || node.data.subType
    if (['summary', 'ai_extract'].includes(actionType) && !node.data.prompt) {
      errors.push({ type: 'warning', nodeId: node.id, message: `动作节点 "${node.data.label}" 建议配置提示词` })
    }
  })
  
  // 检查完整的数据流
  const elementNodes = nodes.value.filter(n => n.type === 'element')
  elementNodes.forEach(elemNode => {
    const hasInput = edges.value.some(e => e.target === elemNode.id)
    if (!hasInput) {
      errors.push({ type: 'error', nodeId: elemNode.id, message: `输出节点 "${elemNode.data.label}" 没有输入连接` })
    }
  })
  
  validationErrors.value = errors
  showValidation.value = true
  
  if (errors.length === 0) {
    ElMessage.success('工作流验证通过')
  } else {
    const errorCount = errors.filter(e => e.type === 'error').length
    const warningCount = errors.filter(e => e.type === 'warning').length
    ElMessage.warning(`发现 ${errorCount} 个错误，${warningCount} 个警告`)
  }
  
  return errors.filter(e => e.type === 'error').length === 0
}

function highlightNode(nodeId) {
  const node = nodes.value.find(n => n.id === nodeId)
  if (node) {
    selectedNode.value = node
    // 滚动到节点位置
  }
}

function handleSave() {
  if (!validateWorkflow()) {
    ElMessageBox.confirm('工作流存在错误，是否仍要保存？', '验证警告', {
      confirmButtonText: '继续保存',
      cancelButtonText: '返回修改',
      type: 'warning'
    }).then(() => {
      showRulePreview()
    }).catch(() => {})
  } else {
    showRulePreview()
  }
}

function showRulePreview() {
  const rules = generateRulesFromWorkflow()
  if (rules.length === 0) {
    ElMessage.warning('没有可保存的规则，请确保有完整的数据流（来源 → 输出）')
    return
  }
  previewRules.value = rules
  showPreview.value = true
}

function generateRulesFromWorkflow() {
  const rules = []
  const elementNodes = nodes.value.filter(n => n.type === 'element')
  
  function traceDataFlow(nodeId, visited = new Set()) {
    if (visited.has(nodeId)) return { sources: [], actions: [] }
    visited.add(nodeId)
    
    const node = nodes.value.find(n => n.id === nodeId)
    if (!node) return { sources: [], actions: [] }
    
    if (node.type === 'source') {
      return { sources: [node], actions: [] }
    }
    
    if (node.type === 'action') {
      const inEdges = edges.value.filter(e => e.target === nodeId)
      let allSources = []
      let allActions = [node]
      
      for (const edge of inEdges) {
        const upstream = traceDataFlow(edge.source, visited)
        allSources = [...allSources, ...upstream.sources]
        allActions = [...allActions, ...upstream.actions]
      }
      
      return { sources: allSources, actions: allActions }
    }
    
    return { sources: [], actions: [] }
  }
  
  for (const elementNode of elementNodes) {
    if (!elementNode.data.elementKey) continue
    
    const incomingEdges = edges.value.filter(e => e.target === elementNode.id)
    if (incomingEdges.length === 0) continue
    
    let allSources = []
    let allActions = []
    
    for (const edge of incomingEdges) {
      const { sources, actions } = traceDataFlow(edge.source)
      allSources = [...allSources, ...sources]
      allActions = [...allActions, ...actions]
    }
    
    const uniqueSources = [...new Map(allSources.map(s => [s.id, s])).values()]
    const uniqueActions = [...new Map(allActions.map(a => [a.id, a])).values()]
    
    const directInputNode = nodes.value.find(n => n.id === incomingEdges[0].source)
    let primaryAction = directInputNode?.type === 'action' ? directInputNode : uniqueActions[0]
    
    const actionType = primaryAction?.data?.actionType || primaryAction?.data?.subType || 'quote'
    
    rules.push({
      elementKey: elementNode.data.elementKey,
      elementName: elementNode.data.elementName || elementNode.data.label,
      actionType: actionType,
      actionLabel: getActionLabel(actionType),
      prompt: primaryAction?.data?.prompt || '',
      sources: uniqueSources.map(s => ({
        name: s.data.sourceName || s.data.label,
        type: s.data.subType,
        locatorType: s.data.locatorType
      })),
      actionsCount: uniqueActions.length
    })
  }
  
  return rules
}

function confirmSave() {
  showPreview.value = false
  doSave()
}

function doSave() {
  const workflowData = {
    nodes: nodes.value,
    edges: edges.value
  }
  emit('save', workflowData)
}

function handleClear() {
  ElMessageBox.confirm('确定要清空画布吗？此操作不可撤销。', '确认清空', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    saveHistory()
    nodes.value = []
    edges.value = []
    selectedNode.value = null
    selectedEdge.value = null
    ElMessage.success('画布已清空')
  }).catch(() => {})
}

function handleFitView() {
  fitView({ padding: 0.2 })
}

// 快捷键
function handleKeydown(event) {
  // 忽略输入框中的快捷键
  if (['INPUT', 'TEXTAREA'].includes(event.target.tagName)) return
  
  const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0
  const ctrlKey = isMac ? event.metaKey : event.ctrlKey
  
  switch (event.key) {
    case 'Delete':
    case 'Backspace':
      if (selectedNode.value) {
        handleDeleteNode(selectedNode.value.id)
      } else if (selectedEdge.value) {
        handleDeleteEdge(selectedEdge.value.id)
      }
      event.preventDefault()
      break
    case 'z':
      if (ctrlKey && event.shiftKey) {
        redo()
        event.preventDefault()
      } else if (ctrlKey) {
        undo()
        event.preventDefault()
      }
      break
    case 'y':
      if (ctrlKey) {
        redo()
        event.preventDefault()
      }
      break
    case 'c':
      if (ctrlKey && selectedNode.value) {
        copyNode(selectedNode.value)
        event.preventDefault()
      }
      break
    case 'v':
      if (ctrlKey) {
        pasteNode()
        event.preventDefault()
      }
      break
    case 'd':
      if (ctrlKey && selectedNode.value) {
        duplicateNode(selectedNode.value)
        event.preventDefault()
      }
      break
    case 's':
      if (ctrlKey) {
        handleSave()
        event.preventDefault()
      }
      break
    case 'Escape':
      selectedNode.value = null
      selectedEdge.value = null
      hideContextMenu()
      break
    case 'a':
      if (ctrlKey) {
        // 全选节点
        event.preventDefault()
      }
      break
  }
}

// VueFlow 初始化完成后调用
const flowInitialized = ref(false)
function onFlowInit() {
  console.log('VueFlow initialized, nodes:', nodes.value.length)
  flowInitialized.value = true
  // 延迟 fitView 确保节点已渲染
  setTimeout(() => {
    console.log('Calling fitView with nodes:', nodes.value.length)
    if (nodes.value.length > 0) {
      fitView({ padding: 0.2, maxZoom: 1, includeHiddenNodes: true })
    }
  }, 200)
}

onMounted(() => {
  // 如果有目标规则，加载该规则的工作流
  if (props.targetRule) {
    loadSingleRuleAsWorkflow(props.targetRule)
  } else if (props.targetElement) {
    // 新建规则模式：预置目标要素节点
    initWithTargetElement(props.targetElement)
  }
  // 新建模式（无 targetRule 和 targetElement）：空白画布，不加载任何规则
  
  // 保存初始状态
  saveHistory()
  
  // 注册快捷键
  window.addEventListener('keydown', handleKeydown)
})

onUnmounted(() => {
  window.removeEventListener('keydown', handleKeydown)
})

// 加载单个规则为工作流（编辑模式）
function loadSingleRuleAsWorkflow(rule) {
  console.log('loadSingleRuleAsWorkflow:', rule)
  const newNodes = []
  const newEdges = []
  const y = 150
  
  const elementId = `element-${rule.id}`
  const sourceId = `source-${rule.id}`
  const actionId = `action-${rule.id}`
  
  let lastNodeId = null
  let xPos = 50
  const nodeSpacing = 250  // 节点间距
  
  // 1. 添加来源节点
  // 对于 use_entity_value（人工录入）类型，添加一个"人工录入"来源节点
  if (rule.actionType === 'use_entity_value') {
    newNodes.push({
      id: sourceId,
      type: 'source',
      position: { x: xPos, y },
      data: {
        nodeType: 'source',
        subType: 'manual',
        label: '人工录入',
        sourceName: '人工录入',
        sourceText: '用户手工输入的值'
      }
    })
    lastNodeId = sourceId
    xPos += nodeSpacing
  } else if (rule.inputs && rule.inputs.length > 0) {
    // 其他类型：从 inputs 获取来源
    const input = rule.inputs[0]
    newNodes.push({
      id: sourceId,
      type: 'source',
      position: { x: xPos, y },
      data: {
        nodeType: 'source',
        subType: input.inputType || 'attachment',
        label: input.sourceName || input.inputName || '来源',
        sourceNodeId: input.sourceNodeId,
        sourceName: input.sourceName || input.inputName,
        sourceText: input.sourceText
      }
    })
    lastNodeId = sourceId
    xPos += nodeSpacing
  }
  
  // 2. 添加动作节点（如果不是 quote 和 use_entity_value 类型）
  if (rule.actionType && rule.actionType !== 'quote' && rule.actionType !== 'use_entity_value') {
    let prompt = ''
    try {
      prompt = rule.actionConfig ? JSON.parse(rule.actionConfig).prompt : ''
    } catch (e) {}
    
    newNodes.push({
      id: actionId,
      type: 'action',
      position: { x: xPos, y },
      data: {
        nodeType: 'action',
        subType: rule.actionType,
        label: getActionLabel(rule.actionType),
        actionType: rule.actionType,
        prompt: prompt
      }
    })
    
    // 连接来源到动作
    if (lastNodeId) {
      newEdges.push({
        id: `edge-${lastNodeId}-${actionId}`,
        source: lastNodeId,
        target: actionId,
        animated: true,
        style: { stroke: '#409eff', strokeWidth: 2 }
      })
    }
    lastNodeId = actionId
    xPos += nodeSpacing
  }
  
  // 3. 添加输出节点（目标要素）
  newNodes.push({
    id: elementId,
    type: 'element',
    position: { x: xPos, y },
    data: {
      nodeType: 'element',
      label: rule.elementKey,
      elementKey: rule.elementKey,
      elementName: getElementName(rule.elementKey)
    }
  })
  
  // 连接到输出节点
  if (lastNodeId) {
    newEdges.push({
      id: `edge-${lastNodeId}-${elementId}`,
      source: lastNodeId,
      target: elementId,
      animated: true,
      style: { stroke: '#67c23a', strokeWidth: 2 }
    })
  }
  
  console.log('Setting nodes:', newNodes.length, 'edges:', newEdges.length)
  nodes.value = newNodes
  edges.value = newEdges
  console.log('After set - nodes:', nodes.value.length, 'edges:', edges.value.length)
}

// 新建规则模式：预置目标要素节点
function initWithTargetElement(element) {
  const elementId = `element-new-${Date.now()}`
  nodes.value = [{
    id: elementId,
    type: 'element',
    position: { x: 400, y: 150 },
    data: {
      nodeType: 'element',
      label: element.elementName || element.elementKey,
      elementKey: element.elementKey,
      elementName: element.elementName || element.elementKey
    }
  }]
  edges.value = []
  
  setTimeout(() => fitView({ padding: 0.3 }), 100)
}

function loadRulesAsWorkflow(rules) {
  const newNodes = []
  const newEdges = []
  let xOffset = 100
  let yOffset = 100
  
  rules.forEach((rule, index) => {
    const y = yOffset + index * 150
    
    if (rule.inputs && rule.inputs.length > 0) {
      const input = rule.inputs[0]
      const sourceId = `source-${rule.id}-${index}`
      newNodes.push({
        id: sourceId,
        type: 'source',
        position: { x: xOffset, y },
        data: {
          nodeType: 'source',
          subType: input.inputType || 'attachment',
          label: input.sourceName || input.inputName || '来源',
          sourceNodeId: input.sourceNodeId,
          sourceName: input.sourceName || input.inputName,
          sourceText: input.sourceText
        }
      })
      
      if (rule.actionType && rule.actionType !== 'quote') {
        const actionId = `action-${rule.id}-${index}`
        newNodes.push({
          id: actionId,
          type: 'action',
          position: { x: xOffset + 200, y },
          data: {
            nodeType: 'action',
            subType: rule.actionType,
            label: getActionLabel(rule.actionType),
            actionType: rule.actionType,
            prompt: rule.actionConfig ? JSON.parse(rule.actionConfig).prompt : ''
          }
        })
        
        newEdges.push({
          id: `edge-${sourceId}-${actionId}`,
          source: sourceId,
          target: actionId,
          animated: true,
          style: { stroke: '#409eff', strokeWidth: 2 }
        })
        
        const elementId = `element-${rule.id}-${index}`
        newNodes.push({
          id: elementId,
          type: 'element',
          position: { x: xOffset + 400, y },
          data: {
            nodeType: 'element',
            label: rule.elementKey,
            elementKey: rule.elementKey,
            elementName: getElementName(rule.elementKey)
          }
        })
        
        newEdges.push({
          id: `edge-${actionId}-${elementId}`,
          source: actionId,
          target: elementId,
          animated: true,
          style: { stroke: '#409eff', strokeWidth: 2 }
        })
      } else {
        const elementId = `element-${rule.id}-${index}`
        newNodes.push({
          id: elementId,
          type: 'element',
          position: { x: xOffset + 250, y },
          data: {
            nodeType: 'element',
            label: rule.elementKey,
            elementKey: rule.elementKey,
            elementName: getElementName(rule.elementKey)
          }
        })
        
        newEdges.push({
          id: `edge-${sourceId}-${elementId}`,
          source: sourceId,
          target: elementId,
          animated: true,
          style: { stroke: '#67c23a', strokeWidth: 2 }
        })
      }
    }
  })
  
  nodes.value = newNodes
  edges.value = newEdges
  
  setTimeout(() => fitView({ padding: 0.2 }), 100)
}

function getActionLabel(actionType) {
  const labels = {
    quote: '引用',
    summary: 'AI 总结',
    ai_extract: 'AI 提取',
    table_extract: '表格提取'
  }
  return labels[actionType] || actionType
}

function getElementName(elementKey) {
  const elem = props.elements.find(e => e.elementKey === elementKey)
  return elem ? elem.elementName : elementKey
}

function getActionTagType(actionType) {
  const types = {
    quote: 'success',
    summary: 'warning',
    ai_extract: '',
    table_extract: 'info'
  }
  return types[actionType] || 'info'
}

defineExpose({
  handleSave,
  handleClear,
  handleFitView,
  undo,
  redo,
  validateWorkflow
})
</script>

<template>
  <div class="rule-workflow" @click="hideContextMenu">
    <div class="workflow-toolbar">
      <div class="toolbar-left">
        <el-button-group>
          <el-button size="small" :disabled="!canUndo" @click="undo" title="撤销 (Ctrl+Z)">
            ↩️ 撤销
          </el-button>
          <el-button size="small" :disabled="!canRedo" @click="redo" title="重做 (Ctrl+Y)">
            ↪️ 重做
          </el-button>
        </el-button-group>
        
        <el-divider direction="vertical" />
        
        <el-button size="small" @click="handleFitView" title="适应视图">📐 适应</el-button>
        <el-button size="small" @click="validateWorkflow" title="验证工作流">✅ 验证</el-button>
        <el-button size="small" type="danger" plain @click="handleClear">🗑️ 清空</el-button>
        
        <el-divider direction="vertical" />
        
        <el-button type="primary" size="small" @click="handleSave" title="保存 (Ctrl+S)">
          💾 保存规则
        </el-button>
      </div>
      <div class="toolbar-right">
        <span class="workflow-stats">
          节点: {{ nodes.length }} | 连线: {{ edges.length }}
        </span>
        <el-tag v-if="validationErrors.length > 0" type="warning" size="small">
          {{ validationErrors.filter(e => e.type === 'error').length }} 错误
        </el-tag>
      </div>
    </div>
    
    <!-- 验证结果面板 -->
    <div class="validation-panel" v-if="showValidation && validationErrors.length > 0">
      <div class="validation-header">
        <span>验证结果</span>
        <el-button text size="small" @click="showValidation = false">✕</el-button>
      </div>
      <div class="validation-list">
        <div 
          v-for="(err, idx) in validationErrors" 
          :key="idx" 
          class="validation-item"
          :class="err.type"
          @click="highlightNode(err.nodeId)"
        >
          <span class="validation-icon">{{ err.type === 'error' ? '❌' : '⚠️' }}</span>
          <span class="validation-msg">{{ err.message }}</span>
        </div>
      </div>
    </div>
    
    <div class="workflow-container">
      <NodePanel 
        :attachments="attachments" 
        :elements="elements"
        class="workflow-node-panel"
      />
      
      <div class="workflow-canvas" @dragover="onDragOver" @drop="onDrop">
        <VueFlow
          v-model:nodes="nodes"
          v-model:edges="edges"
          :node-types="nodeTypes"
          :default-viewport="{ zoom: 1, x: 0, y: 0 }"
          :min-zoom="0.2"
          :max-zoom="2"
          class="vue-flow-wrapper"
          @init="onFlowInit"
        >
          <Background pattern-color="#aaa" :gap="16" />
          <Controls position="bottom-left" />
          <MiniMap position="bottom-right" />
        </VueFlow>
        
        <!-- 右键菜单 -->
        <div 
          v-if="contextMenu.visible" 
          class="context-menu"
          :style="{ left: contextMenu.x + 'px', top: contextMenu.y + 'px' }"
          @click.stop
        >
          <template v-if="contextMenu.type === 'node'">
            <div class="context-menu-item" @click="handleContextMenuAction('copy')">
              📋 复制 <span class="shortcut">Ctrl+C</span>
            </div>
            <div class="context-menu-item" @click="handleContextMenuAction('duplicate')">
              📑 复制节点 <span class="shortcut">Ctrl+D</span>
            </div>
            <div class="context-menu-divider"></div>
            <div class="context-menu-item danger" @click="handleContextMenuAction('delete')">
              🗑️ 删除 <span class="shortcut">Delete</span>
            </div>
          </template>
          <template v-else-if="contextMenu.type === 'edge'">
            <div class="context-menu-item danger" @click="handleContextMenuAction('delete')">
              🗑️ 删除连线 <span class="shortcut">Delete</span>
            </div>
          </template>
          <template v-else>
            <div class="context-menu-item" @click="handleContextMenuAction('paste')" :class="{ disabled: !clipboard }">
              📋 粘贴 <span class="shortcut">Ctrl+V</span>
            </div>
            <div class="context-menu-divider"></div>
            <div class="context-menu-item" @click="handleFitView">
              📐 适应视图
            </div>
          </template>
        </div>
      </div>
      
      <PropertyPanel
        :selected-node="selectedNode"
        :selected-edge="selectedEdge"
        :attachments="attachments"
        :elements="elements"
        class="workflow-property-panel"
        @update-node="handleNodeUpdate"
        @delete-node="handleDeleteNode"
        @delete-edge="handleDeleteEdge"
      />
    </div>
    
    <!-- 快捷键提示 -->
    <div class="shortcuts-hint">
      <span>快捷键: Ctrl+S 保存 | Ctrl+Z 撤销 | Ctrl+Y 重做 | Delete 删除 | Ctrl+C/V 复制粘贴</span>
    </div>
    
    <!-- 规则预览弹窗 -->
    <el-dialog
      v-model="showPreview"
      title="📋 规则预览"
      width="700"
      :close-on-click-modal="false"
    >
      <div class="preview-content">
        <p class="preview-desc">将创建以下 <strong>{{ previewRules.length }}</strong> 条规则：</p>
        
        <div class="preview-list">
          <div 
            v-for="(rule, idx) in previewRules" 
            :key="idx" 
            class="preview-item"
            :class="{ expanded: expandedRuleIdx === idx }"
          >
            <div class="preview-header" @click="expandedRuleIdx = expandedRuleIdx === idx ? null : idx">
              <span class="preview-index">{{ idx + 1 }}</span>
              <span class="preview-element">{{ rule.elementName }}</span>
              <el-tag size="small" :type="getActionTagType(rule.actionType)">
                {{ rule.actionLabel }}
              </el-tag>
              <span class="preview-sources-count" v-if="rule.sources.length > 0">
                📎 {{ rule.sources.length }}
              </span>
              <span class="preview-expand-icon">{{ expandedRuleIdx === idx ? '▼' : '▶' }}</span>
            </div>
            <div class="preview-body" v-show="expandedRuleIdx === idx">
              <div class="preview-row">
                <span class="preview-label">要素标识:</span>
                <code class="preview-value">{{ rule.elementKey }}</code>
              </div>
              <div class="preview-row" v-if="rule.sources.length > 0">
                <span class="preview-label">数据来源:</span>
                <span class="preview-value">
                  <el-tag v-for="(src, i) in rule.sources" :key="i" size="small" type="info" class="source-tag">
                    📎 {{ src.name }}
                  </el-tag>
                </span>
              </div>
              <div class="preview-row" v-if="rule.prompt">
                <span class="preview-label">提示词:</span>
                <span class="preview-value preview-prompt">{{ rule.prompt }}</span>
              </div>
            </div>
          </div>
        </div>
      </div>
      
      <template #footer>
        <el-button @click="showPreview = false">取消</el-button>
        <el-button type="primary" @click="confirmSave">
          确认保存 ({{ previewRules.length }} 条规则)
        </el-button>
      </template>
    </el-dialog>
  </div>
</template>

<style scoped>
.rule-workflow {
  display: flex;
  flex-direction: column;
  height: calc(100vh - 54px);  /* 弹窗高度减去 header */
  background: #f5f7fa;
}

.workflow-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 16px;
  background: white;
  border-bottom: 1px solid #e4e7ed;
}

.toolbar-left {
  display: flex;
  gap: 8px;
}

.toolbar-right {
  display: flex;
  align-items: center;
  gap: 16px;
}

.workflow-stats {
  font-size: 13px;
  color: #909399;
}

.workflow-container {
  display: flex;
  flex: 1;
  overflow: hidden;
}

.workflow-node-panel {
  width: 280px;
  flex-shrink: 0;
  background: white;
  border-right: 1px solid #e4e7ed;
  overflow-y: auto;
}

.workflow-canvas {
  flex: 1;
  position: relative;
}

.vue-flow-wrapper {
  width: 100%;
  height: 100%;
}

.workflow-property-panel {
  width: 300px;
  flex-shrink: 0;
  background: white;
  border-left: 1px solid #e4e7ed;
  overflow-y: auto;
}

:deep(.vue-flow__node) {
  cursor: grab;
}

:deep(.vue-flow__node:active) {
  cursor: grabbing;
}

:deep(.vue-flow__edge-path) {
  stroke-width: 2;
}

:deep(.vue-flow__handle) {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #409eff;
  border: 2px solid white;
}

:deep(.vue-flow__handle-left) {
  left: -6px;
}

:deep(.vue-flow__handle-right) {
  right: -6px;
}

/* 验证面板 */
.validation-panel {
  background: #fef0f0;
  border-bottom: 1px solid #fbc4c4;
  padding: 8px 16px;
}

.validation-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 13px;
  font-weight: 500;
  color: #f56c6c;
  margin-bottom: 8px;
}

.validation-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.validation-item {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  border-radius: 4px;
  font-size: 12px;
  cursor: pointer;
  transition: all 0.2s;
}

.validation-item.error {
  background: #fef0f0;
  color: #f56c6c;
  border: 1px solid #fbc4c4;
}

.validation-item.warning {
  background: #fdf6ec;
  color: #e6a23c;
  border: 1px solid #f5dab1;
}

.validation-item:hover {
  transform: translateY(-1px);
  box-shadow: 0 2px 6px rgba(0,0,0,0.1);
}

.validation-icon {
  font-size: 12px;
}

.validation-msg {
  max-width: 300px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 右键菜单 */
.context-menu {
  position: fixed;
  background: white;
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.15);
  min-width: 180px;
  padding: 6px 0;
  z-index: 1000;
}

.context-menu-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 16px;
  font-size: 13px;
  color: #303133;
  cursor: pointer;
  transition: background 0.15s;
}

.context-menu-item:hover {
  background: #f5f7fa;
}

.context-menu-item.danger {
  color: #f56c6c;
}

.context-menu-item.danger:hover {
  background: #fef0f0;
}

.context-menu-item.disabled {
  color: #c0c4cc;
  cursor: not-allowed;
}

.context-menu-item.disabled:hover {
  background: transparent;
}

.context-menu-item .shortcut {
  font-size: 11px;
  color: #909399;
  margin-left: 20px;
}

.context-menu-divider {
  height: 1px;
  background: #e4e7ed;
  margin: 6px 0;
}

/* 快捷键提示 */
.shortcuts-hint {
  padding: 8px 16px;
  background: #f5f7fa;
  border-top: 1px solid #e4e7ed;
  font-size: 11px;
  color: #909399;
  text-align: center;
}

/* 工具栏分隔线 */
.toolbar-left :deep(.el-divider--vertical) {
  height: 20px;
  margin: 0 8px;
}

/* 规则预览弹窗 */
.preview-content {
  max-height: 60vh;
  overflow-y: auto;
}

.preview-desc {
  margin-bottom: 16px;
  color: #606266;
  font-size: 14px;
}

.preview-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.preview-item {
  border: 1px solid #e4e7ed;
  border-radius: 8px;
  overflow: hidden;
}

.preview-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  background: #f5f7fa;
  cursor: pointer;
  transition: background 0.2s;
}

.preview-header:hover {
  background: #ebeef5;
}

.preview-item.expanded .preview-header {
  border-bottom: 1px solid #e4e7ed;
}

.preview-sources-count {
  font-size: 12px;
  color: #909399;
}

.preview-expand-icon {
  margin-left: auto;
  font-size: 10px;
  color: #909399;
}

.preview-index {
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #409eff;
  color: white;
  border-radius: 50%;
  font-size: 12px;
  font-weight: 500;
}

.preview-element {
  flex: 1;
  font-size: 14px;
  font-weight: 500;
  color: #303133;
}

.preview-body {
  padding: 12px 16px;
}

.preview-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 8px;
}

.preview-row:last-child {
  margin-bottom: 0;
}

.preview-label {
  flex-shrink: 0;
  width: 70px;
  font-size: 12px;
  color: #909399;
}

.preview-value {
  flex: 1;
  font-size: 13px;
  color: #303133;
}

.preview-value code {
  background: #f5f7fa;
  padding: 2px 6px;
  border-radius: 4px;
  font-family: monospace;
  font-size: 12px;
}

.preview-prompt {
  background: #fdf6ec;
  padding: 6px 10px;
  border-radius: 4px;
  font-size: 12px;
  line-height: 1.5;
  color: #e6a23c;
}

.source-tag {
  margin-right: 6px;
  margin-bottom: 4px;
}
</style>
