'use client'

import { useState, type ReactElement } from 'react'
import { useDeepCompareEffect } from '@react-hookz/web'

import type { GDSComponentProps } from '@graphprotocol/gds-css'
import avatarPlaceholderRounded from '@graphprotocol/gds-react/assets/avatar-placeholder-rounded.png'
import { createIdenticon, PLACEHOLDER_ADDRESS } from '@graphprotocol/gds-utils'

import { useCSSPropsPolyfill, useGDS } from '../hooks/index.ts'
import { cn, getAssetUrl, getCSSPropsAttributes, isReactNode } from '../utils/index.ts'
import { AvatarMeta } from './Avatar.meta.ts'
import { MaybeButtonOrLink, type MaybeButtonOrLinkProps } from './base/MaybeButtonOrLink.tsx'
import { Tooltip, type TooltipProps } from './Tooltip.tsx'

const PLACEHOLDER_SRC_CIRCLE = createIdenticon(PLACEHOLDER_ADDRESS)
const PLACEHOLDER_SRC_ROUNDED = getAssetUrl(avatarPlaceholderRounded)

export declare namespace AvatarProps {
  interface OwnProps {
    /**
     * Image source URL or array of URLs to try as fallbacks. If an array is provided, each URL will
     * be tried in order until one loads successfully. Alternatively, this can be a React element
     * (e.g. an icon) for custom avatar content.
     */
    src: string | string[] | ReactElement | undefined
    /**
     * Text used as the avatar's accessible name (e.g. the username or address of the represented
     * user). Necessary if the avatar is clickable, recommended otherwise (unless the same
     * information is already present as visible text nearby, then this should be an empty string).
     */
    alt: string | undefined
    /** For clickable avatars, defaults to `alt`, otherwise defaults to `undefined` */
    tooltip?: TooltipProps['content'] | Omit<TooltipProps, 'children'> | undefined
  }
  interface CommonProps extends OwnProps, GDSComponentProps<typeof AvatarMeta> {}
  interface ButtonProps extends CommonProps, Omit<MaybeButtonOrLinkProps.ButtonProps, 'children'> {}
  interface LinkProps extends CommonProps, Omit<MaybeButtonOrLinkProps.LinkProps, 'children'> {}
  interface OtherElementProps
    extends CommonProps, Omit<MaybeButtonOrLinkProps.OtherElementProps, 'children'> {}
}

export type AvatarProps =
  | AvatarProps.ButtonProps
  | AvatarProps.LinkProps
  | AvatarProps.OtherElementProps

export function Avatar({
  ref: passedRef,
  shape,
  size,
  src: passedSrc,
  alt,
  tooltip,
  className,
  style,
  ...props
}: AvatarProps) {
  useGDS()

  const [cssPropsPolyfillPassedRef, cssPropsPolyfillAttributes, cssProps] = useCSSPropsPolyfill(
    AvatarMeta,
    { shape, size },
    { ref: passedRef, returnPropValues: { shape } },
  )

  const sourcesToTryInOrder = passedSrc ? (Array.isArray(passedSrc) ? passedSrc : [passedSrc]) : []
  const [currentSourceIndex, setCurrentSourceIndex] = useState(0)
  const src =
    sourcesToTryInOrder[currentSourceIndex] ??
    (cssProps.shape === 'circle' ? PLACEHOLDER_SRC_CIRCLE : PLACEHOLDER_SRC_ROUNDED)

  /**
   * Reset to first URL when `passedSrc` changes (using deep comparison so that a different array
   * with the same values as the previous one doesn't trigger a reset).
   */
  useDeepCompareEffect(() => {
    setCurrentSourceIndex(0)
  }, [sourcesToTryInOrder])

  return (
    <MaybeButtonOrLink
      ref={cssPropsPolyfillPassedRef}
      className={cn(
        'group/avatar peer/avatar gds-avatar root-flex u:shrink-0 u:outline-0 i:size-max',
        className,
      )}
      {...getCSSPropsAttributes(AvatarMeta, { shape, size }, style)}
      {...cssPropsPolyfillAttributes}
      {...props}
      render={(renderProps, { Element, elementProps, category }) => {
        return (
          <Tooltip
            content={category !== 'other' ? alt : undefined}
            {...(isReactNode(tooltip) ? { content: tooltip } : tooltip)}
            triggerProps={renderProps}
          >
            <Element {...elementProps}>
              <span className="nested/avatar-paint pointer-events-none shrink-0">
                <span
                  className={`
                    block overflow-clip bg-default
                    group-clickable-focus-visible/avatar:outline
                    @prop-size-xsmall/avatar:size-4
                    @prop-size-small/avatar:size-5
                    @prop-size-medium/avatar:size-6
                    @prop-size-large/avatar:size-8
                    @prop-size-xlarge/avatar:size-12
                    @prop-shape-circle/avatar:rounded-full
                    @prop-shape-rounded/avatar:rounded-4
                    @prop-shape-rounded/avatar:@prop-size-xsmall/avatar:rounded-2
                    @prop-shape-rounded/avatar:@prop-size-xlarge/avatar:rounded-8
                    uu:*:absolute
                    uu:*:inset-0
                    uu:*:m-auto
                    uu:*:size-full
                    uu:*:object-cover
                    i:@prop-size-xsmall/avatar:**:icon:default-size-2.5
                    i:@prop-size-small/avatar:**:icon:default-size-3
                    i:@prop-size-medium/avatar:**:icon:default-size-4
                    i:@prop-size-large/avatar:**:icon:default-size-5
                    i:@prop-size-xlarge/avatar:**:icon:default-size-8
                  `}
                >
                  {src ? (
                    typeof src === 'string' ? (
                      <img
                        src={src}
                        alt=""
                        onError={() => setCurrentSourceIndex(currentSourceIndex + 1)}
                        className="[[src^='data:image/bmp']]:[image-rendering:pixelated]"
                      />
                    ) : (
                      <Tooltip.Collector>{src}</Tooltip.Collector>
                    )
                  ) : null}
                  {alt ? <span className="sr-only">{alt}</span> : null}
                </span>
              </span>
            </Element>
          </Tooltip>
        )
      }}
    />
  )
}
