import type { Edge, Node } from '@vue-flow/core'
import type {
  FilterGroup,
  ParamBinding,
  ScoreRule,
  TaskTemplateDsl
} from '../types'
import { WCS_NODE_MAP, type WcsNodeType } from './nodeCatalog'

export type WcsFlowNodeData = {
  type: WcsNodeType
  label: string
  summary: string
  /** 绑定在 DSL 中的定位 */
  ref?: {
    kind: 'meta' | 'trigger' | 'binding' | 'filter' | 'score' | 'allocate' | 'action' | 'policy' | 'end'
    side?: 'source' | 'target'
    id?: string
    index?: number
  }
}

export type WcsFlowNode = Node<WcsFlowNodeData>

const COL = { main: 280, source: 80, target: 480 }
const ROW_H = 90

function summaryTrigger(tpl: TaskTemplateDsl): string {
  return `${tpl.trigger.type} · ${tpl.trigger.source}`
}

function summaryBinding(b: ParamBinding): string {
  return `${b.as} ← ${b.from}`
}

function summaryFilter(g: FilterGroup): string {
  const n = g.expr.type === 'group' ? g.expr.children.length : 1
  return `${g.severity === 'hard' ? '硬性' : '弹性'} · ${n} 条条件`
}

function summaryScore(s: ScoreRule): string {
  return `${s.function} · w=${s.weight}`
}

