/**
 * 公式解析引擎
 * 支持运算符: + - * / ( ) 以及 min(), max(), round() 函数
 * 变量名规则: 字母开头，字母数字下划线组合
 */

/**
 * 从公式中提取依赖的变量名
 */
export function extractDependencies(formula: string): string[] {
  const vars = new Set<string>()
  // 匹配标识符（排除函数名和数字）
  const matches = formula.match(/[a-zA-Z_]\w*/g) || []
  const builtins = new Set(['min', 'max', 'round', 'Math'])
  for (const m of matches) {
    if (!builtins.has(m)) vars.add(m)
  }
  return [...vars]
}

/**
 * 安全的公式求值器
 * 仅支持 +、-、*、/、()、数字、变量名、min()、max()、round()
 */
export function evalFormula(formula: string, variables: Record<string, number>): number {
  // 替换变量为值
  const expr = formula.replace(/[a-zA-Z_]\w*/g, (name) => {
    if (name === 'min' || name === 'max' || name === 'round') return name
    if (name in variables) return String(variables[name])
    // 未知变量视为 0（兼容可选字段未传值的场景）
    return '0'
  })

  // 安全校验：只允许数字、运算符、括号、min/max/round
  if (!/^[\d\s+\-*/().,minaxroud]+$/.test(expr)) {
    throw new Error(`公式包含非法字符: ${expr}`)
  }

  try {
    // eslint-disable-next-line no-new-func
    const result = new Function('min', 'max', 'round', `return ${expr}`)(Math.min, Math.max, Math.round)
    return Math.round(result * 100) / 100
  } catch (e: any) {
    throw new Error(`公式求值失败: ${formula}, 错误: ${e.message}`)
  }
}

/**
 * 拓扑排序：按依赖关系排列计算项
 * 输入项在前，计算项按依赖顺序排列
 */
export function topologicalSort(items: { code: string; type: string; calcType?: string; formula?: string | null; dependencies?: string }[]): typeof items {
  const inDegree = new Map<string, number>()
  const graph = new Map<string, string[]>()

  for (const item of items) {
    inDegree.set(item.code, 0)
    graph.set(item.code, [])
  }

  // 解析依赖，构建 DAG
  for (const item of items) {
    let deps: string[] = []
    if (item.dependencies) {
      try { deps = JSON.parse(item.dependencies) } catch { deps = [] }
    } else if (item.formula && item.type === 'CALCULATED' && item.calcType !== 'SYSTEM') {
      // 如果 dependencies 未设置，从公式中自动提取
      deps = extractDependencies(item.formula)
    }
    for (const dep of deps) {
      if (graph.has(dep)) {
        graph.get(dep)!.push(item.code)
        inDegree.set(item.code, (inDegree.get(item.code) || 0) + 1)
      }
    }
  }

  // Kahn 算法
  const queue: string[] = []
  for (const [code, degree] of inDegree.entries()) {
    if (degree === 0) queue.push(code)
  }

  const sorted: typeof items = []
  // 先排输入项（保持原顺序）
  for (const item of items) {
    if (item.type === 'INPUT' && inDegree.get(item.code) === 0) {
      sorted.push(item)
    }
  }
  queue.length = 0
  for (const [code, degree] of inDegree.entries()) {
    if (degree === 0 && !items.find(i => i.code === code && i.type === 'INPUT')) {
      queue.push(code)
    }
  }

  while (queue.length) {
    const code = queue.shift()!
    const item = items.find(i => i.code === code)
    if (item && item.type !== 'INPUT') {
      sorted.push(item)
    }
    for (const next of graph.get(code) || []) {
      inDegree.set(next, (inDegree.get(next) || 0) - 1)
      if (inDegree.get(next) === 0) queue.push(next)
    }
  }

  // 检查循环依赖
  if (sorted.length < items.length) {
    const missing = items.filter(i => !sorted.find(s => s.code === i.code))
    throw new Error(`薪酬模板存在循环依赖，涉及字段: ${missing.map(m => m.code).join(', ')}`)
  }

  return sorted
}
