import { toRaw } from 'vue'
import type { ParamFieldDef, WorkflowDefinition, WorkflowEdgeDef, WorkflowNodeDef } from '@/types/workflow'
import type { WorkflowCatalogExport } from '@/workflow/workflowLibrary'
import { defaultParamsForType, NODE_CATALOG_MAP } from '@/workflow/nodeCatalog'
import { WAIT_EVENT_HANDLES, WAIT_EVENT_HANDLE_LABELS } from '@/workflow/waitEventParams'

/** 深拷贝流程定义（workflow 为纯 JSON 结构，避免 structuredClone 对 Proxy/不可克隆对象报错） */
export function cloneWorkflowDefinition(def: WorkflowDefinition): WorkflowDefinition {
  const raw = toRaw(def) as WorkflowDefinition
  return JSON.parse(JSON.stringify(raw)) as WorkflowDefinition
}

export interface ValidationIssue {
  level: 'error' | 'warning'
  message: string
}

export function validateWorkflow(def: WorkflowDefinition): ValidationIssue[] {
  const issues: ValidationIssue[] = []
  const nodes = def.nodes
  const edges = def.edges

  const starts = nodes.filter((n) => n.type === 'start')
  const ends = nodes.filter((n) => n.type === 'end')

  if (starts.length === 0) issues.push({ level: 'error', message: '流程缺少「开始」节点' })
  if (starts.length > 1) issues.push({ level: 'error', message: '流程只能有一个「开始」节点' })
  if (ends.length === 0) issues.push({ level: 'warning', message: '建议添加「结束」节点' })

  const outCount = new Map<string, number>()
  const inCount = new Map<string, number>()
  for (const e of edges) {
    outCount.set(e.source, (outCount.get(e.source) ?? 0) + 1)
    inCount.set(e.target, (inCount.get(e.target) ?? 0) + 1)
  }

  for (const n of nodes) {
    if (n.type === 'start' && (outCount.get(n.id) ?? 0) === 0) {
      issues.push({ level: 'error', message: `「${n.label}」未连接下游节点` })
    }
    if (n.type === 'end' && (inCount.get(n.id) ?? 0) === 0) {
      issues.push({ level: 'warning', message: `「${n.label}」没有上游连线` })
    }
    if (n.type !== 'start' && n.type !== 'end' && (inCount.get(n.id) ?? 0) === 0) {
      issues.push({ level: 'warning', message: `节点「${n.label}」处于孤立状态` })
    }
    if (n.type === 'waitEvent') {
      const outs = edges.filter((e) => e.source === n.id)
      const hasTriggered = outs.some(
        (e) => e.sourceHandle === WAIT_EVENT_HANDLES.triggered || !e.sourceHandle
      )
      const hasTimeout = outs.some((e) => e.sourceHandle === WAIT_EVENT_HANDLES.timeout)
      const timeoutSec = Number(n.params.timeoutSec ?? 0)
      if (!hasTriggered) {
        issues.push({ level: 'warning', message: `「${n.label}」未连接「正常执行」出口` })
      }
      if (timeoutSec > 0 && !hasTimeout) {
        issues.push({
          level: 'warning',
          message: `「${n.label}」未连接「超时执行」出口，超时后将无法继续执行`
        })
      }
    }
  }

  return issues
}

/** 控件参数 schema 变更时递增，用于丢弃过期的 localStorage 数据 */
export const WORKFLOW_CATALOG_VERSION = 3

export const WORKFLOW_EDITOR_STORAGE_KEY = 'workflowEditor.draft'

const DEPRECATED_ASSIGN_STRATEGY: Record<string, string> = {
  nearest: 'carId',
  idle: 'carType',
  fixed: 'carId',
  byType: 'carType'
}

function isFieldVisible(field: ParamFieldDef, params: Record<string, unknown>): boolean {
  if (!field.when) return true
  return params[field.when.key] === field.when.equals
}

