<template>
  <div class="flow-editor-page">
    <el-page-header @back="$router.back()" :content="isEdit ? '编辑: ' + flowName : '创建新流'" />

    <el-alert v-if="initError" type="error" :title="initError" show-icon closable @close="initError = ''" style="margin-top: 12px" />

    <el-card style="margin-top: 20px">
      <div class="editor-toolbar">
        <el-input v-model="flowName" placeholder="流名称" style="width: 200px" />
        <el-input v-model="flowDesc" placeholder="描述" style="width: 300px; margin-left: 12px" />
        <el-select v-model="flowMode" style="width: 140px; margin-left: 12px" :disabled="isEdit">
          <el-option label="对话型 (Chatflow)" value="chatflow" />
          <el-option label="工作流型 (Workflow)" value="workflow" />
        </el-select>
        <el-button type="primary" @click="saveFlow" :loading="saving">保存</el-button>
        <el-button @click="testFlow">验证</el-button>
        <el-button v-if="isEdit" type="success" @click="publishFlow">上架到企微</el-button>
        <el-button v-if="isEdit" type="primary" @click="publishToWeb">上架到网页</el-button>
        <el-button v-if="isEdit" @click="showVersionHistory">版本历史</el-button>
      </div>
    </el-card>

    <div v-if="!initError" class="editor-body">
      <div class="node-panel">
        <div class="panel-title">节点面板</div>
        <div
          v-for="node in nodeTypes"
          :key="node.type"
          class="node-item"
          draggable="true"
          @dragstart="onDragStart($event, node)"
        >
          <el-icon :size="18"><component :is="node.icon" /></el-icon>
          <span>{{ node.label }}</span>
        </div>
        <el-divider />
        <div class="panel-title">操作提示</div>
        <div class="panel-hint">
          <p>拖拽节点到画布</p>
          <p>从绿色圆点拖线(true)</p>
          <p>从红色圆点拖线(false)</p>
          <p>循环: 青色(循环体)/灰色(完成)</p>
          <p>选中连线/节点按 Delete 删除</p>
          <p>右键点击连线可删除</p>
          <p>点击空白处取消选中</p>
          <p>滚轮缩放画布</p>
        </div>
      </div>

      <div class="canvas-wrapper">
        <div class="canvas-header">
          <span>设计画布</span>
          <el-button size="small" @click="clearCanvas">清空</el-button>
        </div>
        <FlowCanvas
          ref="canvasRef"
          :nodes="nodes"
          :edges="edges"
          :node-color="getMiniMapColor"
          @node-click="onNodeClick"
          @pane-click="onPaneClick"
          @drop="onDrop"
          @node-delete="removeNode"
          @update:nodes="(v: any) => nodes = v"
          @update:edges="(v: any) => edges = v"
        />
      </div>

      <div class="config-panel" v-if="selectedNodeId">
        <div class="panel-title">节点配置</div>
        <div class="config-actions">
          <el-button size="small" type="danger" @click="removeNode(selectedNodeId)">删除节点</el-button>
        </div>
        <el-form label-width="100px" size="small" style="margin-top:12px">
          <el-form-item label="类型">
            <el-tag>{{ selectedNode?.data?.typeDesc || selectedNode?.type }}</el-tag>
          </el-form-item>
          <el-form-item label="名称">
            <el-input v-model="selectedNodeData.label" @change="onConfigLabelChange" />
          </el-form-item>

          <component 
            v-if="selectedNode?.data?.type"
            :is="getConfigComponent(selectedNode.data.type)"
            v-model="selectedNodeData.config"
            :model-list="modelList"
            :mcp-servers="mcpServers"
            @change="onConfigChange"
          />
        </el-form>
      </div>
    </div>

    <el-dialog v-model="versionDialogVisible" title="版本历史" width="700px">
      <el-table :data="versions" v-loading="loadingVersions" max-height="400">
        <el-table-column prop="version" label="版本" width="80" />
        <el-table-column prop="changelog" label="变更说明" min-width="200">
          <template #default="{ row }">
            <span v-if="row.changelog">{{ row.changelog }}</span>
            <span v-else style="color:#999">-</span>
          </template>
        </el-table-column>
        <el-table-column label="时间" width="170">
          <template #default="{ row }">
            {{ formatTime(row.created_at) }}
          </template>
        </el-table-column>
        <el-table-column label="操作" width="120">
          <template #default="{ row }">
            <el-button size="small" type="primary" link @click="rollbackVersion(row.id)">
              回滚到此版本
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <div v-if="!versions.length && !loadingVersions" style="text-align:center;padding:24px;color:#999">
        暂无版本记录
      </div>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { MarkerType } from '@vue-flow/core'
