import React, { useState, useEffect, useMemo } from 'react';
import Image, { ImageProps } from 'next/image';
import { useQuery } from '@tanstack/react-query';
import { DefaultService } from '@lib/api';
import { ImageOff, RefreshCw } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Skeleton } from '@/components/ui/skeleton';
import { cn } from '@/utils';

interface SecureImageProps extends Omit<ImageProps, 'src' | 'alt'> {
  src?: string;
  alt?: string;
  onSrcRefreshed?: (newSrc: string) => void;
  showErrorState?: boolean;
}

export const SecureImage = React.forwardRef<HTMLImageElement, SecureImageProps>(({ 
  src: initialSrc, 
  alt, 
  className, 
  onError, 
  onSrcRefreshed, 
  onLoad,
  width,
  height,
  showErrorState = true,
  ...props 
}, ref) => {
  const [shouldFetchSignedUrl, setShouldFetchSignedUrl] = useState(false);
  const [hasFinalError, setHasFinalError] = useState(false);
  // Track the last source that successfully loaded (or is currently trying to load)
  // We use this to manage the loading skeleton state
  const [loadedSrc, setLoadedSrc] = useState<string | null>(null);
  // Track if we've already notified parent to prevent infinite loops
  const hasNotifiedRef = React.useRef(false);

  // Reset state when initialSrc changes，并根据 URL 类型决定是否需要"预签名"
  useEffect(() => {
    // 本地或 data URI 不需要签名
    const isLocalLike =
      !!initialSrc &&
      (initialSrc.startsWith('/files/') ||
        initialSrc.startsWith('http://localhost') ||
        initialSrc.startsWith('https://localhost') ||
        initialSrc.startsWith('data:'));

    const shouldAutoSign =
      !!initialSrc &&
      initialSrc.startsWith('http') &&
      !isLocalLike;

    setShouldFetchSignedUrl(shouldAutoSign);
    setHasFinalError(false);
    setLoadedSrc(null);
    hasNotifiedRef.current = false;
  }, [initialSrc]);

  // Query for signed URL
  const { 
    data: signedUrlData, 
    isLoading: isSigning,
    isError: isSignError
  } = useQuery({
    queryKey: ['signed-url', initialSrc],
    queryFn: async () => {
      if (!initialSrc) throw new Error('No source provided');
      const response = await DefaultService.signUrlEndpoint({ url: initialSrc });
      // BaseResponse structure: { code, message, data: { url: ... } }
      const data = (response as any).data?.data;
      return data;
    },
    enabled: !!initialSrc && shouldFetchSignedUrl,
    staleTime: 1000 * 60 * 5, // Cache signed URL for 5 minutes
    retry: 1,
  });

  const effectiveSrc = useMemo(() => {
    if (signedUrlData?.url && signedUrlData.url !== initialSrc) {
      return signedUrlData.url;
    }
    return initialSrc;
  }, [initialSrc, signedUrlData]);

  // Notify parent if source refreshed (only once to prevent infinite loops)
  useEffect(() => {
    if (signedUrlData?.url && onSrcRefreshed && !hasNotifiedRef.current) {
      hasNotifiedRef.current = true;
      onSrcRefreshed(signedUrlData.url);
    }
  }, [signedUrlData, onSrcRefreshed]);

  const handleImageError = (e: React.SyntheticEvent<HTMLImageElement, Event>) => {
    if (onError) {
      onError(e);
    }

    // If we haven't tried signing yet, try it now
    if (!shouldFetchSignedUrl && initialSrc) {
      setShouldFetchSignedUrl(true);
    } else {
      // We already tried signing (or query failed), or no src
      setHasFinalError(true);
    }
  };

  const handleImageLoad = (e: React.SyntheticEvent<HTMLImageElement, Event>) => {
    setLoadedSrc(effectiveSrc || null);
    if (onLoad) {
      onLoad(e);
    }
  };

  const handleManualRetry = (e: React.MouseEvent) => {
    e.stopPropagation();
    // Resetting state to trigger re-render and potential re-fetch if needed
    // In React Query, we might want to invalidate the query if we think it's stale,
    // but here manual retry usually means "try the whole flow again"
    setHasFinalError(false);
    setShouldFetchSignedUrl(false);
    setLoadedSrc(null);
    // If we want to force re-sign, we might need to invalidate the query
    // But for now, resetting local state restarts the "try original -> try signed" flow
  };

  // Determine if we are visually loading
  // 1. We are waiting for the signing query
  // 2. OR The image source has changed but hasn't triggered onLoad yet
  const isLoading = isSigning || (effectiveSrc !== loadedSrc && !hasFinalError);

  // 如果打算预签名但签名结果尚未返回，先不渲染 Image，避免用旧 URL 触发 403
  const shouldDelayInitialLoad = shouldFetchSignedUrl && !signedUrlData && !hasFinalError;

  // Determine layout mode: if width/height are provided, use them; otherwise use fill
  const isFill = !width && !height && !props.fill;

  if (hasFinalError && showErrorState) {
    return (
      <div 
        className={cn("flex flex-col items-center justify-center bg-muted/50 rounded-md text-muted-foreground gap-2", isFill ? "w-full h-full" : "", className)}
        style={{ width: width || undefined, height: height || undefined, minHeight: isFill ? undefined : '100px' }}
      >
        <ImageOff className="w-8 h-8 opacity-50" />
        <span className="text-xs">加载失败</span>
        <Button 
          variant="ghost" 
          size="sm" 
          className="h-6 px-2 text-xs gap-1 hover:bg-muted"
          onClick={handleManualRetry}
        >
          <RefreshCw className="w-3 h-3" />
          重试
        </Button>
      </div>
    );
  }

  return (
    <div className={cn("relative overflow-hidden", isFill ? "w-full h-full" : "", className)} style={{ width, height }}>
      {isLoading && (
        <Skeleton className="absolute inset-0 w-full h-full z-10" />
      )}
      {effectiveSrc && !shouldDelayInitialLoad && (
        <Image
          ref={ref}
          src={effectiveSrc}
          alt={alt || ''}
          width={width}
          height={height}
          fill={isFill}
          className={cn(
            "transition-opacity duration-300", 
            isLoading ? "opacity-0" : "opacity-100",
            // Pass className to Image as well to support object-fit/cover utilities
            className
          )}
          onError={handleImageError}
          onLoad={handleImageLoad}
          {...props}
        />
      )}
    </div>
  );
});

SecureImage.displayName = 'SecureImage';
