import { forwardRef, useEffect, useState } from 'react'

import type {
  UnityIllustrationAsset,
  UnityIllustrationName,
} from '../../generated/illustrationAssets'
import type {
  IllustratedIconProps,
  IllustratedPictureProps,
} from '../illustration/Illustration'

import { lazyIllustrationsMap } from '../../generated/illustrationAssets'
import {
  illustratedIcon,
  illustratedPicture,
  Illustration,
} from '../illustration/Illustration'

/**
 * Props for the LazyIllustration component.
 *
 * This type is a union of two possible prop types:
 * 1. Props for the picture variant (omitting 'src' and adding 'name')
 * 2. Props for the icon variant (omitting 'src' and adding 'name')
 * @property {UnityIllustrationName} name - The name of the illustration to load dynamically.
 *   This must be one of the valid illustration names from the Unity illustration library.
 * @property {'picture' | 'icon'} variant - The illustration variant. It can be a regular picture or an illustrated icon.
 * @property {string} alt - Accessible name for the illustration (required when isDecorative is false or undefined).
 * @property {string} [description] - Optional description for complex illustrations.
 * @property {boolean} [isDecorative] - Whether this is decorative (hidden from screen readers).
 * @property {'sm' | 'md' | 'lg'} [size] - The size of the illustration. Required when variant is 'icon'.
 */
export type LazyIllustrationProps =
  | (Omit<IllustratedPictureProps, 'src'> & { name: UnityIllustrationName })
  | (Omit<IllustratedIconProps, 'src'> & { name: UnityIllustrationName })

/**
 * A component that lazily loads illustrations by name and renders them using the Illustration component.
 *
 * The LazyIllustration component dynamically imports illustrations based on their name using the
 * lazyIllustrationsMap from the generated illustrationAssets file. This allows for code-splitting
 * and on-demand loading of illustrations, which can significantly improve initial load performance.
 * @param {LazyIllustrationProps} props - Component props including name, variant, and standard HTML attributes
 * @see {@link LazyIllustrationProps} for all available props
 * @example
 * ```tsx
 * import { LazyIllustration } from '@payfit/unity-illustrations'
 *
 * function Example() {
 *   return (
 *     <LazyIllustration
 *       variant="picture"
 *       name="FAQ"
 *       className="uy:size-[150px]"
 *       alt="FAQ illustration"
 *     />
 *   )
 * }
 * ```
 * @example
 * ```tsx
 * // Using with icon variant
 * import { LazyIllustration } from '@payfit/unity-illustrations'
 *
 * function IconExample() {
 *   return (
 *     <LazyIllustration
 *       variant="icon"
 *       size="md"
 *       name="Binoculars"
 *       alt="Binoculars icon"
 *     />
 *   )
 * }
 * ```
 * @remarks
 * Unlike the Illustration component, you don't need to import illustration assets individually.
 * Simply provide the name of the illustration you want to use, and LazyIllustration will handle
 * the dynamic import for you. The component returns null during loading, so you may want to
 * provide your own loading state or placeholder.
 */
export const LazyIllustration = forwardRef<
  HTMLImageElement,
  LazyIllustrationProps
>(({ name, ...props }, ref) => {
  const [illustration, setIllustration] =
    useState<UnityIllustrationAsset | null>(null)
  const [isLoading, setIsLoading] = useState(true)

  useEffect(() => {
    const loadIllustration = async () => {
      try {
        setIsLoading(true)
        const importFn = lazyIllustrationsMap[name]

        const module = await importFn()
        setIllustration(module.default)
      } catch (error) {
        console.error(`Failed to load illustration "${name}":`, error)
      } finally {
        setIsLoading(false)
      }
    }

    void loadIllustration()
  }, [name])

  if (isLoading || !illustration) {
    const clsx =
      props.variant === 'picture'
        ? illustratedPicture({ className: props.className })
        : illustratedIcon({
            className: props.className,
            size: (props as IllustratedIconProps).size,
          })
    return <div className={clsx} />
  }

  // Handle the discriminated union by using type assertion
  // This is safe because LazyIllustrationProps inherits from IllustrationProps (excluding src and name)
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  return <Illustration ref={ref} src={illustration} {...(props as any)} />
})

LazyIllustration.displayName = 'LazyIllustration'