import { Promotion, ChatDotRound, Tools, Connection, Bell, DataAnalysis, Search, RefreshRight, Document, Link, Operation, Edit, Sunny } from '@element-plus/icons-vue'
import FlowCanvas from './FlowCanvas.vue'
import TriggerConfig from './node-configs/TriggerConfig.vue'
import LlmConfig from './node-configs/LlmConfig.vue'
import ToolConfig from './node-configs/ToolConfig.vue'
import McpConfig from './node-configs/McpConfig.vue'
import NotifyConfig from './node-configs/WecomNotifyConfig.vue'
import ConditionConfig from './node-configs/ConditionConfig.vue'
import RagConfig from './node-configs/RagConfig.vue'
import OutputConfig from './node-configs/OutputConfig.vue'
import LoopConfig from './node-configs/LoopConfig.vue'
import CodeConfig from './node-configs/CodeConfig.vue'
import HttpRequestConfig from './node-configs/HttpRequestConfig.vue'
import QuestionClassifierConfig from './node-configs/QuestionClassifierConfig.vue'
import VariableAssignerConfig from './node-configs/VariableAssignerConfig.vue'
import TemplateTransformConfig from './node-configs/TemplateTransformConfig.vue'
import IterationConfig from './node-configs/IterationConfig.vue'
import QuestionOptimiserConfig from './node-configs/QuestionOptimiserConfig.vue'
import MergeConfig from './node-configs/MergeConfig.vue'

const route = useRoute()
const router = useRouter()
const flowId = computed(() => route.params.id as string)
const isEdit = computed(() => !!flowId.value)

const initError = ref('')

const flowName = ref('新工作流')
const flowDesc = ref('')
const flowStatus = ref('')
const flowMode = ref('chatflow')
const saving = ref(false)
const versionDialogVisible = ref(false)
const versions = ref<any[]>([])
const loadingVersions = ref(false)
const selectedNodeId = ref('')
const selectedNodeData = ref<any>({})
const mcpServers = ref<any[]>([])
const modelList = ref<any[]>([])

const nodes = ref<any[]>([])
const edges = ref<any[]>([])
const canvasRef = ref<any>(null)

let nodeCounter = 0

