import {
  useCallback,
  useEffect,
  useRef,
  type ComponentProps,
} from "react"

import { Button } from "~/components/ui/button"
import { cn } from "~/lib/utils"

type SignaturePadProps = Omit<ComponentProps<"div">, "onChange"> & {
  width?: number
  height?: number
  strokeColor?: string
  strokeWidth?: number
  disabled?: boolean
  onChange?: (dataUrl: string | null) => void
  onClear?: () => void
}

function SignaturePad({
  width = 400,
  height = 160,
  strokeColor,
  strokeWidth = 2,
  disabled = false,
  onChange,
  onClear,
  className,
  ...props
}: SignaturePadProps) {
  const canvasRef = useRef<HTMLCanvasElement>(null)
  const drawing = useRef(false)
  const empty = useRef(true)

  const getCtx = useCallback(() => {
    const canvas = canvasRef.current
    if (!canvas) return null
    const ctx = canvas.getContext("2d")
    if (!ctx) return null
    ctx.strokeStyle = strokeColor ?? "currentColor"
    ctx.lineWidth = strokeWidth
    ctx.lineCap = "round"
    ctx.lineJoin = "round"
    return ctx
  }, [strokeColor, strokeWidth])

  const getPos = (e: MouseEvent | TouchEvent, canvas: HTMLCanvasElement) => {
    const rect = canvas.getBoundingClientRect()
    const scaleX = canvas.width / rect.width
    const scaleY = canvas.height / rect.height
    const source = "touches" in e ? e.touches[0] : e
    return {
      x: (source.clientX - rect.left) * scaleX,
      y: (source.clientY - rect.top) * scaleY,
    }
  }

  const startDraw = useCallback(
    (e: MouseEvent | TouchEvent) => {
      if (disabled) return
      const canvas = canvasRef.current
      if (!canvas) return
      const ctx = getCtx()
      if (!ctx) return
      drawing.current = true
      const { x, y } = getPos(e, canvas)
      ctx.beginPath()
      ctx.moveTo(x, y)
      e.preventDefault()
    },
    [disabled, getCtx]
  )

  const draw = useCallback(
    (e: MouseEvent | TouchEvent) => {
      if (!drawing.current || disabled) return
      const canvas = canvasRef.current
      if (!canvas) return
      const ctx = getCtx()
      if (!ctx) return
      const { x, y } = getPos(e, canvas)
      ctx.lineTo(x, y)
      ctx.stroke()
      empty.current = false
      e.preventDefault()
    },
    [disabled, getCtx]
  )

  const endDraw = useCallback(() => {
    if (!drawing.current) return
    drawing.current = false
    const canvas = canvasRef.current
    if (!canvas) return
    onChange?.(empty.current ? null : canvas.toDataURL())
  }, [onChange])

  useEffect(() => {
    const canvas = canvasRef.current
    if (!canvas) return
    canvas.addEventListener("mousedown", startDraw)
    canvas.addEventListener("mousemove", draw)
    canvas.addEventListener("mouseup", endDraw)
    canvas.addEventListener("mouseleave", endDraw)
    canvas.addEventListener("touchstart", startDraw, { passive: false })
    canvas.addEventListener("touchmove", draw, { passive: false })
    canvas.addEventListener("touchend", endDraw)
    return () => {
      canvas.removeEventListener("mousedown", startDraw)
      canvas.removeEventListener("mousemove", draw)
      canvas.removeEventListener("mouseup", endDraw)
      canvas.removeEventListener("mouseleave", endDraw)
      canvas.removeEventListener("touchstart", startDraw)
      canvas.removeEventListener("touchmove", draw)
      canvas.removeEventListener("touchend", endDraw)
    }
  }, [startDraw, draw, endDraw])

  const clear = () => {
    const canvas = canvasRef.current
    if (!canvas) return
    const ctx = canvas.getContext("2d")
    ctx?.clearRect(0, 0, canvas.width, canvas.height)
    empty.current = true
    onChange?.(null)
    onClear?.()
  }

  return (
    <div
      data-slot="signature-pad"
      data-disabled={disabled || undefined}
      className={cn(
        "inline-flex flex-col overflow-hidden rounded-lg border bg-background data-[disabled]:opacity-50",
        className
      )}
      {...props}
    >
      <canvas
        ref={canvasRef}
        width={width}
        height={height}
        className="block touch-none bg-card"
        style={{ width, height }}
      />
      <div className="flex items-center justify-between border-t bg-muted/30 px-3 py-1.5">
        <span className="text-xs text-muted-foreground">Sign above</span>
        <Button
          type="button"
          variant="ghost"
          size="xs"
          onClick={clear}
          disabled={disabled}
        >
          Clear
        </Button>
      </div>
    </div>
  )
}

export { SignaturePad }
export type { SignaturePadProps }
