// @ts-check
import Utils from '../../utils/Utils'

/**
 * ApexCharts Tooltip.Intersect Class.
 * This file deals with functions related to intersecting tooltips
 * (tooltips that appear when user hovers directly over a data-point whether)
 *
 * @module Tooltip.Intersect
 **/

class Intersect {
  /**
   * @param {import('./Tooltip').default} tooltipContext
   */
  constructor(tooltipContext) {
    this.w = tooltipContext.w
    const w = this.w
    this.ttCtx = tooltipContext

    this.isVerticalGroupedRangeBar =
      !w.globals.isBarHorizontal &&
      w.config.chart.type === 'rangeBar' &&
      w.config.plotOptions.bar.rangeBarGroupRows
  }

  // a helper function to get an element's attribute value
  /**
   * @param {Event} e
   * @param {string} attr
   */
  getAttr(e, attr) {
    return parseFloat(
      /** @type {Element} */ (e.target).getAttribute(attr) ?? '',
    )
  }

  // handle tooltip for heatmaps and treemaps
  /** @param {{e: any, opt: any, x: any, y: any, type: any}} opts */
  handleHeatTreeTooltip({ e, opt, x, y, type }) {
    const ttCtx = this.ttCtx
    const w = this.w

    if (e.target.classList.contains(`apexcharts-${type}-rect`)) {
      const i = this.getAttr(e, 'i')
      const j = this.getAttr(e, 'j')
      const cx = this.getAttr(e, 'cx')
      const cy = this.getAttr(e, 'cy')
      const width = this.getAttr(e, 'width')
      const height = this.getAttr(e, 'height')

      ttCtx.tooltipLabels.drawSeriesTexts({
        ttItems: opt.ttItems,
        i,
        j,
        shared: false,
        e,
      })

      w.interact.capturedSeriesIndex = i
      w.interact.capturedDataPointIndex = j

      x = cx + ttCtx.tooltipRect.ttWidth / 2 + width
      y = cy + ttCtx.tooltipRect.ttHeight / 2 - height / 2

      ttCtx.tooltipPosition.moveXCrosshairs(cx + width / 2)

      if (x > w.layout.gridWidth / 2) {
        x = cx - ttCtx.tooltipRect.ttWidth / 2 + width
      }
      if (ttCtx.w.config.tooltip.followCursor) {
        const seriesBound = w.dom.elWrap.getBoundingClientRect()
        x =
          (w.interact.clientX ?? 0) -
          seriesBound.left -
          (x > w.layout.gridWidth / 2 ? ttCtx.tooltipRect.ttWidth : 0)
        y =
          (w.interact.clientY ?? 0) -
          seriesBound.top -
          (y > w.layout.gridHeight / 2 ? ttCtx.tooltipRect.ttHeight : 0)
      }
    }

    return {
      x,
      y,
    }
  }

  /**
   * handle tooltips for line/area/scatter charts where tooltip.intersect is true
   * when user hovers over the marker directly, this function is executed
   */
  /** @param {{e: any, opt: any, x: any, y: any}} opts */
  handleMarkerTooltip({ e, opt, x, y }) {
    const w = this.w
    const ttCtx = this.ttCtx

    let i
    let j
    if (e.target.classList.contains('apexcharts-marker')) {
      const cx = parseInt(opt.paths.getAttribute('cx'), 10)
      const cy = parseInt(opt.paths.getAttribute('cy'), 10)
      const val = parseFloat(opt.paths.getAttribute('val'))

      j = parseInt(opt.paths.getAttribute('rel'), 10)
      i =
        parseInt(
          opt.paths.parentNode.parentNode.parentNode.getAttribute('rel'),
          10,
        ) - 1

      if (ttCtx.intersect) {
        const el = Utils.findAncestor(opt.paths, 'apexcharts-series')
        if (el) {
          i = parseInt(el.getAttribute('data:realIndex'), 10)
        }
      }

      ttCtx.tooltipLabels.drawSeriesTexts({
        ttItems: opt.ttItems,
        i,
        j,
        shared: ttCtx.showOnIntersect ? false : w.config.tooltip.shared,
        e,
      })

      if (e.type === 'mouseup') {
        ttCtx.markerClick(e, i, j)
      }

      w.interact.capturedSeriesIndex = i
      w.interact.capturedDataPointIndex = j

      x = cx
      y = cy + w.layout.translateY - ttCtx.tooltipRect.ttHeight * 1.4

      if (ttCtx.w.config.tooltip.followCursor) {
        const elGrid = ttCtx.getElGrid()
        if (!elGrid) return { x, y }
        const seriesBound = elGrid.getBoundingClientRect()
        y = ttCtx.e.clientY + w.layout.translateY - seriesBound.top
      }

      if (val < 0) {
        y = cy
      }
      ttCtx.marker.enlargeCurrentPoint(j, opt.paths, x, y)
    }

    return {
      x,
      y,
    }
  }

