'use client'

import { useRef, useState } from 'react'
import { useMergedRefs } from '@base-ui/utils/useMergedRefs'

import type { GDSComponentProps } from '@graphprotocol/gds-css'
import { CopyInteractiveIcon } from '@graphprotocol/gds-react/icons'

import { useGDS } from '../hooks/index.ts'
import { cn, isReactNode } from '../utils/index.ts'
import { Button, type ButtonProps } from './Button.tsx'
import type { CopyButtonMeta } from './CopyButton.meta.ts'
import { Tooltip } from './Tooltip.tsx'

export interface CopyButtonProps
  extends
    Omit<ButtonProps.ButtonProps, 'onCopy' | 'variant'>,
    GDSComponentProps<typeof CopyButtonMeta> {
  /** The text to copy. */
  value: string
  onCopy?: ((value: string) => void) | undefined
}

export function CopyButton({
  ref: passedRef,
  value,
  addonAfter,
  tooltip,
  onCopy,
  className,
  children,
  ...props
}: CopyButtonProps) {
  useGDS()

  const buttonRef = useRef<HTMLButtonElement>(null)
  const buttonPassedRef = useMergedRefs(buttonRef, passedRef)

  const [copied, privateSetCopied] = useState(false)
  const copiedTimeoutRef = useRef<number>(undefined)

  const setCopied = (newCopied: boolean) => {
    window.clearTimeout(copiedTimeoutRef.current)
    privateSetCopied(newCopied)
    if (newCopied) {
      setTooltipOpen(true)
      copiedTimeoutRef.current = window.setTimeout(() => setCopied(false), 1000)
    } else {
      // Ensure the original tooltip is shown again when leaving the button and coming back while copied
      copiedTimeoutRef.current = window.setTimeout(() => {
        if (!tooltipOpenRef.current && buttonRef.current?.matches(':hover, :focus-visible')) {
          setTooltipOpen(true)
        }
      }, 150)
    }
  }

  const hasChildren = children != null
  const copyIcon = <CopyInteractiveIcon copied={copied} />
  const tooltipProps = isReactNode(tooltip) ? { content: tooltip } : tooltip
  const [tooltipOpen, setTooltipOpen] = useState(false)
  const tooltipOpenRef = useRef(tooltipOpen)
  tooltipOpenRef.current = tooltipOpen

  return (
    <Tooltip
      {...tooltipProps}
      content="Copied!"
      {...(copied
        ? {
            open: true,
            onOpenChange: (newOpen, reason) => {
              if (reason === 'disabled') return
              setTooltipOpen(newOpen)
            },
            className: cn('i:prop-disabled-false', tooltipProps?.className),
          }
        : { disabled: true })}
    >
      <Button
        ref={buttonPassedRef}
        addonAfter={addonAfter !== undefined ? addonAfter : hasChildren ? copyIcon : undefined}
        tooltip={{
          open: tooltipOpen,
          onOpenChange: (newOpen) => {
            if (copied) return
            setTooltipOpen(newOpen)
          },
          ...tooltipProps,
        }}
        className={cn('gds-copy-button', className)}
        {...props}
        onClick={async (event) => {
          props.onClick?.(event)
          if (event.defaultPrevented) return
          await navigator.clipboard.writeText(value)
          onCopy?.(value)
          setCopied(true)
        }}
      >
        {hasChildren ? children : copyIcon}
      </Button>
    </Tooltip>
  )
}