const nodeTypes = [
  { type: 'trigger', label: '触发节点', icon: Promotion, typeDesc: '流程触发' },
  { type: 'llm', label: 'LLM处理', icon: ChatDotRound, typeDesc: 'AI处理' },
  { type: 'tool', label: '工具调用', icon: Tools, typeDesc: '工具调用' },
  { type: 'mcp', label: 'MCP服务', icon: Connection, typeDesc: '外部MCP' },
  { type: 'notify', label: '通知', icon: Bell, typeDesc: '消息通知' },
  { type: 'condition', label: '条件判断', icon: DataAnalysis, typeDesc: '条件分支' },
  { type: 'rag', label: 'RAG检索', icon: Search, typeDesc: '知识库检索' },
  { type: 'loop', label: '循环', icon: RefreshRight, typeDesc: '循环迭代' },
  { type: 'merge', label: '变量聚合', icon: Connection, typeDesc: '并行汇聚' },
  { type: 'code', label: '代码执行', icon: Document, typeDesc: '代码执行' },
  { type: 'output', label: '输出节点', icon: Promotion, typeDesc: '结果输出' },
  { type: 'http_request', label: 'HTTP请求', icon: Link, typeDesc: '外部API调用' },
  { type: 'question_classifier', label: '问题分类', icon: Operation, typeDesc: '意图分类路由' },
  { type: 'variable_assigner', label: '变量赋值', icon: Edit, typeDesc: '变量操作' },
  { type: 'template_transform', label: '模板转换', icon: Document, typeDesc: '格式转换' },
  { type: 'iteration', label: '迭代处理', icon: RefreshRight, typeDesc: '数组遍历' },
  { type: 'question_optimiser', label: '问题优化', icon: Sunny, typeDesc: '查询优化' },
]

const configComponentMap: Record<string, any> = {
  trigger: TriggerConfig,
  llm: LlmConfig,
  tool: ToolConfig,
  mcp: McpConfig,
  notify: NotifyConfig,
  wecom_notify: NotifyConfig,
  condition: ConditionConfig,
  rag: RagConfig,
  output: OutputConfig,
  loop: LoopConfig,
  code: CodeConfig,
  merge: MergeConfig,
  http_request: HttpRequestConfig,
  question_classifier: QuestionClassifierConfig,
  variable_assigner: VariableAssignerConfig,
  template_transform: TemplateTransformConfig,
  iteration: IterationConfig,
  question_optimiser: QuestionOptimiserConfig,
}

function getConfigComponent(type: string) {
  return configComponentMap[type] || null
}

const colorMap: Record<string, string> = {
  trigger: '#722ed1',
  llm: '#409EFF',
  tool: '#67C23A',
  mcp: '#E6A23C',
  notify: '#F56C6C',
  wecom_notify: '#F56C6C',
  condition: '#909399',
  rag: '#337ecc',
  loop: '#13c2c2',
  code: '#eb2f96',
  output: '#722ed1',
  http_request: '#2d8cf0',
  question_classifier: '#ff9900',
  variable_assigner: '#19be6b',
  template_transform: '#9c27b0',
  iteration: '#ff5722',
  question_optimiser: '#e6a23c',
}

const selectedNode = computed(() => nodes.value.find((n: any) => n.id === selectedNodeId.value) || null)

function getMiniMapColor(node: any) {
  return colorMap[node?.data?.type || node?.type] || '#409EFF'
}

function onDragStart(event: DragEvent, node: any) {
  if (event.dataTransfer) {
    event.dataTransfer.setData('application/vueflow', JSON.stringify(node))
    event.dataTransfer.effectAllowed = 'move'
  }
}

function onDrop(event: DragEvent) {
  const dataStr = event.dataTransfer?.getData('application/vueflow')
  if (!dataStr) return
  const nodeData = JSON.parse(dataStr)
  const id = `node_${++nodeCounter}`
  nodes.value.push({
    id,
    type: 'custom',
    position: { x: 100 + nodeCounter * 50, y: 100 + nodeCounter * 30 },
    data: {
      label: nodeData.label,
      type: nodeData.type,
      typeDesc: nodeData.typeDesc,
      color: colorMap[nodeData.type] || '#409EFF',
      config: getDefaultConfig(nodeData.type),
    },
    draggable: true,
    connectable: true,
  })
}

