<template>
  <div v-if="dag" class="wf-dag">
    <h4 class="wf-dag-title">{{ $t('taskDetail.workflowDagTitle') || '工作流结构' }}</h4>

    <!-- 拓扑分层渲染 -->
    <div class="wf-dag-layers">
      <div v-for="(layer, li) in layers" :key="li" class="wf-dag-layer">
        <div class="wf-dag-layer-label">{{ $t('taskDetail.workflowLayer') || '层' }} {{ li + 1 }}</div>
        <div class="wf-dag-layer-nodes">
          <div
            v-for="node in layer"
            :key="node.id"
            class="wf-dag-node"
            :class="nodeClass(node.id, props.nodeStatuses)"
          >
            <span class="wf-dag-node-status-dot"></span>
            <span class="wf-dag-node-name">{{ node.id }}</span>
            <span class="wf-dag-node-type">{{ nodeTypeLabel[node.node_type] || node.node_type }}</span>
            <span v-if="nodeStatuses[node.id]" class="wf-dag-node-status">{{ statusLabel(nodeStatuses[node.id]) }}</span>
          </div>
        </div>
        <!-- 出边条件标签（同层节点出站边展示条件） -->
        <div v-if="layerOutEdges[li]" class="wf-dag-outedges">
          <div v-for="edge in layerOutEdges[li]" :key="edge.source + '→' + edge.target" class="wf-dag-outedge">
            <span class="wf-dag-outedge-arrow">→ {{ edge.target }}</span>
            <span class="wf-dag-cond-label" :class="{ 'wf-dag-cond-label--uncond': !edge.condition }">{{ edge.condition || ($t('taskDetail.workflowUnconditional') || '无条件') }}</span>
          </div>
        </div>
        <!-- 层间箭头 -->
        <div v-if="li < layers.length - 1" class="wf-dag-layer-arrow">↓</div>
      </div>
    </div>

    <!-- 边条件编辑列表 -->
    <details class="wf-dag-edges-detail">
      <summary class="wf-dag-edges-summary">{{ $t('taskDetail.workflowEdgeDetail') || '边条件编辑' }} ({{ dag.edges.length }})</summary>
      <div class="wf-dag-edges">
        <div v-for="(edge, i) in dag.edges" :key="i" class="wf-dag-edge">
          <span class="wf-dag-edge-arrow">{{ edge.source }} → {{ edge.target }}</span>
          <input
            v-if="editingIndex === i"
            ref="editInputRef"
            class="wf-dag-edge-input"
            :value="editValue"
            @input="editValue = ($event.target as HTMLInputElement).value"
            @keydown.enter="saveCondition(i)"
            @keydown.escape="cancelEdit"
            @blur="saveCondition(i)"
            :placeholder="$t('taskDetail.workflowCondPlaceholder')"
          />
          <span
            v-else
            class="wf-dag-cond-label"
            :class="{ 'wf-dag-cond-label--uncond': !edge.condition, 'wf-dag-cond-label--editable': true }"
            @click="startEdit(i, edge)"
          >{{ edge.condition || $t('taskDetail.workflowUnconditional') }}</span>
        </div>
      </div>
    </details>
  </div>
  <div v-else class="wf-dag wf-dag--empty">
    {{ $t('taskDetail.workflowDagEmpty') || '暂无工作流数据' }}
  </div>
</template>

<script setup lang="ts">
import { ref, computed, nextTick } from 'vue'

interface DagNode {
  id: string
  node_type: string
  config?: Record<string, unknown>
}
interface EdgeDef {
  source: string
  target: string
  condition?: string | null
}
export type NodeStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped'

const props = withDefaults(defineProps<{
  dagJson: string
  nodeStatuses?: Record<string, NodeStatus>
}>(), {
  nodeStatuses: () => ({}),
})

const emit = defineEmits<{
  (e: 'update:dagJson', val: string): void
}>()

const dag = computed<{ nodes: DagNode[]; edges: EdgeDef[] } | null>(() => {
  try { return JSON.parse(props.dagJson) }
  catch { return null }
})

