<template>
  <div class="workflow-canvas-page">
    <div class="canvas-header">
      <div class="header-left">
        <el-button @click="goBack" icon="el-icon-back" plain size="small">返回模板库</el-button>
        <span class="header-title">工作流可视化编辑</span>
        <el-tag v-if="templateData" size="small">{{ templateData.template_name || templateData.templateName }}</el-tag>
      </div>
      <div class="header-right">
        <el-radio-group v-model="viewMode" size="small">
          <el-radio-button label="canvas">画布视图</el-radio-button>
          <el-radio-button label="list">列表视图</el-radio-button>
        </el-radio-group>
        <el-button type="primary" size="small" @click="saveWorkflow" :loading="saving" style="margin-left:12px">
          <i class="el-icon-check"></i> 保存
        </el-button>
      </div>
    </div>

    <div v-if="!workflowId" class="no-template">
      <el-empty description="请从模板库选择一个模板进行编辑">
        <el-button type="primary" @click="goBack">返回模板库</el-button>
      </el-empty>
    </div>

    <template v-else>
      <el-card class="info-card" shadow="never" v-if="templateData">
        <el-descriptions :column="4" size="small" border>
          <el-descriptions-item label="模板名称">
            <el-input v-model="templateName" size="mini" style="width:200px" />

          </el-descriptions-item>
          <el-descriptions-item label="行业类型">{{ templateField('industry_type') || '-' }}</el-descriptions-item>
          <el-descriptions-item label="模板编码">{{ templateField('template_code') || '-' }}</el-descriptions-item>
          <el-descriptions-item label="节点数量">{{ nodes.length }} 个</el-descriptions-item>
        </el-descriptions>
      </el-card>

      <!-- ── 画布视图 ── -->
      <div v-show="viewMode === 'canvas'" class="canvas-container" ref="canvasContainer">
        <div class="canvas-toolbar">
          <el-button size="mini" @click="addNode(1)" type="success" plain>开始</el-button>
          <el-button size="mini" @click="addNode(2)" plain>消息</el-button>
          <el-button size="mini" @click="addNode(3)" type="warning" plain>判断</el-button>
          <el-button size="mini" @click="addNode(4)" type="info" plain>等待</el-button>
          <el-button size="mini" @click="addNode(5)" type="danger" plain>结束</el-button>
          <el-button size="mini" @click="addNode(8)" plain>优惠券</el-button>
          <el-button size="mini" @click="addNode(9)" plain>标签</el-button>
          <el-button size="mini" @click="addNode(10)" plain>关怀</el-button>
          <el-button size="mini" @click="addNode(11)" plain>调研</el-button>
          <el-button size="mini" @click="addNode(12)" type="info" plain>画像</el-button>
          <el-button size="mini" @click="addNode(13)" plain>复购</el-button>
          <el-button size="mini" @click="addNode(14)" plain>智能API</el-button>
          <el-button size="mini" @click="autoLayout" icon="el-icon-sort" style="margin-left:8px">自动排列</el-button>
        </div>
        <div class="canvas-area" ref="canvasArea"
             @mousedown="onCanvasMouseDown"
             @mousemove="onCanvasMouseMove"
             @mouseup="onCanvasMouseUp">
          <svg class="canvas-lines" v-if="nodes.length > 1">
            <defs>
              <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
                <polygon points="0 0, 10 3.5, 0 7" fill="#409EFF" />
              </marker>
              <marker id="arrowhead-green" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
                <polygon points="0 0, 10 3.5, 0 7" fill="#67C23A" />
              </marker>
              <marker id="arrowhead-red" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
                <polygon points="0 0, 10 3.5, 0 7" fill="#F56C6C" />
              </marker>
            </defs>
            <line v-for="(edge, idx) in edges" :key="'e'+idx"
                  :x1="edge.x1" :y1="edge.y1" :x2="edge.x2" :y2="edge.y2"
                  :stroke="edge.color || '#409EFF'" stroke-width="2"
                  :marker-end="edge.marker || 'url(#arrowhead)'" />
          </svg>
          <div v-for="(node, index) in nodes" :key="'n'+index"
               class="canvas-node"
               :class="'node-type-' + (NODE_COLORS[node.nodeType] ? node.nodeType : 'default')"
               :style="{ left: node.x + 'px', top: node.y + 'px' }"
               @mousedown.stop="onNodeMouseDown($event, index)">
            <div class="node-title">
              <span>{{ node.nodeName }}</span>
              <el-tag size="mini" :type="getNodeTagType(node.nodeType)">{{ NODE_NAMES[node.nodeType] || node.nodeType }}</el-tag>
            </div>
            <div class="node-body" v-if="node.messageTemplate">
              <div class="node-msg">{{ truncate(node.messageTemplate, 50) }}</div>
            </div>
            <div class="node-body" v-if="node.nodeType === 3 && node.conditionExpr">
              <el-tag size="mini" type="warning">条件分支</el-tag>
            </div>
            <div class="node-actions">
              <el-button size="mini" type="text" icon="el-icon-edit" @click.stop="editNode(index)"></el-button>
              <el-button size="mini" type="text" icon="el-icon-delete" @click.stop="removeNode(index)" style="color:#F56C6C"></el-button>
            </div>
          </div>
        </div>
      </div>

      <!-- ── 列表视图 ── -->
      <div v-show="viewMode === 'list'" class="list-container">
        <el-card shadow="never">
          <div slot="header">
            <div style="display:flex;justify-content:space-between;align-items:center">
              <span>节点列表（{{ nodes.length }}个）</span>
              <el-button type="primary" size="small" @click="showAddNodeDialog = true">
                <i class="el-icon-plus"></i> 添加节点
              </el-button>
            </div>
          </div>
          <el-timeline>
            <el-timeline-item v-for="(node, index) in nodes" :key="index" :type="getNodeTagType(node.nodeType)">
              <el-card shadow="hover" class="timeline-node-card">
                <div style="display:flex;justify-content:space-between;align-items:center">
                  <div>
                    <span style="font-weight:bold">{{ node.nodeName }}</span>
                    <el-tag size="small" :type="getNodeTagType(node.nodeType)" style="margin-left:8px">{{ NODE_NAMES[node.nodeType] || node.nodeType }}</el-tag>
                    <span v-if="node.nextNodeCode" style="font-size:12px;color:#909399;margin-left:8px">→ {{ node.nextNodeCode }}</span>
                  </div>
                  <div>
                    <el-button size="mini" type="text" icon="el-icon-edit" @click="editNode(index)">编辑</el-button>
                    <el-button size="mini" type="text" icon="el-icon-delete" @click="removeNode(index)" style="color:#F56C6C">删除</el-button>
                  </div>
                </div>
                <div v-if="node.messageTemplate" style="margin-top:8px;font-size:12px;color:#606266;white-space:pre-wrap;background:#f5f7fa;padding:8px;border-radius:4px">{{ node.messageTemplate }}</div>
              </el-card>
            </el-timeline-item>
          </el-timeline>
        </el-card>
      </div>
    </template>

    <!-- 节点编辑弹窗 -->
    <el-dialog :title="editingNodeIndex >= 0 ? '编辑节点' : '添加节点'" :visible.sync="nodeDialogVisible" width="650px" append-to-body>
      <el-form :model="nodeForm" label-width="100px" size="small">
        <el-row :gutter="12">
          <el-col :span="12">
            <el-form-item label="节点名称"><el-input v-model="nodeForm.nodeName" /></el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="节点编码"><el-input v-model="nodeForm.nodeCode" /></el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="12">
          <el-col :span="12">
            <el-form-item label="节点类型">
              <el-select v-model="nodeForm.nodeType" style="width:100%">
                <el-option v-for="(name, type) in NODE_NAMES" :key="type" :label="name" :value="Number(type)" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="下一节点"><el-input v-model="nodeForm.nextNodeCode" placeholder="下一节点编码" /></el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="12">
          <el-col :span="12">
            <el-form-item label="排序号"><el-input-number v-model="nodeForm.sortNo" :min="0" style="width:100%" /></el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="最大轮次"><el-input-number v-model="nodeForm.maxRound" :min="0" style="width:100%" /></el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="12">
          <el-col :span="12">
            <el-form-item label="模型"><el-input v-model="nodeForm.modelName" placeholder="如 gpt-4o-mini" /></el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="场景编码"><el-input v-model="nodeForm.sceneCode" placeholder="如 sale" /></el-form-item>
          </el-col>
        </el-row>
        <el-form-item label="Prompt模板" v-if="[2,7,10,11,13].includes(nodeForm.nodeType)">
          <el-select v-model="nodeForm.promptId" style="width:100%" placeholder="选择Prompt模板（可选）" clearable filterable :loading="promptLoading">
            <el-option v-for="p in promptList" :key="p.id" :label="p.promptName || p.title" :value="p.id" />
          </el-select>
        </el-form-item>
        <el-form-item v-if="nodeForm.nodeType === 2 || nodeForm.nodeType === 10 || nodeForm.nodeType === 11" label="消息模板">
          <el-input v-model="nodeForm.messageTemplate" type="textarea" :rows="4" />
        </el-form-item>
        <el-form-item v-if="nodeForm.nodeType === 3" label="条件表达式">
          <el-input v-model="nodeForm.conditionExpr" type="textarea" :rows="3" placeholder='{"branches":[{"nextNode":"A","condition":"...","desc":"描述"}]}' />
        </el-form-item>
        <el-form-item v-if="nodeForm.nodeType === 4" label="等待配置">
          <el-input v-model="nodeForm.nodeConfig" type="textarea" :rows="3" placeholder='{"waitType":"daily","sendTime":"08:00:00"}' />
        </el-form-item>
        <el-form-item v-if="[8,9,12,14].includes(nodeForm.nodeType)" label="节点配置JSON">
          <el-input v-model="nodeForm.nodeConfig" type="textarea" :rows="3" />
        </el-form-item>
        <el-form-item label="发送时间" v-if="nodeForm.nodeType === 4">
          <el-time-picker v-model="nodeForm._sendTime" value-format="HH:mm:ss" placeholder="选择时间" />
        </el-form-item>
      </el-form>
      <div slot="footer">
        <el-button @click="nodeDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="confirmNode">确定</el-button>
      </div>
    </el-dialog>

    <!-- 快速添加节点 -->
    <el-dialog title="添加节点" :visible.sync="showAddNodeDialog" width="400px" append-to-body>
      <div style="display:flex;flex-direction:column;gap:6px">
        <el-button v-for="(name, type) in NODE_NAMES" :key="type" @click="addNode(Number(type)); showAddNodeDialog=false" :type="getNodeTagType(Number(type))" plain>{{ name }}</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { getWorkflowNodes, saveWorkflowNodes, listPrompts } from '@/api/workflow/lobster'