function getDefaultConfig(type: string) {
  const defaults: Record<string, any> = {
    trigger: { event_type: 'text_message', channels: ['wecom'], callback_url: '' },
    llm: { system_prompt: '', model: 'gpt-4o-mini', temperature: 0.7, agent_id: '', max_tokens: 2000, context_length: 5, memory_mode: 'short_term', stream: true, tool_call: false },
    tool: { tool_type: '', tool_name: '', param_mapping: '{}', tool_params: {}, timeout: 30, retry_count: 0, error_handling: 'throw' },
    mcp: { mcp_server: '', tool_name: '', input_params: {}, timeout: 30, response_parser: 'json', error_handling: 'throw' },
    notify: { channels: { wecom: true, web: false }, message_template: '', web_template: '', target: '', message_type: 'text', async_send: false, error_handling: 'throw' },
    wecom_notify: { channels: { wecom: true, web: false }, message_template: '', web_template: '', target: '', message_type: 'text', async_send: false, error_handling: 'throw' },
    condition: { condition_type: 'expression', condition: '', true_label: '是', false_label: '否', default_branch: 'false' },
    rag: { knowledge_base: '', top_k: 5, search_mode: 'hybrid', similarity_threshold: 0.7, result_sort: 'similarity', include_metadata: true },
    loop: { loop_type: 'fixed', count: 3, iterator_variable: 'item', max_iterations: 10, items: [] },
    merge: { merge_type: 'concat', expected_branches: 2 },
  code: { language: 'python', code: '', timeout: 30, sandbox: true },
  output: { format: 'text', output_template: '', indent: 2, encoding: 'utf-8', truncate: false, max_length: 2000 },
  http_request: { method: 'GET', url: '', headers: '{}', body: '', auth_type: 'none', auth_config: {}, timeout: 30, retry_count: 0 },
  question_classifier: { categories: [], instruction: '', model: '', temperature: 0.3 },
  variable_assigner: { assignments: [] },
  template_transform: { template: '', output_type: 'string' },
  iteration: { input_array_source: '', max_iterations: 20 },
  question_optimiser: { optimization_type: 'rewrite', model: '' },
  }
  return defaults[type] || {}
}

function onNodeClick({ node }: any) {
  selectedNodeId.value = node.id
  const d = node.data || {}
  selectedNodeData.value = { label: d.label || '', typeDesc: d.typeDesc || '', config: { ...(d.config || {}) } }
}

function onPaneClick() { selectedNodeId.value = '' }

function onConfigLabelChange() {
  const idx = nodes.value.findIndex((n: any) => n.id === selectedNodeId.value)
  if (idx !== -1) {
    const updated = { ...nodes.value[idx] }
    updated.data = { ...updated.data, label: selectedNodeData.value.label }
    nodes.value = nodes.value.map((n: any, i: number) => i === idx ? updated : n)
  }
}

function onConfigChange() {
  const idx = nodes.value.findIndex((n: any) => n.id === selectedNodeId.value)
  if (idx === -1) return
  const found = nodes.value[idx]
  const updated = { ...found }
  updated.data = { ...found.data, config: { ...selectedNodeData.value.config } }
  nodes.value = nodes.value.map((n: any, i: number) => i === idx ? updated : n)
}

function removeNode(id: string) {
  nodes.value = nodes.value.filter((n: any) => n.id !== id)
  edges.value = edges.value.filter((e: any) => e.source !== id && e.target !== id)
  if (selectedNodeId.value === id) selectedNodeId.value = ''
}

function clearCanvas() { nodes.value = []; edges.value = []; nodeCounter = 0; selectedNodeId.value = '' }

function getEdgeStyle(sourceHandle: string | undefined) {
  if (sourceHandle === 'false') return { stroke: '#E53935', strokeWidth: 3 }
  if (sourceHandle === 'loop_body') return { stroke: '#13c2c2', strokeWidth: 3, strokeDasharray: '6,3' }
  if (sourceHandle === 'loop_done') return { stroke: '#909399', strokeWidth: 3 }
  return { stroke: '#1976D2', strokeWidth: 3 }
}

