'use client';

import * as React from 'react';
import NextImage from 'next/image';
import { cn } from '@/lib/utils';
import { demoImage, type DemoCategory } from '@/lib/demo-images';
import { BrandedPlaceholder } from './branded-placeholder';

export interface SmartImageProps {
  /** Primary image URL from backend. May be null/undefined/empty. */
  src?: string | null;
  /** Required alt text. Pass empty string for decorative images. */
  alt: string;
  /** Demo category — picks a decorative fallback and the placeholder icon. */
  fallbackCategory?: DemoCategory;
  /** Stable seed (e.g. service public_id) so a decorative fallback is deterministic. */
  fallbackSeed?: string;
  /** Explicit fallback URL takes precedence over the category demo image. */
  fallbackSrc?: string;
  /**
   * B3 — real-entity mode. When true, a missing or broken `src` renders a
   * branded placeholder (initial + category icon) instead of a curated stock
   * photo, so a marketing image is never shown as a real vendor/service's media.
   */
  entity?: boolean;
  /** First-letter label for the branded placeholder (the real entity's name). */
  placeholderLabel?: string;
  /** Show a neutral skeleton until first paint. */
  showSkeleton?: boolean;
  /** Wrapper class — defaults to the container. */
  className?: string;
  /** Class for the rendered image. */
  imgClassName?: string;
  /** next/image responsive `sizes` hint. */
  sizes?: string;
  loading?: 'lazy' | 'eager';
  /** Mark as LCP (skips lazy-loading). */
  priority?: boolean;
  /** Escape hatch for hosts the Next optimizer can't handle. */
  unoptimized?: boolean;
  'aria-hidden'?: boolean;
  id?: string;
  style?: React.CSSProperties;
}

/**
 * Drop-in image with:
 *   - C1: served through `next/image` (responsive srcset, AVIF/WebP, no CLS).
 *   - graceful fallback: decorative usages pivot to a curated demo image on a
 *     missing/broken src; real entities (`entity`) pivot to a branded placeholder.
 *   - a neutral skeleton until first paint; never a broken-image icon.
 *
 * Uses `fill`, so the wrapper must be sized (every call site passes a sized
 * `className`, e.g. `h-full w-full` inside an aspect-ratio box, or `h-20 w-20`).
 */
export function SmartImage({
  src,
  alt,
  fallbackCategory = 'generic',
  fallbackSeed,
  fallbackSrc,
  entity = false,
  placeholderLabel,
  showSkeleton = true,
  className,
  imgClassName,
  sizes = '100vw',
  loading = 'lazy',
  priority,
  unoptimized,
  'aria-hidden': ariaHidden,
  id,
  style,
}: SmartImageProps) {
  const hasSrc = typeof src === 'string' && src.trim().length > 0;
  const demoFallback = fallbackSrc ?? demoImage(fallbackCategory, fallbackSeed);

  const [errored, setErrored] = React.useState(false);
  const [loaded, setLoaded] = React.useState(false);

  // Reset state when the src prop changes (list re-use).
  React.useEffect(() => {
    setErrored(false);
    setLoaded(false);
  }, [src]);

  // B3 — real entity with no usable image (or a broken one): branded placeholder.
  if (entity && (!hasSrc || errored)) {
    return (
      <BrandedPlaceholder
        label={placeholderLabel}
        category={fallbackCategory}
        alt={alt || undefined}
        className={cn('block', className)}
      />
    );
  }

  // Decorative: use the demo image when no src, and pivot to it once on error.
  const finalSrc = hasSrc && !errored ? (src as string) : demoFallback;

  // next/image `fill` needs a positioned ancestor. Respect an explicit
  // position in `className` (overlay heroes use `absolute inset-0`); otherwise
  // establish one with `relative`.
  const hasPosition = /(?:^|\s)(absolute|fixed|sticky|relative)(?:\s|$)/.test(className ?? '');

  return (
    <span className={cn(!hasPosition && 'relative', 'block overflow-hidden', className)}>
      {showSkeleton && !loaded ? (
        <span aria-hidden className="absolute inset-0 bg-neutral-100" />
      ) : null}
      <NextImage
        src={finalSrc}
        alt={alt}
        fill
        sizes={sizes}
        priority={priority}
        loading={priority ? undefined : loading}
        unoptimized={unoptimized}
        aria-hidden={ariaHidden}
        id={id}
        style={style}
        onLoad={() => setLoaded(true)}
        onError={() => setErrored(true)}
        className={cn(
          'transition-opacity duration-300',
          loaded ? 'opacity-100' : 'opacity-0',
          imgClassName,
        )}
      />
    </span>
  );
}