const NODE_NAMES = {
  1: '开始', 2: '消息', 3: '判断', 4: '等待', 5: '结束',
  7: '成单', 8: '优惠券', 9: '标签', 10: '关怀', 11: '调研',
  12: '画像', 13: '复购', 14: '智能API',
  20: '意图识别', 21: '转人工检测', 22: '质检评分',
  23: '知识库检索', 30: '企微消息', 31: '个微消息',
  40: '变量赋值', 41: '打标签', 42: 'Webhook',
  50: 'SOP执行', 51: 'CID任务', 52: '商品推送', 53: '物流推送', 100: '外部API'
}
const NODE_COLORS = {
  1: 'success', 2: '', 3: 'warning', 4: 'info', 5: 'danger',
  7: '', 8: 'warning', 9: 'info', 10: '', 11: '', 12: 'info', 13: '', 14: '',
  20: '', 21: 'warning', 22: 'info'
}

export default {
  name: 'LobsterCanvas',
  data() {
    return {
      workflowId: null,
      templateData: null,
      templateName: '',
      nodes: [],
      viewMode: 'canvas',
      nodeDialogVisible: false,
      showAddNodeDialog: false,
      editingNodeIndex: -1,
      nodeForm: {},
      promptLoading: false,
      promptList: [],
      dragging: false, dragIndex: -1, dragOffsetX: 0, dragOffsetY: 0,
      saving: false,
      NODE_NAMES, NODE_COLORS
    }
  },
  computed: {
    edges() {
      const result = []
      const nodeMap = {}
      this.nodes.forEach((n, i) => { nodeMap[n.nodeCode] = i })
      this.nodes.forEach((n, i) => {
        if (n.nodeType === 3 && n.conditionExpr) {
          // 判断节点：解析 branches
          try {
            const cfg = typeof n.conditionExpr === 'string' ? JSON.parse(n.conditionExpr) : n.conditionExpr
            if (cfg && cfg.branches) {
              cfg.branches.forEach(b => {
                const ti = nodeMap[b.nextNode]
                if (ti !== undefined && this.nodes[ti]) {
                  const color = b.nextNode === (cfg.branches[0] && cfg.branches[0].nextNode) ? '#67C23A' : '#F56C6C'
                  result.push({
                    x1: n.x + 90, y1: n.y + 40, x2: this.nodes[ti].x + 90, y2: this.nodes[ti].y + 40,
                    color, marker: color === '#67C23A' ? 'url(#arrowhead-green)' : 'url(#arrowhead-red)'
                  })
                }
              })
            }
          } catch (e) {}
        }
        if (n.nextNodeCode) {
          const ti = nodeMap[n.nextNodeCode]
          if (ti !== undefined && this.nodes[ti]) {
            result.push({ x1: n.x + 90, y1: n.y + 40, x2: this.nodes[ti].x + 90, y2: this.nodes[ti].y + 40, color: '#409EFF', marker: 'url(#arrowhead)' })
          }
        }
      })
      return result
    }
  },
  created() {
    this.workflowId = this.$route.query.workflowId
    if (this.workflowId) this.loadNodes()
  },
  methods: {
    templateField(key) {
      return this.templateData ? (this.templateData[key] || this.templateData[key.replace(/_/g, '')]) : null
    },
    truncate(s, max) { return s && s.length > max ? s.substring(0, max) + '...' : s },
    loadNodes() {
      getWorkflowNodes(this.workflowId).then(res => {
        const data = res.data || {}
        this.templateData = data.template || {}
        this.templateName = this.templateData.template_name || this.templateData.templateName || ''
        this.nodes = (data.nodes || []).map((n, i) => ({
          ...n,
          x: n.x || 100 + (i % 5) * 200,
          y: n.y || 80 + Math.floor(i / 5) * 130,
          maxRound: n.max_round || n.maxRound || 0,
          nodeType: Number(n.node_type || n.nodeType),
          nodeCode: n.node_code || n.nodeCode,
          nodeName: n.node_name || n.nodeName,
          nextNodeCode: n.next_node_code || n.nextNodeCode || '',
          messageTemplate: n.message_template || n.messageTemplate || '',
          conditionExpr: n.condition_expr || n.conditionExpr || '',
          nodeConfig: n.node_config || n.nodeConfig || '',
          sceneCode: n.scene_code || n.sceneCode || '',
          modelName: n.model_name || n.modelName || '',
          sendTime: n.send_time || n.sendTime || '',
          sortNo: n.sort_no || n.sortNo || i + 1,
          promptId: extractPromptId(n.node_config || n.nodeConfig)
        }))
      }).catch(() => this.$message.error('加载工作流失败'))
    },
    goBack() { this.$router.push('/lobster/production-workflow/template') },
    loadPrompts() {
      if (this.promptList.length > 0) return
      this.promptLoading = true
      listPrompts({ page: 1, size: 200 }).then(res => {
        this.promptList = (res.data && res.data.records) ? res.data.records : (res.data || [])
      }).finally(() => { this.promptLoading = false })
    },
    addNode(type) {
      const idx = this.nodes.length + 1
      const defaultName = NODE_NAMES[type] || '节点'
      this.nodes.push({
        nodeType: type, nodeName: defaultName + '_' + idx, nodeCode: 'NODE_' + idx,
        nextNodeCode: '', messageTemplate: '', conditionExpr: '', nodeConfig: '',
        sceneCode: '', modelName: '', sendTime: '', sortNo: idx, maxRound: 0,
        x: 100 + (idx % 5) * 200, y: 80 + Math.floor(idx / 5) * 130
      })
    },
    editNode(index) {
      this.editingNodeIndex = index
      const n = this.nodes[index]
      this.nodeForm = { ...n, _sendTime: n.sendTime || null }
      this.nodeDialogVisible = true
      this.loadPrompts()
    },
    confirmNode() {
      if (this.nodeForm._sendTime) this.nodeForm.sendTime = this.nodeForm._sendTime
      delete this.nodeForm._sendTime
      if (this.editingNodeIndex >= 0) {
        const pos = { x: this.nodes[this.editingNodeIndex].x, y: this.nodes[this.editingNodeIndex].y }
        this.$set(this.nodes, this.editingNodeIndex, { ...this.nodeForm, ...pos })
      }
      this.nodeDialogVisible = false; this.editingNodeIndex = -1
    },
    removeNode(index) {
      this.$confirm('确定删除该节点吗？', '提示', { type: 'warning' }).then(() => this.nodes.splice(index, 1))
    },
    autoLayout() {
      const cols = 5
      this.nodes.forEach((n, i) => {
        this.$set(n, 'x', 100 + (i % cols) * 200)
        this.$set(n, 'y', 80 + Math.floor(i / cols) * 130)
      })
    },
    saveWorkflow() {
      this.saving = true
      const payload = {
        workflowId: Number(this.workflowId),
        templateName: this.templateName,
        industryType: this.templateField('industry_type'),
        description: this.templateField('description'),
        nodes: this.nodes.map(n => {
          let nodeConfig = n.nodeConfig || null
          // promptId 合并到 nodeConfig JSON
          if (n.promptId) {
            try {
              const cfg = nodeConfig ? JSON.parse(nodeConfig) : {}
              cfg.promptId = n.promptId
              nodeConfig = JSON.stringify(cfg)
            } catch (e) { nodeConfig = JSON.stringify({ promptId: n.promptId }) }
          }
          return {
            nodeCode: n.nodeCode, nodeName: n.nodeName, nodeType: n.nodeType,
            sortNo: n.sortNo, nextNodeCode: n.nextNodeCode || null,
            messageTemplate: n.messageTemplate || null,
            conditionExpr: n.conditionExpr || null,
            nodeConfig: nodeConfig,
            sceneCode: n.sceneCode || null, modelName: n.modelName || null,
            sendTime: n.sendTime || null, maxRound: n.maxRound || 0
          }
        })
      }
      saveWorkflowNodes(payload).then(res => {
        this.$message.success(res.msg || '保存成功')
      }).catch(err => {
        this.$message.error('保存失败：' + (err.message || ''))
      }).finally(() => { this.saving = false })
    },
    onNodeMouseDown(e, index) {
      this.dragging = true; this.dragIndex = index
      this.dragOffsetX = e.clientX - this.nodes[index].x
      this.dragOffsetY = e.clientY - this.nodes[index].y
    },
    onCanvasMouseMove(e) {
      if (!this.dragging || this.dragIndex < 0) return
      const rect = this.$refs.canvasArea.getBoundingClientRect()
      this.$set(this.nodes[this.dragIndex], 'x', Math.max(0, e.clientX - this.dragOffsetX - rect.left))
      this.$set(this.nodes[this.dragIndex], 'y', Math.max(0, e.clientY - this.dragOffsetY - rect.top))
    },
    onCanvasMouseUp() { this.dragging = false; this.dragIndex = -1 },
    onCanvasMouseDown() {},
    getNodeTagType(type) {
      const m = { 1: 'success', 3: 'warning', 4: 'info', 5: 'danger', 8: 'warning', 9: 'info', 12: 'info', 21: 'warning', 22: 'info' }
      return m[type] || ''
    },
    extractPromptId(nodeConfig) {
      if (!nodeConfig) return null
      try { return JSON.parse(nodeConfig).promptId || null } catch (e) { return null }
    }
  }
}
</script>

