/**
 * ECharts 性能优化配置
 *
 * 提供大数据量场景下的性能优化方案
 */

import type { EChartOption } from 'echarts'

/**
 * 大数据量配置
 */
export const performanceConfig = {
  // 渐进式渲染
  progressive: 1000, // 渐进式渲染阈值
  progressiveThreshold: 5000, // 开启渐进式渲染的数据量阈值

  // 悬停层阈值
  hoverLayerThreshold: 3000, // 开启悬停层的数据量阈值

  // 使用 UTC 时间
  useUTC: false,

  // 动画配置
  animation: true,
  animationDuration: 1000,
  animationDurationUpdate: 500,
  animationEasing: 'cubicOut',
  animationEasingUpdate: 'cubicInOut',

  // 数据量限制
  maxDataPoints: 10000, // 最大数据点数
  maxCategories: 100, // 最大分类数
}

/**
 * 应用性能优化配置
 */
export function applyPerformanceConfig(option: EChartOption): EChartOption {
  return {
    ...option,
    progressive: performanceConfig.progressive,
    progressiveThreshold: performanceConfig.progressiveThreshold,
    hoverLayerThreshold: performanceConfig.hoverLayerThreshold,
    useUTC: performanceConfig.useUTC,
    animation: performanceConfig.animation,
    animationDuration: performanceConfig.animationDuration,
    animationDurationUpdate: performanceConfig.animationDurationUpdate,
    animationEasing: performanceConfig.animationEasing,
    animationEasingUpdate: performanceConfig.animationEasingUpdate,
  }
}

/**
 * 采样数据（大数据量场景）
 */
export function sampleData<T>(data: T[], maxSize: number): T[] {
  if (data.length <= maxSize) return data

  const step = Math.ceil(data.length / maxSize)
  return data.filter((_, index) => index % step === 0)
}

/**
 * 聚合数据（时间序列）
 */
export function aggregateDataByTime(
  data: Array<{ date: string; value: number }>,
  interval: 'day' | 'week' | 'month' | 'year'
): Array<{ date: string; value: number }> {
  const grouped = new Map<string, number[]>()

  data.forEach((item) => {
    const date = new Date(item.date)
    let key: string

    switch (interval) {
      case 'day':
        key = date.toISOString().split('T')[0]
        break
      case 'week':
        const weekStart = new Date(date)
        weekStart.setDate(date.getDate() - date.getDay())
        key = weekStart.toISOString().split('T')[0]
        break
      case 'month':
        key = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`
        break
      case 'year':
        key = String(date.getFullYear())
        break
    }

    if (!grouped.has(key)) {
      grouped.set(key, [])
    }
    grouped.get(key)!.push(item.value)
  })

  return Array.from(grouped.entries()).map(([date, values]) => ({
    date,
    value: values.reduce((sum, v) => sum + v, 0) / values.length,
  }))
}

/**
 * 分页数据
 */
export function paginateData<T>(data: T[], page: number, pageSize: number): T[] {
  const start = page * pageSize
  const end = start + pageSize
  return data.slice(start, end)
}

/**
 * 虚拟滚动配置
 */
export const virtualScrollConfig = {
  enabled: true,
  itemSize: 40, // 每项高度
  bufferSize: 10, // 缓冲区大小
  threshold: 100, // 启用虚拟滚动的数据量阈值
}

/**
 * 数据压缩配置
 */
export const dataCompressionConfig = {
  enabled: true,
  threshold: 1000, // 启用压缩的数据量阈值
  algorithm: 'lttb', // 算法: lttb (Largest-Triangle-Three-Buckets)
}

/**
 * LTTB 算法实现（ downsampling）
 */
export function lttbDownsampling(
  data: Array<{ x: number; y: number }>,
  threshold: number
): Array<{ x: number; y: number }> {
  if (threshold >= data.length || threshold <= 0) return data

  const sampled = []
  const bucketSize = (data.length - 2) / (threshold - 2)

  let a = 0 // 初始点
  sampled.push(data[a])

  let maxAreaPoint
  let maxArea
  let area

  for (let i = 0; i < threshold - 2; i++) {
    // 计算下一个桶的边界
    const avgRangeStart = Math.floor((i + 1) * bucketSize) + 1
    const avgRangeEnd = Math.floor((i + 2) * bucketSize) + 1
    const avgRangeLength = avgRangeEnd - avgRangeStart

    const avgX = 0
    const avgY = 0

    for (let j = avgRangeStart; j < avgRangeEnd && j < data.length; j++) {
      avgX += data[j].x
      avgY += data[j].y
    }

    avgX /= avgRangeLength
    avgY /= avgRangeLength

    // 获取桶的起始和结束点
    const rangeOffs = Math.floor((i + 0) * bucketSize) + 1
    const rangeTo = Math.floor((i + 1) * bucketSize) + 1

    const pointAX = data[a].x
    const pointAY = data[a].y

    maxArea = -1
    maxAreaPoint = data[rangeOffs]

    for (let j = rangeOffs; j < rangeTo && j < data.length; j++) {
      area = Math.abs(
        (pointAX - avgX) * (data[j].y - pointAY) -
        (pointAX - data[j].x) * (avgY - pointAY)
      )

      if (area > maxArea) {
        maxArea = area
        maxAreaPoint = data[j]
      }
    }

    sampled.push(maxAreaPoint)
    a = rangeOffs
  }

  sampled.push(data[data.length - 1]) // 最后一个点

  return sampled
}

/**
 * 防抖函数（用于 resize）
 */
export function debounce<T extends (...args: any[]) => any>(
  func: T,
  wait: number
): (...args: Parameters<T>) => void {
  let timeout: ReturnType<typeof setTimeout> | null = null

  return function (this: any, ...args: Parameters<T>) {
    if (timeout) clearTimeout(timeout)
    timeout = setTimeout(() => {
      func.apply(this, args)
    }, wait)
  }
}

/**
 * 节流函数（用于滚动事件）
 */
export function throttle<T extends (...args: any[]) => any>(
  func: T,
  limit: number
): (...args: Parameters<T>) => void {
  let inThrottle: boolean = false

  return function (this: any, ...args: Parameters<T>) {
    if (!inThrottle) {
      func.apply(this, args)
      inThrottle = true
      setTimeout(() => (inThrottle = false), limit)
    }
  }
}

/**
 * 监控图表性能
 */
export class ChartPerformanceMonitor {
  private renderTimes: number[] = []
  private maxSamples = 100

  recordRenderTime(time: number) {
    this.renderTimes.push(time)
    if (this.renderTimes.length > this.maxSamples) {
      this.renderTimes.shift()
    }
  }

  getAverageRenderTime(): number {
    if (this.renderTimes.length === 0) return 0
    const sum = this.renderTimes.reduce((a, b) => a + b, 0)
    return sum / this.renderTimes.length
  }

  getMaxRenderTime(): number {
    if (this.renderTimes.length === 0) return 0
    return Math.max(...this.renderTimes)
  }

  getMinRenderTime(): number {
    if (this.renderTimes.length === 0) return 0
    return Math.min(...this.renderTimes)
  }

  getStats() {
    return {
      average: this.getAverageRenderTime(),
      max: this.getMaxRenderTime(),
      min: this.getMinRenderTime(),
      samples: this.renderTimes.length,
    }
  }

  clear() {
    this.renderTimes = []
  }
}

/**
 * 创建性能监控器
 */
export function createPerformanceMonitor(): ChartPerformanceMonitor {
  return new ChartPerformanceMonitor()
}
