import * as React from "react";
import {
  iconImports,
  strokeIconNames,
  type IconImportName,
  type IconComponentProps,
} from "./dynamic-icon-imports.js";

export interface DynamicIconProps extends React.ComponentProps<"svg"> {
  /**
   * The name of the icon to render.
   * - Lucide icons: kebab-case (e.g., "camera", "chevron-down")
   * - Flag icons: ISO 3166-1 alpha-3 or PascalCase (e.g., "USA", "BasqueCountry")
   * - Social media icons: PascalCase (e.g., "FacebookOriginal", "TiktokNeutral")
   * - Custom icons: kebab-case (e.g., "bright-local-logo")
   */
  name: IconImportName | (string & {});
  /**
   * Icon size in pixels (applies to both width and height)
   * @default 16
   */
  size?: number;
  /**
   * Icon color (uses currentColor by default)
   */
  color?: string;
  /**
   * Stroke width for line icons (Lucide/custom only, ignored for flag/social media)
   * @default 1.33
   */
  strokeWidth?: number;
  /**
   * When true, keeps stroke width consistent regardless of icon size.
   * Only applies to Lucide and custom icons. Ignored for flag/social media icons.
   * @default true
   */
  absoluteStrokeWidth?: boolean;
  /**
   * Fallback content displayed while the icon is loading.
   * Defaults to an invisible placeholder that matches the icon's dimensions to prevent layout shift.
   */
  fallback?: React.ReactNode;
}

/**
 * Pre-create all lazy components at module load time.
 * React.lazy() still defers the actual icon import until the component is rendered,
 * maintaining code-splitting benefits. This only creates lightweight lazy wrappers.
 */
const lazyIcons: Record<
  string,
  React.LazyExoticComponent<React.ComponentType<IconComponentProps>>
> = {};

for (const [name, loader] of Object.entries(iconImports)) {
  if (loader) {
    lazyIcons[name] = React.lazy(
      loader as () => Promise<{
        default: React.ComponentType<IconComponentProps>;
      }>
    );
  }
}

/**
 * DynamicIcon component for rendering any icon by name at runtime.
 *
 * Supports Lucide icons, custom BrightLocal icons, flag icons (ISO 3166-1 alpha-3),
 * and social media icons — all resolved from a single unified registry.
 *
 * @example
 * // Lucide icon
 * <DynamicIcon name="camera" size={24} color="red" />
 *
 * @example
 * // Flag icon
 * <DynamicIcon name="USA" size={24} />
 *
 * @example
 * // Social media icon
 * <DynamicIcon name="FacebookOriginal" size={32} />
 *
 * @example
 * // With custom loading fallback
 * <DynamicIcon name="camera" size={24} fallback={<Skeleton />} />
 *
 * @example
 * // Opt out of default placeholder
 * <DynamicIcon name="camera" size={24} fallback={null} />
 *
 * @example
 * // Database-driven
 * <DynamicIcon name={country.iso} size={16} />
 */
export const DynamicIcon = React.forwardRef<SVGSVGElement, DynamicIconProps>(
  (
    {
      name,
      size = 16,
      strokeWidth = 1.33,
      absoluteStrokeWidth = true,
      fallback,
      ...props
    },
    ref
  ) => {
    const Icon = lazyIcons[name];

    if (!Icon) {
      console.warn(`Icon "${name}" not found in icon registry`);
      return null;
    }

    // Only pass stroke-specific props to icons that support them.
    // Flag and social media icons spread props onto <svg> and would
    // trigger React warnings for unknown DOM attributes like absoluteStrokeWidth.
    const iconProps: IconComponentProps = strokeIconNames.has(name)
      ? { ref, size, strokeWidth, absoluteStrokeWidth, ...props }
      : { ref, size, ...props };

    const placeholder =
      fallback !== undefined ? (
        fallback
      ) : (
        <span
          aria-hidden="true"
          style={{
            display: "inline-block",
            width: size,
            height: size,
            flexShrink: 0,
          }}
        />
      );

    return (
      <React.Suspense fallback={placeholder}>
        <Icon {...iconProps} />
      </React.Suspense>
    );
  }
);

DynamicIcon.displayName = "DynamicIcon";
