import { Image, ImageProps, Skeleton } from "antd";
import {
  CSSProperties,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import styles from "./ImageWithFallback.module.css";

/**
 * Custom hook for handling lazy loading with Intersection Observer
 * @param options Configuration options for the hook
 * @returns Object containing ref and isInView state
 */
const useLazyLoading = ({
  lazy = true,
  priority = false,
}: {
  lazy?: boolean;
  priority?: boolean;
}) => {
  const [isInView, setIsInView] = useState<boolean>(!lazy || priority);
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    // Skip lazy loading if priority is true or lazy is false
    if (lazy && !priority) {
      const observer = new IntersectionObserver(
        ([entry]) => {
          if (entry.isIntersecting) {
            setIsInView(true);
            observer.disconnect();
          }
        },
        {
          rootMargin: "50px", // Start loading 50px before the image comes into view
          threshold: 0.1,
        },
      );

      const currentRef = ref.current;
      if (currentRef) {
        observer.observe(currentRef);
      }

      // Cleanup function
      return () => {
        if (currentRef) {
          observer.unobserve(currentRef);
        }
        observer.disconnect();
      };
    } else {
      setIsInView(true);
    }
  }, [lazy, priority]);

  return { ref, isInView };
};

/**
 * Props for the ImageWithFallback component
 * @extends ImageProps from Ant Design's Image component
 */
interface ImageWithFallbackProps extends ImageProps {
  /** URL for the fallback image to display when the main image fails to load */
  fallbackSrc: string;
  /** Border radius for the image (can be number in pixels or string like '8px' or '50%') */
  borderRadius?: number | string;
  /** If true, loads the image immediately without lazy loading */
  priority?: boolean;
  /** If true (default), enables lazy loading of the image */
  lazy?: boolean;
  /** Type of placeholder to show while loading (not fully implemented) */
  placeholder?: "blur" | "empty";
  /** Base64 data URL for blur placeholder (not fully implemented) */
  blurDataURL?: string;
  /** Width of the image (number for pixels, string for other units) */
  width?: number | string;
  /** Height of the image (number for pixels, string for other units) */
  height?: number | string;
  /** Additional styles for the loading container */
  loadingContainerStyle?: CSSProperties;
}

/**
 * Enhanced image component with fallback support, lazy loading, and loading states
 *
 * Features:
 * - Displays a fallback image when the main image fails to load
 * - Supports lazy loading using Intersection Observer API
 * - Shows loading skeleton while the image is loading
 * - Handles various styling options including border radius
 * - Optimized with React.memo to prevent unnecessary re-renders
 */
const ImageWithFallback = ({
  fallbackSrc,
  src,
  borderRadius,
  style,
  alt = "Image", // Default alt text for better accessibility
  priority = false,
  lazy = true,
  width,
  height,
  loadingContainerStyle,
  ...rest
}: ImageWithFallbackProps) => {
  const [imgSrc, setImgSrc] = useState<string>(src || fallbackSrc);
  const [isLoading, setIsLoading] = useState<boolean>(true);
  const [hasError, setHasError] = useState<boolean>(false);

  // Use the custom lazy loading hook
  const { ref: imageRef, isInView } = useLazyLoading({ lazy, priority });

  // Default width value for consistency
  const defaultWidth = 90;

  /**
   * Handle image load error
   * Sets the fallback image and updates loading/error states
   */
  const handleError = useCallback(() => {
    setImgSrc(fallbackSrc);
    setHasError(true);
    setIsLoading(false);
  }, [fallbackSrc]);

  /**
   * Handle successful image load
   * Updates loading and error states
   */
  const handleLoad = useCallback(() => {
    setIsLoading(false);
    setHasError(false);
  }, []);

  // Update imgSrc when src or fallbackSrc props change
  useEffect(() => {
    setImgSrc(src || fallbackSrc);
    setIsLoading(true);
    setHasError(false);
  }, [src, fallbackSrc]);

  // Memoize combined style to prevent unnecessary re-renders
  const combinedStyle: CSSProperties = useMemo(
    () => ({
      ...style,
      ...(borderRadius && { borderRadius }),
    }),
    [style, borderRadius],
  );

  /**
   * Memoized skeleton loader component
   */
  const SkeletonLoader = useMemo(
    () => (
      <Skeleton.Image
        active
        style={{
          border: "none",
          height,
          width: width || defaultWidth,
          ...loadingContainerStyle,
        }}
      />
    ),
    [height, width, defaultWidth, loadingContainerStyle],
  );

  // Memoize loading container styles for better performance
  const loadingContainerStyles: CSSProperties = useMemo(
    () => ({
      zIndex: 1,
      borderRadius: borderRadius || "8px",
      height,
      width: width || defaultWidth,
      textAlign: "center",
      ...loadingContainerStyle,
    }),
    [borderRadius, height, width, defaultWidth, loadingContainerStyle],
  );

  // Memoize image styles for better performance
  const imageStyles: CSSProperties = useMemo(
    () => ({
      opacity: isLoading ? 0 : 1,
      transition: "opacity 0.3s ease-in-out",
      objectFit: "cover",
      ...combinedStyle,
    }),
    [isLoading, combinedStyle],
  );

  // Don't render the image until it's in view (for lazy loading)
  if (lazy && !priority && !isInView) {
    return (
      <div
        ref={imageRef}
        className={styles.loadingContainer}
        style={{
          ...combinedStyle,
          height,
          width: width || defaultWidth,
        }}
        aria-label={`Loading ${alt}`}
        role="progressbar"
      >
        {SkeletonLoader}
      </div>
    );
  }

  return (
    <div
      ref={imageRef}
      style={{ position: "relative", height }}
      className={hasError ? styles.errorState : undefined}
      data-testid="image-with-fallback-container"
    >
      {/* Loading state */}
      {isLoading && !hasError && (
        <div
          className={`${styles.loadingContainer} ${styles.loadingState}`}
          style={loadingContainerStyles}
          aria-hidden="true"
        >
          <Skeleton.Image
            active
            style={{
              height,
              width: width || defaultWidth,
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              position: "absolute",
              top: "50%",
              left: "50%",
              transform: "translate(-50%, -50%)",
              ...loadingContainerStyle,
            }}
          />
        </div>
      )}

      {/* Main image */}
      <Image
        src={imgSrc}
        onError={handleError}
        onLoad={handleLoad}
        style={imageStyles}
        alt={alt}
        width={width}
        height={height}
        preview={false}
        aria-hidden={isLoading}
        data-testid="image-with-fallback"
        {...rest}
      />
    </div>
  );
};

export default ImageWithFallback;