/** 将节点 params 与当前控件库定义对齐：补默认值、迁移废弃枚举、剔除无效字段 */
export function normalizeNodeParams(type: string, params: Record<string, unknown>): Record<string, unknown> {
  const def = NODE_CATALOG_MAP.get(type)
  if (!def) return { ...params }

  const merged: Record<string, unknown> = { ...defaultParamsForType(type), ...params }

  if (type === 'assignCar' && typeof merged.strategy === 'string') {
    const migrated = DEPRECATED_ASSIGN_STRATEGY[merged.strategy]
    if (migrated) merged.strategy = migrated
  }

  const result: Record<string, unknown> = {}
  for (const field of def.params) {
    if (!isFieldVisible(field, merged)) continue
    let value = merged[field.key]
    if (field.type === 'select' && field.options?.length) {
      const valid = field.options.some((o) => o.value === value)
      if (!valid) value = field.defaultValue ?? field.options[0]!.value
    }
    if (value !== undefined) result[field.key] = value
  }
  return result
}

export function normalizeWorkflow(def: WorkflowDefinition): WorkflowDefinition {
  return {
    ...def,
    nodes: def.nodes.map((n) => ({
      ...n,
      params: normalizeNodeParams(n.type, n.params)
    }))
  }
}

interface StoredWorkflowDraft {
  catalogVersion: number
  workflow: WorkflowDefinition
}

export function loadStoredWorkflow(): WorkflowDefinition | null {
  try {
    const raw = localStorage.getItem(WORKFLOW_EDITOR_STORAGE_KEY)
    if (!raw) return null
    const parsed = JSON.parse(raw) as StoredWorkflowDraft
    if (parsed.catalogVersion !== WORKFLOW_CATALOG_VERSION) return null
    if (!parsed.workflow?.nodes) return null
    return normalizeWorkflow(parsed.workflow)
  } catch {
    return null
  }
}

export function saveStoredWorkflow(def: WorkflowDefinition): void {
  try {
    const payload: StoredWorkflowDraft = {
      catalogVersion: WORKFLOW_CATALOG_VERSION,
      workflow: def
    }
    localStorage.setItem(WORKFLOW_EDITOR_STORAGE_KEY, JSON.stringify(payload))
  } catch {
    /* localStorage 可能被禁用 */
  }
}

export function clearStoredWorkflow(): void {
  try {
    localStorage.removeItem(WORKFLOW_EDITOR_STORAGE_KEY)
  } catch {
    /* ignore */
  }
}

export function toFlowNodes(nodes: WorkflowNodeDef[]) {
  return nodes.map((n) => ({
    id: n.id,
    type: 'workflow',
    position: { ...n.position },
    data: { type: n.type, label: n.label, params: { ...n.params } }
  }))
}

export function toFlowEdges(edges: WorkflowEdgeDef[]) {
  return edges.map((e) => ({
    id: e.id,
    source: e.source,
    target: e.target,
    sourceHandle: e.sourceHandle,
    label: e.label ?? edgeHandleLabel(e.sourceHandle),
    selectable: true,
    interactionWidth: 20,
    animated: true,
    style: { stroke: edgeStroke(e.sourceHandle), strokeWidth: 2 },
    labelStyle: { fill: '#606266', fontSize: 11 },
    labelBgStyle: { fill: '#fff', fillOpacity: 0.85 }
  }))
}

function edgeHandleLabel(sourceHandle?: string): string | undefined {
  if (sourceHandle === WAIT_EVENT_HANDLES.triggered) return WAIT_EVENT_HANDLE_LABELS.triggered
  if (sourceHandle === WAIT_EVENT_HANDLES.timeout) return WAIT_EVENT_HANDLE_LABELS.timeout
  if (sourceHandle === 'success') return '成立'
  if (sourceHandle === 'fail') return '不成立'
  return undefined
}

function edgeStroke(sourceHandle?: string): string {
  if (sourceHandle === WAIT_EVENT_HANDLES.timeout || sourceHandle === 'fail') return '#f56c6c'
  if (sourceHandle === WAIT_EVENT_HANDLES.triggered || sourceHandle === 'success') return '#67c23a'
  return '#409eff'
}