const nodeTypeLabel: Record<string, string> = {
  'AiNode': 'AI',
  'HumanNode': '人工审批',
  'ScriptNode': '脚本',
  'AiSelfReviewNode': 'AI 自审',
}

function statusLabel(s: NodeStatus): string {
  return { pending: '⏳', running: '🔄', completed: '✅', failed: '❌', skipped: '⏭️' }[s] || ''
}

function nodeClass(id: string, statuses: Record<string, NodeStatus>): Record<string, boolean> {
  const s = statuses[id]
  return {
    'wf-dag-node--running': s === 'running',
    'wf-dag-node--completed': s === 'completed',
    'wf-dag-node--failed': s === 'failed',
    'wf-dag-node--skipped': s === 'skipped',
  }
}

/** 拓扑分层 */
const layers = computed<(DagNode & { deps?: string[] })[][]>(() => {
  if (!dag.value) return []
  const d = dag.value
  const inDegree: Record<string, number> = {}
  const edgeMap: Record<string, string[]> = {}
  for (const n of d.nodes) {
    inDegree[n.id] = 0
    edgeMap[n.id] = []
  }
  for (const e of d.edges) {
    if (!edgeMap[e.source]) edgeMap[e.source] = []
    edgeMap[e.source].push(e.target)
    inDegree[e.target] = (inDegree[e.target] || 0) + 1
  }
  const result: (DagNode & { deps?: string[] })[][] = []
  let queue = Object.entries(inDegree).filter(([, d]) => d === 0).map(([id]) => id)
  const visited = new Set<string>()
  while (queue.length > 0) {
    const layer: DagNode[] = []
    const next: string[] = []
    for (const id of queue) {
      if (visited.has(id)) continue
      visited.add(id)
      const node = d.nodes.find(n => n.id === id)
      if (node) layer.push(node)
      for (const target of (edgeMap[id] || [])) {
        inDegree[target]--
        if (inDegree[target] === 0) next.push(target)
      }
    }
    if (layer.length > 0) result.push(layer)
    queue = next
  }
  return result
})

/**
 * 每层出边索引（缓存版）：layers 变化时重新计算，避免模板内重复调用函数。
 * 返回 { [layerIndex]: EdgeDef[] }，仅含出边非空的层。
 */
const layerOutEdges = computed<Record<number, EdgeDef[]>>(() => {
  if (!dag.value) return {}
  const bySource: Record<string, EdgeDef[]> = {}
  for (const e of dag.value.edges) {
    if (!bySource[e.source]) bySource[e.source] = []
    bySource[e.source].push(e)
  }
  const result: Record<number, EdgeDef[]> = {}
  for (let li = 0; li < layers.value.length; li++) {
    const edges: EdgeDef[] = []
    for (const node of layers.value[li]) {
      const out = bySource[node.id]
      if (out) edges.push(...out)
    }
    if (edges.length > 0) result[li] = edges
  }
  return result
})

// ── 条件编辑 ──
const editingIndex = ref<number | null>(null)
const editValue = ref('')
const editInputRef = ref<HTMLInputElement | null>(null)

function startEdit(i: number, edge: EdgeDef) {
  editingIndex.value = i
  editValue.value = edge.condition || ''
  nextTick(() => editInputRef.value?.focus())
}

function saveCondition(i: number) {
  if (!dag.value || editingIndex.value !== i) return
  const edges = [...dag.value.edges]
  const val = editValue.value.trim()
  edges[i] = { ...edges[i], condition: val || null }
  const updated = { ...dag.value, edges }
  emit('update:dagJson', JSON.stringify(updated, null, 2))
  editingIndex.value = null
}

function cancelEdit() {
  editingIndex.value = null
}
</script>