  /**
   * handle tooltips for bar/column charts
   */
  /** @param {{e: any, opt: any}} opts */
  handleBarTooltip({ e, opt }) {
    const w = this.w
    const ttCtx = this.ttCtx

    const tooltipEl = ttCtx.getElTooltip()

    let bx = 0
    let x = 0
    let y = 0
    let i = 0
    let strokeWidth
    const barXY = this.getBarTooltipXY({
      e,
      opt,
    })
    if (barXY.j === null && barXY.barHeight === 0 && barXY.barWidth === 0) {
      return // bar was not hovered and didn't receive correct coords
    }

    i = barXY.i
    const j = barXY.j

    w.interact.capturedSeriesIndex = i
    w.interact.capturedDataPointIndex =
      j !== null ? j : w.interact.capturedDataPointIndex

    if (
      (w.globals.isBarHorizontal && ttCtx.tooltipUtil.hasBars()) ||
      !w.config.tooltip.shared
    ) {
      x = barXY.x
      y = barXY.y
      strokeWidth = Array.isArray(w.config.stroke.width)
        ? w.config.stroke.width[i]
        : w.config.stroke.width
      bx = x
    } else {
      if (!w.globals.comboCharts && !w.config.tooltip.shared) {
        // todo: re-check this condition as it's always 0
        bx = bx / 2
      }
    }

    // y is NaN, make it touch the bottom of grid area
    if (isNaN(y)) {
      y = w.globals.svgHeight - ttCtx.tooltipRect.ttHeight
    }

    if (x + ttCtx.tooltipRect.ttWidth > w.layout.gridWidth) {
      x = x - ttCtx.tooltipRect.ttWidth
    } else if (x < 0) {
      x = 0
    }

    if (ttCtx.w.config.tooltip.followCursor) {
      const elGrid = ttCtx.getElGrid()
      if (!elGrid) return
    }

    // if tooltip is still null, querySelector
    if (ttCtx.tooltip === null) {
      ttCtx.tooltip = w.dom.baseEl.querySelector('.apexcharts-tooltip')
    }

    if (!w.config.tooltip.shared) {
      if (w.globals.comboBarCount > 0) {
        ttCtx.tooltipPosition.moveXCrosshairs(bx + strokeWidth / 2)
      } else {
        ttCtx.tooltipPosition.moveXCrosshairs(bx)
      }
    }

    // move tooltip here
    if (
      !ttCtx.fixedTooltip &&
      (!w.config.tooltip.shared ||
        (w.globals.isBarHorizontal && ttCtx.tooltipUtil.hasBars()))
    ) {
      y = y + w.layout.translateY - ttCtx.tooltipRect.ttHeight / 2

      if (tooltipEl) {
        tooltipEl.style.left = x + w.layout.translateX + 'px'
        tooltipEl.style.top = y + 'px'
      }
    }
  }

  /** @param {{e: any, opt: any}} opts */
  getBarTooltipXY({ e, opt }) {
    const w = this.w
    let j = null
    const ttCtx = this.ttCtx
    let i = 0
    let x = 0
    let y = 0
    let barWidth = 0
    let barHeight = 0

    const cl = e.target.classList

    if (
      cl.contains('apexcharts-bar-area') ||
      cl.contains('apexcharts-candlestick-area') ||
      cl.contains('apexcharts-boxPlot-area') ||
      cl.contains('apexcharts-rangebar-area')
    ) {
      const bar = e.target
      const barRect = bar.getBoundingClientRect()

      const seriesBound = opt.elGrid.getBoundingClientRect()

      const bh = barRect.height
      barHeight = barRect.height
      const bw = barRect.width

      const cx = parseInt(bar.getAttribute('cx'), 10)
      const cy = parseInt(bar.getAttribute('cy'), 10)
      barWidth = parseFloat(bar.getAttribute('barWidth'))
      const clientX = e.type === 'touchmove' ? e.touches[0].clientX : e.clientX

      j = parseInt(bar.getAttribute('j'), 10)
      i = parseInt(bar.parentNode.getAttribute('rel'), 10) - 1

      const y1 = bar.getAttribute('data-range-y1')
      const y2 = bar.getAttribute('data-range-y2')

      if (w.globals.comboCharts) {
        i = parseInt(bar.parentNode.getAttribute('data:realIndex'), 10)
      }

      /**
       * @param {number} x
       */
      const handleXForColumns = (x) => {
        if (w.axisFlags.isXNumeric) {
          x = cx - bw / 2
        } else {
          if (this.isVerticalGroupedRangeBar) {
            x = cx + bw / 2
          } else {
            x = cx - ttCtx.dataPointsDividedWidth + bw / 2
          }
        }
        return x
      }

      const handleYForBars = () => {
        return (
          cy -
          ttCtx.dataPointsDividedHeight +
          bh / 2 -
          ttCtx.tooltipRect.ttHeight / 2
        )
      }

      ttCtx.tooltipLabels.drawSeriesTexts({
        ttItems: opt.ttItems,
        i,
        j,
        y1: y1 ? parseInt(y1, 10) : null,
        y2: y2 ? parseInt(y2, 10) : null,
        shared: ttCtx.showOnIntersect ? false : w.config.tooltip.shared,
        e,
      })

      if (w.config.tooltip.followCursor) {
        if (w.globals.isBarHorizontal) {
          x = clientX - seriesBound.left + 15
          y = handleYForBars()
        } else {
          x = handleXForColumns(x)
          y = e.clientY - seriesBound.top - ttCtx.tooltipRect.ttHeight / 2 - 15
        }
      } else {
        if (w.globals.isBarHorizontal) {
          x = cx
          if (ttCtx.xyRatios && x < ttCtx.xyRatios.baseLineInvertedY) {
            x = cx - ttCtx.tooltipRect.ttWidth
          }
          y = handleYForBars()
        } else {
          x = handleXForColumns(x)
          y = cy // - ttCtx.tooltipRect.ttHeight / 2 + 10
        }
      }
    }

    return {
      x,
      y,
      barHeight,
      barWidth,
      i,
      j,
    }
  }
}

export default Intersect
