import { nextTick, onBeforeUnmount, onMounted, watch } from 'vue'
import { init } from 'echarts/core'

export function useEcharts(chartElement, chartOptions) {
  let chartInstance = null
  let resizeObserver = null
  let renderFrame = 0
  let initialFrame = 0
  let initialTimer = 0
  let initialRendered = false
  let initialPending = false
  let latestOption = null
  const initialAnimationDelay = 180

  function renderChart() {
    if (!chartElement.value) {
      return
    }
    if (!chartInstance) {
      chartInstance = init(chartElement.value, null, { renderer: 'canvas' })
      chartInstance.resize()
    }

    if (!initialRendered) {
      initialRendered = true
      initialPending = true
      latestOption = chartOptions.value
      if (typeof window !== 'undefined') {
        initialTimer = window.setTimeout(() => {
          initialTimer = 0
          initialFrame = window.requestAnimationFrame(() => {
            initialFrame = 0
            initialPending = false
            chartInstance?.setOption(latestOption || chartOptions.value, {
              notMerge: true,
              lazyUpdate: false
            })
            latestOption = null
          })
        }, initialAnimationDelay)
        return
      }
    }

    if (initialPending) {
      latestOption = chartOptions.value
      return
    }

    chartInstance.setOption(chartOptions.value, {
      notMerge: false,
      lazyUpdate: false
    })
  }

  function handleResize() {
    chartInstance?.resize()
  }

  function scheduleRender() {
    if (typeof window === 'undefined') {
      renderChart()
      return
    }
    if (renderFrame) {
      window.cancelAnimationFrame(renderFrame)
    }
    renderFrame = window.requestAnimationFrame(() => {
      renderFrame = 0
      renderChart()
    })
  }

  function bindResize() {
    if (!chartElement.value) {
      return
    }
    if (typeof ResizeObserver !== 'undefined') {
      resizeObserver = new ResizeObserver(handleResize)
      resizeObserver.observe(chartElement.value)
    }
    window.addEventListener('resize', handleResize)
  }

  function unbindResize() {
    resizeObserver?.disconnect()
    resizeObserver = null
    window.removeEventListener('resize', handleResize)
  }

  onMounted(() => {
    renderChart()
    bindResize()
  })

  onBeforeUnmount(() => {
    unbindResize()
    if (renderFrame && typeof window !== 'undefined') {
      window.cancelAnimationFrame(renderFrame)
      renderFrame = 0
    }
    if (initialFrame && typeof window !== 'undefined') {
      window.cancelAnimationFrame(initialFrame)
      initialFrame = 0
    }
    if (initialTimer && typeof window !== 'undefined') {
      window.clearTimeout(initialTimer)
      initialTimer = 0
    }
    if (chartInstance) {
      chartInstance.dispose()
      chartInstance = null
    }
  })

  watch(chartOptions, () => {
    nextTick(scheduleRender)
  }, { deep: true })

  return {
    renderChart
  }
}