<style scoped>
.wf-dag {
  margin-top: 8px; padding: 8px 12px;
  background: var(--df-bg-card, #fafafa);
  border-radius: var(--df-radius-sm, 4px); font-size: 12px;
}
.wf-dag--empty { color: var(--df-text-dim, #888); }
.wf-dag-title { margin: 0 0 8px; font-size: 13px; font-weight: 600; }

.wf-dag-layers { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.wf-dag-layer { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.wf-dag-layer-label { font-size: 10px; color: var(--df-text-dim, #888); align-self: flex-start; }
.wf-dag-layer-nodes {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 6px;
}
.wf-dag-layer-arrow { font-size: 16px; color: var(--df-text-dim, #888); line-height: 1; }

.wf-dag-node {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 10px;
  background: var(--df-bg, #fff);
  border: 1px solid var(--df-border, #ddd);
  border-radius: var(--df-radius-sm, 4px);
  transition: border-color 0.3s, background 0.3s;
}
.wf-dag-node-name { font-weight: 500; white-space: nowrap; }
.wf-dag-node-type {
  font-size: 10px; color: var(--df-text-dim, #888);
  background: var(--df-bg-card, #f0f0f0); padding: 0 4px; border-radius: 3px;
}
.wf-dag-node-status { font-size: 11px; }
.wf-dag-node-status-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--df-text-dim, #ccc);
}
.wf-dag-node--running {
  border-color: var(--df-info, #4a90e2);
  background: rgba(74,144,226,0.05);
}
.wf-dag-node--running .wf-dag-node-status-dot {
  background: var(--df-info, #4a90e2);
  animation: pulse 1s ease-in-out infinite;
}
.wf-dag-node--completed { border-color: var(--df-success, #3ddba0); }
.wf-dag-node--completed .wf-dag-node-status-dot { background: var(--df-success, #3ddba0); }
.wf-dag-node--failed { border-color: var(--df-danger, #e5484d); background: rgba(229,72,77,0.05); }
.wf-dag-node--failed .wf-dag-node-status-dot { background: var(--df-danger, #e5484d); }
.wf-dag-node--skipped { opacity: 0.5; }

@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }

/* 出边条件标签 */
.wf-dag-outedges {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 4px 12px;
  padding: 2px 0;
}
.wf-dag-outedge {
  display: inline-flex; align-items: center; gap: 4px; font-size: 10px;
}
.wf-dag-outedge-arrow { color: var(--df-text-primary); white-space: nowrap; }

/* 条件标签（共享：出边展示 + 编辑列表） */
.wf-dag-cond-label {
  font-family: var(--df-font-mono); font-size: 10px; padding: 0 4px;
  background: rgba(74,144,226,0.08); border-radius: 2px; color: var(--df-info, #4a90e2);
  max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wf-dag-cond-label--uncond { background: transparent; color: var(--df-text-dim, #aaa); }
.wf-dag-cond-label--editable {
  cursor: pointer; font-size: 11px; padding: 1px 6px; border-radius: 3px;
  transition: background 0.15s;
}
.wf-dag-cond-label--editable:hover { background: rgba(74,144,226,0.18); }
.wf-dag-cond-label--uncond.wf-dag-cond-label--editable:hover { background: var(--df-bg-card); }

/* 边条件编辑列表 */
.wf-dag-edges-detail { margin-top: 8px; }
.wf-dag-edges-summary { cursor: pointer; font-size: 11px; color: var(--df-info, #4a90e2); }
.wf-dag-edges { display: flex; flex-direction: column; gap: 3px; margin-top: 4px; }
.wf-dag-edge { display: flex; align-items: center; gap: 6px; padding: 2px 0; font-size: 11px; }
.wf-dag-edge-arrow { color: var(--df-text-primary); white-space: nowrap; }
.wf-dag-edge-input {
  flex: 1; min-width: 0; font-family: var(--df-font-mono); font-size: 11px;
  padding: 2px 6px; border: 0.5px solid var(--df-border); border-radius: 3px;
  background: var(--df-bg); color: var(--df-text); outline: none;
}
.wf-dag-edge-input:focus { border-color: var(--df-accent); }
</style>