<style scoped>
.workflow-canvas-page { padding: 16px; }
.canvas-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; padding: 12px 16px; background: #fff; border-radius: 4px; box-shadow: 0 1px 4px rgba(0,0,0,0.08); }
.header-left { display: flex; align-items: center; gap: 12px; }
.header-title { font-size: 16px; font-weight: bold; }
.header-right { display: flex; align-items: center; }
.info-card { margin-bottom: 16px; }
.no-template { text-align: center; padding: 100px 0; }
.canvas-toolbar { margin-bottom: 12px; display:flex; flex-wrap:wrap; gap:4px; }
.canvas-area { position: relative; width: 100%; height: 700px; border: 1px solid #dcdfe6; border-radius: 4px; background: #fafafa; overflow: auto; }
.canvas-lines { position: absolute; top: 0; left: 0; width: 3000px; height: 3000px; pointer-events: none; }
.canvas-node { position: absolute; width: 180px; background: #fff; border: 2px solid #dcdfe6; border-radius: 8px; cursor: move; user-select: none; box-shadow: 0 2px 8px rgba(0,0,0,0.1); z-index: 10; }
.canvas-node:hover { box-shadow: 0 4px 16px rgba(0,0,0,0.15); z-index: 20; }
.canvas-node.node-type-1 { border-color: #67C23A; }
.canvas-node.node-type-2 { border-color: #409EFF; }
.canvas-node.node-type-3 { border-color: #E6A23C; }
.canvas-node.node-type-4 { border-color: #909399; }
.canvas-node.node-type-5 { border-color: #F56C6C; }
.canvas-node.node-type-8 { border-color: #E6A23C; }
.canvas-node.node-type-9 { border-color: #909399; }
.canvas-node.node-type-12 { border-color: #909399; }
.canvas-node.node-type-14 { border-color: #3b82f6; }
.canvas-node.node-type-21 { border-color: #E6A23C; }
.node-title { display: flex; justify-content: space-between; align-items: center; padding: 8px 12px; border-bottom: 1px solid #ebeef5; font-size: 13px; font-weight: bold; }
.node-body { padding: 8px 12px; }
.node-msg { font-size: 12px; color: #606266; white-space: pre-wrap; }
.node-actions { padding: 4px 8px; text-align: right; border-top: 1px solid #ebeef5; }
.list-container { margin-top: 16px; }
.timeline-node-card { margin-bottom: 4px; }
</style>