/** 将 DSL 展开为纵向流水线节点（源/宿分两列展示策略节点） */
export function dslToFlow(tpl: TaskTemplateDsl): { nodes: WcsFlowNode[]; edges: Edge[] } {
  const nodes: WcsFlowNode[] = []
  const edges: Edge[] = []
  let y = 40

  const add = (
    id: string,
    type: WcsNodeType,
    label: string,
    summary: string,
    position: { x: number; y: number },
    ref?: WcsFlowNodeData['ref']
  ) => {
    nodes.push({
      id,
      type: 'wcs',
      position,
      data: { type, label, summary, ref },
      draggable: true
    })
  }

  add('n-trigger', 'trigger', '触发', summaryTrigger(tpl), { x: COL.main, y }, { kind: 'trigger' })
  y += ROW_H

  const bindIds: string[] = []
  tpl.bindings.forEach((b, i) => {
    const id = `n-bind-${i}`
    bindIds.push(id)
    add(id, 'binding', '参数绑定', summaryBinding(b), { x: COL.main, y }, { kind: 'binding', index: i, id: b.as })
    y += ROW_H * 0.75
  })
  if (!tpl.bindings.length) {
    add('n-bind-empty', 'binding', '参数绑定', '点击右侧添加绑定', { x: COL.main, y }, { kind: 'binding', index: -1 })
    bindIds.push('n-bind-empty')
    y += ROW_H
  }

  const srcStartY = y
  let sy = srcStartY
  let ty = srcStartY
  const srcFilterIds: string[] = []
  const tgtFilterIds: string[] = []

  tpl.locationStrategies.source.filterGroups.forEach((g) => {
    const id = `n-filter-source-${g.id}`
    srcFilterIds.push(id)
    add(id, 'filter', `源过滤 · ${g.id}`, summaryFilter(g), { x: COL.source, y: sy }, {
      kind: 'filter',
      side: 'source',
      id: g.id
    })
    sy += ROW_H
  })
  tpl.locationStrategies.target.filterGroups.forEach((g) => {
    const id = `n-filter-target-${g.id}`
    tgtFilterIds.push(id)
    add(id, 'filter', `宿过滤 · ${g.id}`, summaryFilter(g), { x: COL.target, y: ty }, {
      kind: 'filter',
      side: 'target',
      id: g.id
    })
    ty += ROW_H
  })

  const srcScoreIds: string[] = []
  const tgtScoreIds: string[] = []
  tpl.locationStrategies.source.scores.forEach((s) => {
    const id = `n-score-source-${s.id}`
    srcScoreIds.push(id)
    add(id, 'score', `源打分 · ${s.id}`, summaryScore(s), { x: COL.source, y: sy }, {
      kind: 'score',
      side: 'source',
      id: s.id
    })
    sy += ROW_H
  })
  tpl.locationStrategies.target.scores.forEach((s) => {
    const id = `n-score-target-${s.id}`
    tgtScoreIds.push(id)
    add(id, 'score', `宿打分 · ${s.id}`, summaryScore(s), { x: COL.target, y: ty }, {
      kind: 'score',
      side: 'target',
      id: s.id
    })
    ty += ROW_H
  })

  y = Math.max(sy, ty) + 20
  add(
    'n-allocate',
    'allocate',
    '成对预占',
    `topN=${tpl.locationStrategies.source.allocate.topN ?? 5} · 失败全体回滚`,
    { x: COL.main, y },
    { kind: 'allocate' }
  )
  y += ROW_H
  add(
    'n-action',
    'action',
    '任务动作',
    `${tpl.blueprint.taskType} · 自动派车=${tpl.blueprint.options.autoDispatch ? '是' : '否'}`,
    { x: COL.main, y },
    { kind: 'action' }
  )
  y += ROW_H
  add(
    'n-policy',
    'policy',
    '运行策略',
    `预占${tpl.policy.reservationTtlSec}s · 重试${tpl.policy.allocateMaxAttempts}`,
    { x: COL.main, y },
    { kind: 'policy' }
  )
  y += ROW_H
  add('n-end', 'end', '结束', '流水线完成', { x: COL.main, y }, { kind: 'end' })

  const link = (a: string, b: string) => {
    edges.push({ id: `e-${a}-${b}`, source: a, target: b, type: 'smoothstep' })
  }

  // 主链：trigger → binds → allocate → action → policy → end
  let prev = 'n-trigger'
  for (const id of bindIds) {
    link(prev, id)
    prev = id
  }
  const lastBind = prev

  // 从最后绑定分叉到源/宿过滤
  const firstSrc = srcFilterIds[0] ?? srcScoreIds[0]
  const firstTgt = tgtFilterIds[0] ?? tgtScoreIds[0]
  if (firstSrc) link(lastBind, firstSrc)
  if (firstTgt) link(lastBind, firstTgt)

  const chain = (ids: string[]) => {
    for (let i = 0; i < ids.length - 1; i++) link(ids[i], ids[i + 1])
  }
  chain(srcFilterIds)
  chain(tgtFilterIds)
  if (srcFilterIds.length && srcScoreIds.length) link(srcFilterIds[srcFilterIds.length - 1], srcScoreIds[0])
  if (tgtFilterIds.length && tgtScoreIds.length) link(tgtFilterIds[tgtFilterIds.length - 1], tgtScoreIds[0])
  chain(srcScoreIds)
  chain(tgtScoreIds)

  const lastSrc = srcScoreIds[srcScoreIds.length - 1] ?? srcFilterIds[srcFilterIds.length - 1]
  const lastTgt = tgtScoreIds[tgtScoreIds.length - 1] ?? tgtFilterIds[tgtFilterIds.length - 1]
  if (lastSrc) link(lastSrc, 'n-allocate')
  if (lastTgt) link(lastTgt, 'n-allocate')
  if (!lastSrc && !lastTgt) link(lastBind, 'n-allocate')

  link('n-allocate', 'n-action')
  link('n-action', 'n-policy')
  link('n-policy', 'n-end')

  return { nodes, edges }
}

export function defaultNodeLabel(type: WcsNodeType): string {
  return WCS_NODE_MAP[type]?.label ?? type
}

