import { SVGProps, useEffect, useMemo, useRef } from 'react'
import * as d3 from 'd3'

import { useD3MouseZone } from '@components/d3/D3MouseZone'
import { usePartialProps } from '@utils'

import { wrapPlugin } from './base'

import '@styles/d3.less'

export type D3CursorSettings = {
    lineStyle?: SVGProps<SVGGElement>
}

const defaultLineStyle: SVGProps<SVGGElement> = {
    stroke: 'gray',
    strokeWidth: 1,
    strokeOpacity: 1,
    className: '',
}

const defaultSettings: D3CursorSettings = {
    lineStyle: defaultLineStyle,
}

export type D3CursorProps = D3CursorSettings

export const D3Cursor = wrapPlugin<D3CursorSettings>(((props) => {
    const settings = usePartialProps(props, defaultSettings)
    const lineStyle = usePartialProps(settings.lineStyle, defaultLineStyle)

    const { mouseState, zoneRect } = useD3MouseZone()
    const zoneRef = useRef(null)

    const zone = useMemo(() => zoneRef.current ? (() => d3.select(zoneRef.current)) : null, [zoneRef.current])

    const getXLine = useMemo(() => zone ? (() => zone().select('.tooltip-x-line')) : null, [zone])
    const getYLine = useMemo(() => zone ? (() => zone().select('.tooltip-y-line')) : null, [zone])

    useEffect(() => {
        if (!zone || !getXLine || !getYLine) return
        const z = zone()

        if (z.selectAll('line').empty()) {
            z.append('line').attr('class', 'tooltip-x-line').style('pointer-events', 'none')
            z.append('line').attr('class', 'tooltip-y-line').style('pointer-events', 'none')
        }

        getXLine()
            .attr('y1', 0)
            .attr('y2', zoneRect?.height ?? 0)
        
        getYLine()
            .attr('x1', 0)
            .attr('x2', zoneRect?.width ?? 0)
    }, [zone, getXLine, getYLine, zoneRect])

    useEffect(() => {
        if (!getXLine || !getYLine || !mouseState) return

        getXLine()
            .attr('x1', mouseState.x)
            .attr('x2', mouseState.x)
            .attr('opacity', mouseState.visible ? '1' : '0')

        getYLine()
            .attr('y1', mouseState.y)
            .attr('y2', mouseState.y)
            .attr('opacity', mouseState.visible ? '1' : '0')
    }, [mouseState, getXLine, getYLine])


    return (
        <g
            {...lineStyle}
            ref={zoneRef}
            className={`cursor-zone ${lineStyle.className}`}
        />
    )
}), true)

export default D3Cursor
