import React, { ComponentType, useEffect, useRef, useState } from "react"
import { useContextSelector } from "use-context-selector"
import { useTimeout } from "usehooks-ts"
import { Tooltip, TooltipProps } from "../../components/Tooltip"
import { useCallbackRef } from "../../hooks/useCallbackRef"
import { ChartContext } from "./ChartContext"

export type RenderTooltipFn<T, S = object> = ComponentType<
  {
    item: T
    point: Highcharts.Point
  } & S
>

// Extra y-offset required to align tooltip better to different types of highchart points
const POINT_TYPE_TO_Y_OFFSET = {
  marker: 7,
  column: -2,
} as const

type PointType = keyof typeof POINT_TYPE_TO_Y_OFFSET

type ChartTooltipProps = Pick<TooltipProps, "content"> & {
  pointType: PointType
  delay?: number
}

const ChartTooltipBase = ({ content, pointType, delay }: ChartTooltipProps) => {
  const [open, setOpen] = useState(false)
  const [isPanning, setHoveredPoint, hoveredPoint] = useContextSelector(
    ChartContext,
    v => [v.isPanning, v.setHoveredPoint, v.hoveredPoint],
  )
  const [ref, setRef] = useCallbackRef<HTMLDivElement>()
  const hasOpened = useRef(false)

  useEffect(() => {
    if (open) {
      hasOpened.current = true
    }
  }, [open])

  useTimeout(() => setOpen(true), delay ?? 0)

  useEffect(() => {
    const close = () => setHoveredPoint(undefined)
    window.addEventListener("scroll", close)

    return () => {
      window.removeEventListener("scroll", close)
    }
  }, [setHoveredPoint])

  if (isPanning || !hoveredPoint?.series) {
    return <></>
  }

  const seriesLeft =
    "left" in hoveredPoint.series.xAxis
      ? (hoveredPoint.series.xAxis.left as number)
      : 0

  const left =
    seriesLeft + hoveredPoint.series.chart.plotLeft + (hoveredPoint.plotX ?? 0)

  const top =
    hoveredPoint.series.chart.plotTop +
    hoveredPoint.series.yAxis.toPixels(
      hoveredPoint.total ?? hoveredPoint.y ?? 0,
      true,
    ) -
    POINT_TYPE_TO_Y_OFFSET[pointType]

  return (
    <Tooltip
      anchor={ref}
      animation={hasOpened.current ? "none" : "fade-in"}
      content={<div className="contents font-mono">{content}</div>}
      disablePointerEvents
      hoverable={false}
      key={`${left}-${top}`}
      open={delay ? open : true}
    >
      <div
        className="absolute"
        key={`${left}-${top}`}
        ref={setRef}
        style={{
          left,
          top,
        }}
      />
    </Tooltip>
  )
}

export const ChartTooltip = (props: ChartTooltipProps) => {
  const hoveredPoint = useContextSelector(ChartContext, v => v.hoveredPoint)

  return (
    <ChartTooltipBase
      key={props.delay ? `${hoveredPoint?.x}-${hoveredPoint?.y}` : undefined}
      {...props}
    />
  )
}