async function loadFlow() {
  if (!isEdit.value) return
  try {
    const { flowApi } = await import('@/api')
    const res: any = await flowApi.getFlow(flowId.value)
    const flow = res?.data || res || {}
    flowName.value = flow.name || ''
    flowDesc.value = flow.description || ''
    flowStatus.value = flow.status || ''
    flowMode.value = flow.flow_mode || 'chatflow'
    const definition = flow.definition_json || {}
    const loadedNodes: any[] = []
    const loadedEdges: any[] = []
    const defNodes = definition.nodes || []
    for (let i = 0; i < defNodes.length; i++) {
      const n = defNodes[i]
      const nt = nodeTypes.find(t => t.type === n.type)
      loadedNodes.push({
        id: n.id || `node_${i}`, type: 'custom',
        position: n.position || { x: 100 + (i % 4) * 250, y: 60 + Math.floor(i / 4) * 150 },
        data: { label: n.label || n.id || `节点${i}`, type: n.type, typeDesc: nt?.typeDesc || n.type, color: colorMap[n.type] || '#409EFF', config: n.config || {} },
        draggable: true, connectable: true,
      })
    }
    const defEdges = definition.edges || []
    for (const e of defEdges) {
      const source = e.source || e.from
      const target = e.target || e.to
      const cond = e.condition || e.sourceHandle
      loadedEdges.push({ id: e.id || `edge_${source}_${target}`, source, target, sourceHandle: cond || 'source', type: 'smoothstep', animated: cond === 'loop_body', markerEnd: MarkerType.ArrowClosed, style: getEdgeStyle(cond) })
    }
    nodes.value = loadedNodes
    edges.value = loadedEdges
    nodeCounter = defNodes.length
  } catch (e: any) {
    console.error('loadFlow error:', e)
  }
}

async function loadMcpServers() {
  try { const { mcpApi } = await import('@/api'); const res: any = await mcpApi.getServers(); mcpServers.value = Array.isArray(res) ? res : (res?.data || []) } catch {}
}

async function loadModels() {
  try { const { modelProviderApi } = await import('@/api'); const res: any = await modelProviderApi.getAllModels(); modelList.value = Array.isArray(res?.data) ? res.data : (Array.isArray(res) ? res : []) } catch {}
}

async function saveFlow() {
  if (!flowName.value) { ElMessage.warning('请输入流名称'); return }
  saving.value = true
  try {
    const { flowApi } = await import('@/api')
    const snapshot = canvasRef.value?.getSnapshot() || { nodes: [], edges: [] }
    const serializedNodes = snapshot.nodes.map((n: any) => ({ id: n.id, type: n.data?.type || n.type, label: n.data?.label || n.id, config: n.data?.config || {}, position: n.position }))
    const serializedEdges = snapshot.edges.map((e: any) => ({ id: e.id, source: e.source, target: e.target, sourceHandle: e.sourceHandle || 'source' }))
    const payload = { name: flowName.value, description: flowDesc.value, nodes: serializedNodes, edges: serializedEdges, trigger: {}, flow_mode: flowMode.value }
    if (isEdit.value) { await flowApi.updateFlow(flowId.value, payload); ElMessage.success('保存成功') }
    else { const res: any = await flowApi.createFlow(payload); const data = res?.data || res || {}; if (data.id) { router.replace(`/admin/flow/editor/${data.id}`); ElMessage.success('创建成功') } }
  } finally { saving.value = false }
}

async function testFlow() {
  if (!isEdit.value) { ElMessage.info('请先保存再进行验证'); return }
  try { const { flowApi } = await import('@/api'); const res: any = await flowApi.testFlow(flowId.value); const data = res?.data || res || {}; if (data.valid) { ElMessage.success(`验证通过: ${data.node_count}个节点, ${data.edge_count}条边`) } else { ElMessage.warning(`验证问题: ${(data.issues || []).join(', ')}`) } } catch {}
}

async function publishFlow() {
  if (!isEdit.value) { ElMessage.warning('请先保存'); return }
  try { const { flowApi } = await import('@/api'); await flowApi.publishFlow(flowId.value); ElMessage.success('流已上架到企微'); await loadFlow() } catch {}
}

