import type { IChartApi, ISeriesApi, LayoutOptions } from 'lightweight-charts'
import { ColorType, createChart } from 'lightweight-charts'
import {
  createContext,
  useState,
  useRef,
  useEffect,
  useCallback,
  forwardRef,
  useLayoutEffect,
  useImperativeHandle,
  useContext,
} from 'react'

import { GridTile } from '../../../components/mollecules'
import type { Candle } from '../../../generated/ManagingApi'

const Context = createContext(() => {})

const initialData = [
  { time: '2018-10-11', value: 52.89 },
  { time: '2018-10-12', value: 51.65 },
  { time: '2018-10-13', value: 51.56 },
  { time: '2018-10-14', value: 50.19 },
  { time: '2018-10-15', value: 51.86 },
  { time: '2018-10-16', value: 51.25 },
]

const currentDate = new Date(initialData[initialData.length - 1].time)

export const WorkersWidget = (props: any) => {
  const {
    colors: {
      backgroundColor = 'black',
      lineColor = '#2962FF',
      textColor = 'white',
    } = {},
  } = props

  const [chartLayoutOptions, setChartLayoutOptions] = useState<LayoutOptions>()
  // The following variables illustrate how a series could be updated.
  const series1 = useRef(null)
  const [started, setStarted] = useState(false)

  // The purpose of this effect is purely to show how a series could
  // be updated using the `reference` passed to the `Series` component.
  useEffect(() => {
    if (series1.current === null) {
      return
    }

    if (started) {
      const interval = setInterval(() => {
        currentDate.setDate(currentDate.getDate() + 1)
        const next = {
          time: currentDate.toISOString().slice(0, 10),
          value: 53 - 2 * Math.random(),
        }

        if (series1.current) {
          (series1.current as ISeriesApi<'Candlestick'>).update(next)
        }
      }, 1000)
      return () => clearInterval(interval)
    }
  }, [started])

  useEffect(() => {
    setChartLayoutOptions({
      background: {
        color: backgroundColor as string,
        type: ColorType.Solid,
      },
      fontFamily: '',
      fontSize: 5,
      textColor,
    })
  }, [backgroundColor, textColor])

  return (
    <>
      <GridTile title="Workers">
        <button type="button" onClick={() => setStarted((current) => !current)}>
          {started ? 'Stop updating' : 'Start updating series'}
        </button>
        {chartLayoutOptions ? (
          <Chart layout={chartLayoutOptions}>
            <Series
              ref={series1}
              type={'line'}
              data={initialData}
              color={lineColor}
            />
          </Chart>
        ) : null}
      </GridTile>
    </>
  )
}

export function Chart(props: ChartContainerProps) {
  const [container, setContainer] = useState<HTMLElement>()

  const handleRef = useCallback((ref: HTMLElement) => setContainer(ref), [])

  return (
    <div ref={handleRef}>
      {container && <ChartContainer {...props} container={container} />}
    </div>
  )
}

interface ChartContainerProps {
  children: React.ReactNode
  container: HTMLElement | null
  layout: LayoutOptions
}

interface ChartApiRef {
  api(): IChartApi // Replace `YourApiReturnType` with the appropriate return type for `api`
  free(): void
}

const ChartContainer = forwardRef<unknown, ChartContainerProps>(
  (props, ref) => {
    const { children, container, layout, ...rest } = props

    const chartApiRef = useRef<ChartApiRef>({
      api() {
        if (!this._api) {
          this._api = createChart(container!, {
            ...rest,
            height: 300,
            layout,
            width: container!.clientWidth,
          })
          this._api.timeScale().fitContent()
        }
        return this._api
      },
      free() {
        if (this._api) {
          this._api.remove()
        }
      },
    })

    useLayoutEffect(() => {
      const currentRef = chartApiRef.current
      const chart = currentRef.api()

      const handleResize = () => {
        chart.applyOptions({
          ...rest,
          width: container!.clientWidth,
        })
      }

      window.addEventListener('resize', handleResize)
      return () => {
        window.removeEventListener('resize', handleResize)
        chart.remove()
      }
    }, [])

    useLayoutEffect(() => {
      const currentRef = chartApiRef.current
      currentRef.api()
    }, [])

    useLayoutEffect(() => {
      const currentRef = chartApiRef.current
      currentRef.api().applyOptions(rest)
    }, [])

    useImperativeHandle(ref, () => chartApiRef.current.api(), [])

    useEffect(() => {
      const currentRef = chartApiRef.current
      currentRef.api().applyOptions({ layout })
    }, [layout])

    return (
      <Context.Provider value={chartApiRef.current}>
        {props.children}
      </Context.Provider>
    )
  }
)
interface SeriesProps {
  children: React.ReactNode
  data: Candle[] // Replace `YourDataType` with the appropriate type for `data`
  type: 'line' | string // Replace `string` with the appropriate type for `type`
}

interface SeriesRef {
  api: ISeriesApi<'Candlestick'>
  free: void
}

const Series = forwardRef<SeriesRef, SeriesProps>((props, ref) => {
  const parent = useContext(Context)
  const context = useRef({
    api() {
      if (!this._api) {
        const { children, data, type, ...rest } = props
        this._api =
          type === 'line'
            ? parent.api().addLineSeries(rest)
            : parent.api().add(rest)
        this._api.setData(data)
      }
      return this._api
    },
    free() {
      if (this._api) {
        parent.free()
      }
    },
  })

  useLayoutEffect(() => {
    const currentRef = context.current
    currentRef.api()

    return () => currentRef.free()
  }, [])

  useLayoutEffect(() => {
    const currentRef = context.current
    const { children, data, ...rest } = props
    currentRef.api().applyOptions(rest)
  })

  useImperativeHandle(ref, () => context.current.api(), [])

  return (
    <Context.Provider value={context.current}>
      {props.children}
    </Context.Provider>
  )
})

Series.displayName = 'Series'

export default WorkersWidget
