/**
 * CanvasBackground Component
 *
 * Background image, video, and audio for the constructor canvas.
 * Handles blob URLs, Next.js Image optimization, and previous background overlay.
 * Supports custom video playback settings (autoplay, loop, muted, start/end time).
 */

import React, {
  useRef,
  useEffect,
  useState,
  useMemo,
  useCallback,
} from 'react';
import NextImage from 'next/image';
import { useBackgroundVideoPlayback } from '../../hooks/useBackgroundVideoPlayback';
import PreviousBackgroundOverlay from '../PreviousBackgroundOverlay';
import { scheduleAfterPaint } from '../../lib/browserUtils';
import { baseURLApi } from '../../config';

// Type for requestVideoFrameCallback (Safari 15.4+, Chrome 83+)
// The callback receives (now: DOMHighResTimeStamp, metadata: VideoFrameCallbackMetadata)
// but we ignore them since we only need to know the frame was painted
interface HTMLVideoElementWithRVFC extends HTMLVideoElement {
  requestVideoFrameCallback: (
    callback: (
      now: DOMHighResTimeStamp,
      metadata: VideoFrameCallbackMetadata,
    ) => void,
  ) => number;
}

interface CanvasBackgroundProps {
  backgroundImageUrl?: string;
  backgroundVideoUrl?: string;
  backgroundAudioUrl?: string;
  previousBgImageUrl?: string;
  previousBgVideoUrl?: string;
  isSwitching?: boolean;
  isNewBgReady?: boolean;
  onBackgroundReady?: () => void;
  // Video playback settings
  videoAutoplay?: boolean;
  videoLoop?: boolean;
  videoMuted?: boolean;
  videoStartTime?: number | null;
  videoEndTime?: number | null;
  /** Original storage path for video - used for play-once tracking (not the resolved blob URL) */
  videoStoragePath?: string;
}