async function publishToWeb() {
  if (!isEdit.value) { ElMessage.warning('请先保存'); return }
  try { const { flowApi } = await import('@/api'); await flowApi.publishToWeb(flowId.value); ElMessage.success('流已上架到网页'); await loadFlow() } catch {}
}

async function showVersionHistory() {
  versionDialogVisible.value = true
  await loadVersions()
}

async function loadVersions() {
  loadingVersions.value = true
  try {
    const { flowApi } = await import('@/api')
    const res: any = await flowApi.getVersions(flowId.value)
    versions.value = Array.isArray(res?.data) ? res.data : (Array.isArray(res) ? res : [])
  } catch { versions.value = [] }
  finally { loadingVersions.value = false }
}

async function rollbackVersion(versionId: string) {
  try {
    await ElMessageBox.confirm('回滚将覆盖当前定义，确定继续？', '确认回滚', { type: 'warning' })
    const { flowApi } = await import('@/api')
    await flowApi.rollbackVersion(flowId.value, versionId)
    ElMessage.success('已回滚到该版本')
    versionDialogVisible.value = false
    await loadFlow()
  } catch {}
}

function formatTime(ts: string) {
  if (!ts) return '-'
  try { return new Date(ts).toLocaleString() } catch { return ts }
}

onMounted(async () => {
  try {
    if (isEdit.value) { await loadFlow() }
    await Promise.allSettled([loadMcpServers(), loadModels()])
    startAutoSave()
  } catch (e: any) {
    console.error('FlowEditor init error:', e)
    initError.value = '初始化失败: ' + (e?.message || '未知错误')
  }
})

let autoSaveTimer: ReturnType<typeof setInterval> | null = null

function startAutoSave() {
  if (autoSaveTimer) clearInterval(autoSaveTimer)
  autoSaveTimer = setInterval(() => {
    if (flowName.value && isEdit.value) {
      saveFlow().catch(() => {})
    }
  }, 30000)
}

onBeforeUnmount(() => {
  if (autoSaveTimer) clearInterval(autoSaveTimer)
})
</script>

<style scoped>
.flow-editor-page { height: 100%; display: flex; flex-direction: column; }
.editor-toolbar { display: flex; align-items: center; gap: 8px; }
.editor-body { display: flex; gap: 12px; margin-top: 12px; flex: 1; min-height: 0; }
.node-panel { width: 160px; background: #fff; border-radius: 4px; padding: 12px; border: 1px solid #ebeef5; overflow-y: auto; flex-shrink: 0; }
.panel-title { font-weight: bold; margin-bottom: 12px; padding-bottom: 8px; border-bottom: 1px solid #ebeef5; }
.node-item { display: flex; align-items: center; gap: 8px; padding: 8px; margin-bottom: 8px; border: 1px solid #e4e7ed; border-radius: 4px; cursor: grab; font-size: 13px; background: #f5f7fa; transition: all 0.2s; user-select: none; }
.node-item:hover { border-color: #409EFF; background: #ecf5ff; }
.panel-hint { font-size: 12px; color: #909399; line-height: 1.8; }
.panel-hint p::before { content: '• '; color: #409EFF; }
.canvas-wrapper { flex: 1; background: #fff; border-radius: 4px; border: 1px solid #ebeef5; display: flex; flex-direction: column; min-width: 0; }
.canvas-header { display: flex; justify-content: space-between; align-items: center; padding: 8px 16px; border-bottom: 1px solid #ebeef5; font-weight: bold; flex-shrink: 0; }
.config-panel { width: 280px; background: #fff; border-radius: 4px; padding: 12px; border: 1px solid #ebeef5; overflow-y: auto; flex-shrink: 0; }
.config-actions { margin-top: 8px; padding-top: 8px; border-top: 1px solid #ebeef5; }
</style>
