'use client'

import React, { useEffect, useRef, RefObject } from 'react'
import { gsap } from 'gsap'

const lerp = (a: number, b: number, n: number): number => (1 - n) * a + n * b

const getMousePos = (e: MouseEvent, container?: HTMLElement | null): { x: number; y: number } => {
  if (container) {
    const bounds = container.getBoundingClientRect()
    return {
      x: e.clientX - bounds.left,
      y: e.clientY - bounds.top,
    }
  }
  return { x: e.clientX, y: e.clientY }
}

interface CrosshairProps {
  color?: string
  containerRef?: RefObject<HTMLDivElement | null> | null
}

const Crosshair: React.FC<CrosshairProps> = ({ color = 'white', containerRef = null }) => {
  const cursorRef = useRef<HTMLDivElement>(null)
  const lineHorizontalRef = useRef<HTMLDivElement>(null)
  const lineVerticalRef = useRef<HTMLDivElement>(null)
  const filterXRef = useRef<SVGFETurbulenceElement>(null)
  const filterYRef = useRef<SVGFETurbulenceElement>(null)

  let mouse = { x: 0, y: 0 }

  useEffect(() => {
    const handleMouseMove = (ev: Event) => {
      const mouseEvent = ev as MouseEvent
      mouse = getMousePos(mouseEvent, containerRef?.current || undefined)

      if (containerRef?.current) {
        const bounds = containerRef.current.getBoundingClientRect()
        if (
          mouseEvent.clientX < bounds.left ||
          mouseEvent.clientX > bounds.right ||
          mouseEvent.clientY < bounds.top ||
          mouseEvent.clientY > bounds.bottom
        ) {
          gsap.to([lineHorizontalRef.current, lineVerticalRef.current].filter(Boolean), { opacity: 0 })
        } else {
          gsap.to([lineHorizontalRef.current, lineVerticalRef.current].filter(Boolean), { opacity: 1 })
        }
      }
    }

    const target: HTMLElement | Window = containerRef?.current || window
    target.addEventListener('mousemove', handleMouseMove)

    const renderedStyles: {
      [key: string]: { previous: number; current: number; amt: number }
    } = {
      tx: { previous: 0, current: 0, amt: 0.15 },
      ty: { previous: 0, current: 0, amt: 0.15 },
    }

    gsap.set([lineHorizontalRef.current, lineVerticalRef.current].filter(Boolean), { opacity: 0 })

    const onMouseMove = (ev: Event) => {
      const mouseEvent = ev as MouseEvent
      mouse = getMousePos(mouseEvent, containerRef?.current || undefined)

      renderedStyles.tx.previous = renderedStyles.tx.current = mouse.x
      renderedStyles.ty.previous = renderedStyles.ty.current = mouse.y

      gsap.to([lineHorizontalRef.current, lineVerticalRef.current].filter(Boolean), {
        duration: 0.9,
        ease: 'Power3.easeOut',
        opacity: 1,
      })

      requestAnimationFrame(render)

      target.removeEventListener('mousemove', onMouseMove)
    }

    target.addEventListener('mousemove', onMouseMove)

    const primitiveValues = { turbulence: 0 }

    const tl = gsap
      .timeline({
        paused: true,
        onStart: () => {
          if (lineHorizontalRef.current) {
            lineHorizontalRef.current.style.filter = 'url(#filter-noise-x)'
          }
          if (lineVerticalRef.current) {
            lineVerticalRef.current.style.filter = 'url(#filter-noise-y)'
          }
        },
        onUpdate: () => {
          if (filterXRef.current && filterYRef.current) {
            filterXRef.current.setAttribute('baseFrequency', primitiveValues.turbulence.toString())
            filterYRef.current.setAttribute('baseFrequency', primitiveValues.turbulence.toString())
          }
        },
        onComplete: () => {
          if (lineHorizontalRef.current) lineHorizontalRef.current.style.filter = 'none'
          if (lineVerticalRef.current) lineVerticalRef.current.style.filter = 'none'
        },
      })
      .to(primitiveValues, {
        duration: 0.5,
        ease: 'power1',
        startAt: { turbulence: 1 },
        turbulence: 0,
      })

    const enter = () => tl.restart()
    const leave = () => {
      tl.progress(1).kill()
    }

    const render = () => {
      renderedStyles.tx.current = mouse.x
      renderedStyles.ty.current = mouse.y

      for (const key in renderedStyles) {
        const style = renderedStyles[key]
        style.previous = lerp(style.previous, style.current, style.amt)
      }

      if (lineHorizontalRef.current && lineVerticalRef.current) {
        gsap.set(lineVerticalRef.current, { x: renderedStyles.tx.previous })
        gsap.set(lineHorizontalRef.current, { y: renderedStyles.ty.previous })
      }

      requestAnimationFrame(render)
    }

    const links: NodeListOf<HTMLAnchorElement> = containerRef?.current
      ? containerRef.current.querySelectorAll('a')
      : document.querySelectorAll('a')

    links.forEach(link => {
      link.addEventListener('mouseenter', enter)
      link.addEventListener('mouseleave', leave)
    })

    return () => {
      target.removeEventListener('mousemove', handleMouseMove)
      target.removeEventListener('mousemove', onMouseMove)
      links.forEach(link => {
        link.removeEventListener('mouseenter', enter)
        link.removeEventListener('mouseleave', leave)
      })
    }
  }, [containerRef])

  return (
    <div
      ref={cursorRef}
      className={`${containerRef ? 'absolute' : 'fixed'} top-0 left-0 w-full h-full pointer-events-none z-[10000]`}
    >
      <svg className="absolute top-0 left-0 w-full h-full">
        <defs>
          <filter id="filter-noise-x">
            <feTurbulence
              type="fractalNoise"
              baseFrequency="0.000001"
              numOctaves="1"
              ref={filterXRef}
            />
            <feDisplacementMap in="SourceGraphic" scale="40" />
          </filter>
          <filter id="filter-noise-y">
            <feTurbulence
              type="fractalNoise"
              baseFrequency="0.000001"
              numOctaves="1"
              ref={filterYRef}
            />
            <feDisplacementMap in="SourceGraphic" scale="40" />
          </filter>
        </defs>
      </svg>
      <div
        ref={lineHorizontalRef}
        className="absolute w-full h-px pointer-events-none opacity-0 translate-y-1/2"
        style={{ background: color }}
      />
      <div
        ref={lineVerticalRef}
        className="absolute h-full w-px pointer-events-none opacity-0 translate-x-1/2"
        style={{ background: color }}
      />
    </div>
  )
}

export default Crosshair