const CanvasBackground: React.FC<CanvasBackgroundProps> = ({
  backgroundImageUrl,
  backgroundVideoUrl,
  backgroundAudioUrl,
  previousBgImageUrl,
  previousBgVideoUrl,
  isSwitching = false,
  isNewBgReady = false,
  onBackgroundReady,
  videoAutoplay = true,
  videoLoop = true,
  videoMuted = true,
  videoStartTime = null,
  videoEndTime = null,
  videoStoragePath,
}) => {
  // Use background video playback hook for custom start/end time handling
  // Use storagePath for play-once tracking (falls back to videoUrl if not provided)
  const { videoRef, shouldBlockAutoplay } = useBackgroundVideoPlayback({
    videoUrl: backgroundVideoUrl,
    videoStoragePath: videoStoragePath || backgroundVideoUrl,
    autoplay: videoAutoplay,
    loop: videoLoop,
    muted: videoMuted,
    startTime: videoStartTime,
    endTime: videoEndTime,
  });

  // Block autoplay if video already played this session (when loop=false)
  const effectiveAutoplay = videoAutoplay && !shouldBlockAutoplay;

  // Video error state for fallback to proxy URL
  const [videoError, setVideoError] = useState(false);

  // Fallback to proxy URL if presigned URL fails (e.g., CORS, expiration)
  const videoSrc = useMemo(() => {
    if (!backgroundVideoUrl) return undefined;
    if (videoError && videoStoragePath) {
      // Fallback to backend proxy (bypasses CORS issues)
      return `${baseURLApi}/file/download?privateUrl=${encodeURIComponent(videoStoragePath)}`;
    }
    return backgroundVideoUrl;
  }, [backgroundVideoUrl, videoStoragePath, videoError]);

  // Reset error state when video URL changes
  useEffect(() => {
    setVideoError(false);
  }, [backgroundVideoUrl]);

  const handleVideoError = useCallback(() => {
    if (!videoError && videoStoragePath) {
      // eslint-disable-next-line no-console
      console.warn('[CanvasBackground] Video error, falling back to proxy URL');
      setVideoError(true);
    }
  }, [videoError, videoStoragePath]);

  const handleLoad = () => {
    // Wait for paint to ensure background is actually rendered before reporting ready.
    // This prevents the transition overlay from being removed before the background is visible.
    scheduleAfterPaint(() => {
      onBackgroundReady?.();
    });
  };

  const handleError = () => {
    onBackgroundReady?.();
  };

  // Track if we've already called onBackgroundReady to avoid double calls
  const didReportReadyRef = useRef(false);

  // Reset ready flag when video URL changes
  useEffect(() => {
    didReportReadyRef.current = false;
  }, [backgroundVideoUrl]);

  // Handle video first frame ready using requestVideoFrameCallback
  // This ensures the video's first frame is actually painted before we report ready
  useEffect(() => {
    const video = videoRef.current;
    if (!backgroundVideoUrl || !video || didReportReadyRef.current) return;

    const reportVideoReady = () => {
      if (didReportReadyRef.current) return;
      didReportReadyRef.current = true;
      onBackgroundReady?.();
    };

    // Timeout fallback - report ready after 5 seconds even if video hasn't started
    // Prevents infinite loading on slow networks or video initialization failures
    const timeout = setTimeout(() => {
      // eslint-disable-next-line no-console
      console.warn(
        '[CanvasBackground] Video ready timeout, reporting ready anyway',
      );
      reportVideoReady();
    }, 5000);

    // Use requestVideoFrameCallback for precise frame-level timing (Safari 15.4+, Chrome 83+)
    const videoWithRVFC = video as HTMLVideoElementWithRVFC;
    if (typeof videoWithRVFC.requestVideoFrameCallback === 'function') {
      videoWithRVFC.requestVideoFrameCallback(() => {
        clearTimeout(timeout);
        reportVideoReady();
      });
    } else {
      // Fallback: use playing event + scheduleAfterPaint
      const onPlaying = () => {
        clearTimeout(timeout);
        scheduleAfterPaint(() => {
          reportVideoReady();
        });
      };

      video.addEventListener('playing', onPlaying, { once: true });
      return () => {
        clearTimeout(timeout);
        video.removeEventListener('playing', onPlaying);
      };
    }

    return () => clearTimeout(timeout);
  }, [backgroundVideoUrl, onBackgroundReady]);

  // When endTime is set, we disable native loop and handle it via the hook
  const useNativeLoop = videoEndTime == null ? videoLoop : false;

  return (
    <>
      {/* Background image - z-1 keeps it below backdrop blur layer (z-5) */}
      {backgroundImageUrl && (
        <div className='pointer-events-none absolute inset-0 z-1 h-full w-full select-none'>
          {backgroundImageUrl.startsWith('blob:') ? (
            // eslint-disable-next-line @next/next/no-img-element
            <img
              key={`bg_image_${backgroundImageUrl}`}
              src={backgroundImageUrl}
              alt='Background'
              className='absolute inset-0 h-full w-full object-contain'
              draggable={false}
              onLoad={handleLoad}
              onError={handleError}
            />
          ) : (
            <NextImage
              key={`bg_image_${backgroundImageUrl}`}
              src={backgroundImageUrl}
              alt='Background'
              fill
              sizes='100vw'
              className='object-contain'
              draggable={false}
              unoptimized
              onLoad={handleLoad}
              onError={handleError}
            />
          )}
        </div>
      )}

      {/* Previous background overlay - shows during loading (z-2) above new background (z-1).
          Black overlay for fade effect is rendered separately at higher z-index (TransitionBlackOverlay). */}
      <PreviousBackgroundOverlay
        imageUrl={previousBgImageUrl}
        videoUrl={previousBgVideoUrl}
        isSwitching={isSwitching}
        isNewBgReady={isNewBgReady}
      />

      {/* Background video - z-1 keeps it below backdrop blur layer (z-5)
          Note: muted attribute is always true for iOS autoplay compatibility.
          Actual muted state is controlled via useBackgroundVideoPlayback hook
          which sets video.muted property via JavaScript (useEffect).
          webkit-playsinline is legacy attribute for older iOS versions.
          preload="metadata" is required for iOS Safari video initialization. */}
      {backgroundVideoUrl && (
        <video
          ref={videoRef}
          key={`bg_video_${backgroundVideoUrl}`}
          className='absolute inset-0 z-1 h-full w-full object-contain'
          src={videoSrc}
          preload='metadata'
          autoPlay={effectiveAutoplay}
          loop={useNativeLoop}
          muted={videoMuted}
          playsInline
          webkit-playsinline=''
          onError={handleVideoError}
        />
      )}

      {/* Background audio */}
      {backgroundAudioUrl && (
        <audio
          key={`bg_audio_${backgroundAudioUrl}`}
          src={backgroundAudioUrl}
          autoPlay
          loop
          hidden
        />
      )}
    </>
  );
};

export default CanvasBackground;