/** 拖入新节点时，往 DSL 追加默认片段并返回新节点 id */
export function appendDroppedNode(
  tpl: TaskTemplateDsl,
  type: WcsNodeType,
  side: 'source' | 'target' = 'source'
): { tpl: TaskTemplateDsl; focusNodeId: string } {
  const copy = JSON.parse(JSON.stringify(tpl)) as TaskTemplateDsl
  const uid = () => `g${Date.now().toString(36).slice(-5)}`

  switch (type) {
    case 'binding': {
      const as = `field_${copy.bindings.length + 1}`
      copy.bindings.push({
        as,
        from: 'mes.call.materialCode',
        required: false,
        resolve: 'eventPayload'
      })
      return { tpl: copy, focusNodeId: `n-bind-${copy.bindings.length - 1}` }
    }
    case 'filter': {
      const id = uid()
      const st = copy.locationStrategies[side]
      st.filterGroups.push({
        id,
        severity: 'soft',
        expr: {
          type: 'group',
          op: 'and',
          children: [
            {
              type: 'compare',
              left: { ref: 'wcs.disabled' },
              op: 'eq',
              right: { const: false }
            }
          ]
        }
      })
      // 确保降级链包含新 soft 组的首档
      if (!st.degradeChain.length) {
        st.degradeChain = [{ attempt: 0, requireGroups: st.filterGroups.map((g) => g.id) }]
      } else {
        st.degradeChain[0].requireGroups = Array.from(
          new Set([...st.degradeChain[0].requireGroups, id])
        )
      }
      return { tpl: copy, focusNodeId: `n-filter-${side}-${id}` }
    }
    case 'score': {
      const id = uid()
      copy.locationStrategies[side].scores.push({
        id,
        function: 'constant',
        weight: 1,
        params: { value: 50 }
      })
      return { tpl: copy, focusNodeId: `n-score-${side}-${id}` }
    }
    case 'trigger':
    case 'allocate':
    case 'action':
    case 'policy':
    case 'end':
    default:
      // 单例节点：不重复添加，仅聚焦
      return {
        tpl: copy,
        focusNodeId:
          type === 'trigger'
            ? 'n-trigger'
            : type === 'allocate'
              ? 'n-allocate'
              : type === 'action'
                ? 'n-action'
                : type === 'policy'
                  ? 'n-policy'
                  : 'n-end'
      }
  }
}

export function refreshNodeSummaries(tpl: TaskTemplateDsl, nodes: WcsFlowNode[]): WcsFlowNode[] {
  return nodes.map((n) => {
    const data = n.data
    if (!data?.ref) return n
    const ref = data.ref
    let summary = data.summary
    let label = data.label
    if (ref.kind === 'trigger') summary = summaryTrigger(tpl)
    if (ref.kind === 'binding' && ref.index != null && ref.index >= 0 && tpl.bindings[ref.index]) {
      summary = summaryBinding(tpl.bindings[ref.index])
    }
    if (ref.kind === 'filter' && ref.side && ref.id) {
      const g = tpl.locationStrategies[ref.side].filterGroups.find((x) => x.id === ref.id)
      if (g) {
        label = `${ref.side === 'source' ? '源' : '宿'}过滤 · ${g.id}`
        summary = summaryFilter(g)
      }
    }
    if (ref.kind === 'score' && ref.side && ref.id) {
      const s = tpl.locationStrategies[ref.side].scores.find((x) => x.id === ref.id)
      if (s) {
        label = `${ref.side === 'source' ? '源' : '宿'}打分 · ${s.id}`
        summary = summaryScore(s)
      }
    }
    if (ref.kind === 'allocate') {
      summary = `topN=${tpl.locationStrategies.source.allocate.topN ?? 5} · 失败全体回滚`
    }
    if (ref.kind === 'action') {
      summary = `${tpl.blueprint.taskType} · 自动派车=${tpl.blueprint.options.autoDispatch ? '是' : '否'}`
    }
    if (ref.kind === 'policy') {
      summary = `预占${tpl.policy.reservationTtlSec}s · 重试${tpl.policy.allocateMaxAttempts}`
    }
    const next: WcsFlowNode = {
      ...n,
      data: { ...data, label, summary }
    }
    return next
  })
}