export function fromFlowState(
  nodes: Array<{ id: string; position: { x: number; y: number }; data: { type: string; label: string; params: Record<string, unknown> } }>,
  edges: Array<{ id: string; source: string; target: string; sourceHandle?: string | null; label?: string | null }>
): Pick<WorkflowDefinition, 'nodes' | 'edges'> {
  return {
    nodes: nodes.map((n) => ({
      id: n.id,
      type: n.data.type,
      label: n.data.label,
      position: { ...n.position },
      params: { ...n.data.params }
    })),
    edges: edges.map((e) => ({
      id: e.id,
      source: e.source,
      target: e.target,
      sourceHandle: e.sourceHandle ?? undefined,
      label: e.label ?? undefined
    }))
  }
}

let seq = 100
export function nextNodeId() {
  seq += 1
  return `n${seq}`
}

export function nextEdgeId() {
  seq += 1
  return `e${seq}`
}

/** 从连线关系为节点附加 nextId（仅用于导出，不参与画布编辑态） */
export function attachNextIds(
  nodes: WorkflowNodeDef[],
  edges: WorkflowEdgeDef[]
): WorkflowNodeDef[] {
  function targetOf(sourceId: string, sourceHandle?: string): string | null {
    const outs = edges.filter((e) => e.source === sourceId)
    if (sourceHandle) {
      const matched = outs.find((e) => e.sourceHandle === sourceHandle)
      if (matched) return matched.target
    }
    const fallback = outs.find((e) => !e.sourceHandle)
    return fallback?.target ?? null
  }

  return nodes.map((node) => {
    const base = { ...node }
    delete base.nextId
    delete base.failNextId
    delete base.timeoutNextId

    if (node.type === 'end') {
      return { ...base, nextId: null }
    }
    if (node.type === 'branch') {
      return {
        ...base,
        nextId: targetOf(node.id, 'success'),
        failNextId: targetOf(node.id, 'fail')
      }
    }
    if (node.type === 'waitEvent') {
      return {
        ...base,
        nextId: targetOf(node.id, WAIT_EVENT_HANDLES.triggered),
        timeoutNextId: targetOf(node.id, WAIT_EVENT_HANDLES.timeout)
      }
    }
    return { ...base, nextId: targetOf(node.id) }
  })
}

/** 导出用：附带 nextId 的流程快照（params 按当前控件 schema 规范化） */
export function workflowForExport(def: WorkflowDefinition): WorkflowDefinition {
  const normalized = normalizeWorkflow(def)
  return {
    ...normalized,
    nodes: attachNextIds(normalized.nodes, normalized.edges)
  }
}

/** 将流程 DSL 触发浏览器下载为本地 JSON 文件 */
export function downloadWorkflowJson(def: WorkflowDefinition, filename?: string) {
  const payload = workflowForExport(def)
  const base = ((filename ?? def.name) || 'workflow').trim() || 'workflow'
  const safe = base.replace(/[\\/:*?"<>|]/g, '_')
  downloadJsonBlob(JSON.stringify(payload, null, 2), `${safe}.json`)
}

export function downloadJsonBlob(content: string, filename: string) {
  const blob = new Blob([content], { type: 'application/json;charset=utf-8' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = filename
  document.body.appendChild(a)
  a.click()
  document.body.removeChild(a)
  URL.revokeObjectURL(url)
}

/** 导出流程库：包含所有已保存流程（含 nextId） */
export function downloadWorkflowCatalogExport(
  workflows: WorkflowDefinition[],
  filename = 'workflow-catalog'
) {
  const payload: WorkflowCatalogExport = {
    catalogVersion: WORKFLOW_CATALOG_VERSION,
    exportedAt: new Date().toISOString(),
    workflows: workflows.map((w) => workflowForExport(w))
  }
  const safe = filename.replace(/[\\/:*?"<>|]/g, '_')
  downloadJsonBlob(JSON.stringify(payload, null, 2), `${safe}.json`)
}
