'use client'

import {
  createElement,
  isValidElement,
  type ComponentProps,
  type ComponentType,
  type ReactElement,
} from 'react'

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

import { useGDS } from '../hooks/index.ts'
import { cn, getCSSPropsAttributes } from '../utils/index.ts'
import { IconMeta } from './Icon.meta.ts'
import { useTooltipCollector } from './Tooltip.tsx'

export interface IconProps
  extends Omit<ComponentProps<'span'>, 'children' | 'color'>, GDSComponentProps<typeof IconMeta> {
  /**
   * Text used as the icon's accessible name. Required to make sure consumers consider accessibility
   * when manually rendering icons, but should be set to an empty string if the icon is purely
   * decorative, and also accepts `undefined` (same behavior as empty string) to support passing
   * icons as addons to components that handle accessibility on their own (e.g. `<Button
   * addonAfter={ArrowRight}>Continue</Button>`).
   */
  alt: string | undefined
}

export type IconSrc = ReactElement | ComponentType

export interface IconPropsWithSrc extends IconProps {
  src: IconSrc
}

export function Icon({ src, alt, size, color, className, style, ...props }: IconPropsWithSrc) {
  useGDS()

  const iconContent = isValidElement(src) ? src : createElement(src)
  const collectTooltipContent = useTooltipCollector()

  if (collectTooltipContent && alt) {
    collectTooltipContent(alt)
  }

  if ('variant' in props) {
    /**
     * Just in case TS failed to catch a stray `variant` prop (e.g. in `IconGallery.stories.tsx`),
     * we don't want to render it as an (invalid) HTML attribute.
     */
    delete props.variant
  }

  return (
    <span
      role={alt ? 'img' : undefined}
      aria-label={alt || undefined}
      className={cn(
        `gds-icon root-flex items-center justify-center
        ${/* Fix icons wobbling in Safari when rendered on top of opacity/background transitions */ ''}
        u:will-change-transform
        ${/* Flip some icons horizontally in RTL so that `ArrowLeft` points right, etc. */ ''}
        rtl:data-[name*=Left]:*:-scale-x-100
        rtl:data-[name*=Right]:*:-scale-x-100
        u:aspect-square
        ${/* Prevent the icon from being stretched vertically in a flex container, while allowing the height to be responsive (thanks to the `aspect-square`) and overridable (thanks to the unimportant variant) */ ''}
        u:h-max
        ${/* Make the `size` prop work, while allowing sizing icons with `w-*` or `size-*` utilities (thanks to the unimportant variant) */ ''}
        u:w-(--gds-icon-size)
        ${/* Prevent the icon from being squished in a flex container */ ''}
        u:shrink-0
        ${/* Make the `color` prop work, while allowing coloring icons with `text-*` utilities (thanks to the unimportant variant) */ ''}
        u:text-(--gds-icon-color)
        ${/* Make the SVG (or whatever element the inner icon is) fill this wrapper */ ''}
        u:*:w-full`,
        className,
      )}
      {...getCSSPropsAttributes(IconMeta, { size, color }, style)}
      {...props}
    >
      {iconContent}
    </span>
  )
}
