import React, {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';

import useDragLifecycle from '@/hooks/useDragLifecycle';
import useVideoFrames from '@/hooks/useVideoFrames';
import { calculateThumbnailDimensions } from '@/utils/videoAspectRatio';

import Filmstrip from '../timeline/Filmstrip';

interface VideoFrameTimelineProps {
  videoUrl: string;
  videoDuration: number;
  currentTimestamp: number;
  onTimestampChange: (timestamp: number) => void;
  onThumbnailUpdate?: (thumbnailUrl: string) => void;
  videoRef?: React.RefObject<HTMLVideoElement | null>;
  className?: string;
}

const TIMELINE_CONFIG = {
  frameCount: 10,
  seekTimeout: 2000,
};

export const VideoFrameTimeline: React.FC<VideoFrameTimelineProps> = ({
  videoUrl,
  videoDuration,
  currentTimestamp,
  onTimestampChange,
  onThumbnailUpdate: _onThumbnailUpdate,
  videoRef: externalVideoRef,
  className,
}) => {
  const internalVideoRef = useRef<HTMLVideoElement>(null);
  const [videoDimensions, setVideoDimensions] = useState<{
    width: number;
    height: number;
  } | null>(null);

  // Use external video ref if provided, otherwise use internal one
  const videoRef = externalVideoRef || internalVideoRef;

  const frameGeneration = useVideoFrames({
    video: videoRef.current,
    videoUrl,
    videoDuration,
    thumbnails: TIMELINE_CONFIG.frameCount,
    seekTimeout: TIMELINE_CONFIG.seekTimeout,
    generateSpriteSheet: true, // Generate sprite sheet directly
  });

  // Use sprite sheet directly from useVideoFrames
  const spriteSheetUrl = frameGeneration.spriteSheetUrl;

  // Calculate thumbnail dimensions based on video aspect ratio
  const thumbnailDimensions = useMemo(() => {
    return videoDimensions
      ? calculateThumbnailDimensions(
          videoDimensions.width,
          videoDimensions.height
        )
      : { width: 120, height: 68 }; // Fallback to landscape for loading state
  }, [videoDimensions]);

  const timelineRef = useRef<HTMLDivElement>(null);
  const lastSeekTimeRef = useRef<number>(0);
  const canvasRef = useRef<HTMLCanvasElement>(null);

  // Performance-optimized canvas update with throttling and smooth rendering
  const lastCanvasUpdateRef = useRef<number>(0);
  const canvasUpdateThrottleRef = useRef<NodeJS.Timeout | null>(null);

  // Track video dimensions when video loads
  useEffect(() => {
    const video = videoRef.current;
    if (!video) return;

    const handleLoadedMetadata = () => {
      if (video.videoWidth > 0 && video.videoHeight > 0) {
        setVideoDimensions({
          width: video.videoWidth,
          height: video.videoHeight,
        });
      }
    };

    if (video.readyState >= 1) {
      handleLoadedMetadata();
    } else {
      video.addEventListener('loadedmetadata', handleLoadedMetadata, {
        once: true,
      });
    }

    return () => {
      video.removeEventListener('loadedmetadata', handleLoadedMetadata);
    };
  }, [videoRef]);

  const updateCanvasImmediately = useCallback(
    (_timestamp: number) => {
      const previewVideo = videoRef.current;
      const canvas = canvasRef.current;

      if (
        previewVideo &&
        canvas &&
        previewVideo.videoWidth > 0 &&
        previewVideo.videoHeight > 0
      ) {
        const ctx = canvas.getContext('2d');
        if (ctx) {
          // Throttle canvas updates to 60fps for smooth performance
          const now = Date.now();
          if (now - lastCanvasUpdateRef.current > 16) {
            // Clear any pending canvas updates to prevent queue buildup
            if (canvasUpdateThrottleRef.current) {
              clearTimeout(canvasUpdateThrottleRef.current);
            }

            // Use single requestAnimationFrame for smooth rendering
            requestAnimationFrame(() => {
              // High-performance canvas rendering
              ctx.imageSmoothingEnabled = true;
              ctx.imageSmoothingQuality = 'high';
              ctx.clearRect(0, 0, canvas.width, canvas.height);
              ctx.drawImage(previewVideo, 0, 0, canvas.width, canvas.height);
              lastCanvasUpdateRef.current = now;
            });
          } else {
            // Schedule a delayed update for missed frames
            if (canvasUpdateThrottleRef.current) {
              clearTimeout(canvasUpdateThrottleRef.current);
            }
            canvasUpdateThrottleRef.current = setTimeout(() => {
              requestAnimationFrame(() => {
                ctx.imageSmoothingEnabled = true;
                ctx.imageSmoothingQuality = 'high';
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                ctx.drawImage(previewVideo, 0, 0, canvas.width, canvas.height);
                lastCanvasUpdateRef.current = Date.now();
              });
            }, 8); // 8ms delay for 120fps equivalent
          }
        }
      }
    },
    [videoRef]
  );

  const [handleTimelineMouseDown] = useDragLifecycle({
    customState: {
      timelineRef,
      videoRef,
      canvasRef,
      videoDuration,
      onTimestampChange,
      updateCanvasImmediately,
      lastSeekTimeRef,
      canvasUpdateThrottleRef,
    },
    onDragStart: (data) => {
      if (!data.customState.timelineRef.current) return;

      const rect = data.customState.timelineRef.current.getBoundingClientRect();
      const clickX = data.clientX - rect.left;
      const timelineWidth = rect.width;
      const newTimestamp =
        (clickX / timelineWidth) * data.customState.videoDuration;

      data.customState.onTimestampChange(newTimestamp);

      // Update video immediately
      const previewVideo = data.customState.videoRef.current;
      if (previewVideo) {
        const targetTime = newTimestamp / 1000;
        previewVideo.currentTime = targetTime;
      }
    },
    onDragMove: (data) => {
      if (!data.customState.timelineRef.current) return;

      const rect = data.customState.timelineRef.current.getBoundingClientRect();
      const mouseX = data.clientX - rect.left;
      const timelineWidth = rect.width;
      const newTimestamp = Math.max(
        0,
        Math.min(
          data.customState.videoDuration,
          (mouseX / timelineWidth) * data.customState.videoDuration
        )
      );

      data.customState.onTimestampChange(newTimestamp);

      // Update canvas immediately for responsive feedback
      data.customState.updateCanvasImmediately(newTimestamp);

      // Throttle video seeking to 60fps for smooth performance
      const now = Date.now();
      if (now - data.customState.lastSeekTimeRef.current > 16) {
        // Throttle to 60fps for smooth updates
        const previewVideo = data.customState.videoRef.current;
        if (previewVideo && previewVideo.readyState >= 2) {
          const targetTime = newTimestamp / 1000;
          previewVideo.currentTime = targetTime;
          data.customState.lastSeekTimeRef.current = now;
        }
      }
    },
    onDragEnd: (data) => {
      // Cleanup canvas update throttles
      if (data.customState.canvasUpdateThrottleRef?.current) {
        clearTimeout(data.customState.canvasUpdateThrottleRef.current);
        data.customState.canvasUpdateThrottleRef.current = null;
      }
    },
  });

  // Update video preview when timestamp changes
  useEffect(() => {
    const previewVideo = videoRef.current;
    const canvas = canvasRef.current;

    if (previewVideo && canvas && currentTimestamp !== undefined) {
      const targetTime = currentTimestamp / 1000;

      const updateVideoFrame = async () => {
        // Set video time
        previewVideo.currentTime = targetTime;

        // Wait for seek to complete
        await new Promise((resolve) => {
          const onSeeked = () => {
            previewVideo.removeEventListener('seeked', onSeeked);
            resolve(void 0);
          };
          previewVideo.addEventListener('seeked', onSeeked);
        });

        // Draw current frame to canvas
        const ctx = canvas.getContext('2d');
        if (
          ctx &&
          previewVideo.videoWidth > 0 &&
          previewVideo.videoHeight > 0
        ) {
          canvas.width = previewVideo.videoWidth;
          canvas.height = previewVideo.videoHeight;
          ctx.drawImage(previewVideo, 0, 0, canvas.width, canvas.height);
        }
      };

      updateVideoFrame();
    }
  }, [currentTimestamp, videoRef]);

  // Ensure initial canvas draw when component mounts
  useEffect(() => {
    const video = videoRef.current;
    const canvas = canvasRef.current;

    if (
      video &&
      canvas &&
      video.readyState >= video.HAVE_CURRENT_DATA &&
      video.videoWidth > 0 &&
      video.videoHeight > 0
    ) {
      // Video is already loaded, draw initial frame
      const ctx = canvas.getContext('2d');
      if (ctx) {
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;
        ctx.imageSmoothingEnabled = true;
        ctx.imageSmoothingQuality = 'high';
        ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
      }
    }
  }, [videoUrl, spriteSheetUrl, videoRef]); // Trigger when video URL or sprite sheet changes

  // Cleanup throttles on unmount
  useEffect(() => {
    return () => {
      if (canvasUpdateThrottleRef.current) {
        clearTimeout(canvasUpdateThrottleRef.current);
      }
    };
  }, []);

  return (
    <div className={`flex flex-col gap-2 ${className}`}>
      {!externalVideoRef && (
        <video
          ref={internalVideoRef}
          src={videoUrl}
          style={{ display: 'none' }}
          crossOrigin='anonymous'
        />
      )}

      {frameGeneration.isGenerating ? (
        <div className='flex h-16 items-center justify-center'>
          <div className='h-4 w-4 animate-spin rounded-full border-2 border-accent-pink border-t-transparent' />
        </div>
      ) : spriteSheetUrl ? (
        <div
          ref={timelineRef}
          className='relative h-16 w-full cursor-pointer overflow-hidden rounded-lg'
          onMouseDown={handleTimelineMouseDown}
          role='slider'
          tabIndex={0}
          aria-label='Video timeline scrubber'
          aria-valuemin={0}
          aria-valuemax={videoDuration}
          aria-valuenow={currentTimestamp}
          onKeyDown={(e) => {
            if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
              e.preventDefault();
              const step = videoDuration / 20; // 20 steps
              const newTimestamp =
                e.key === 'ArrowLeft'
                  ? Math.max(0, currentTimestamp - step)
                  : Math.min(videoDuration, currentTimestamp + step);
              onTimestampChange(newTimestamp);
            }
          }}
        >
          {/* Filmstrip background */}
          <Filmstrip
            src={spriteSheetUrl}
            thumbnailColumns={Math.ceil(
              Math.sqrt(frameGeneration.thumbnails.length)
            )}
            thumbnailRows={Math.ceil(
              frameGeneration.thumbnails.length /
                Math.ceil(Math.sqrt(frameGeneration.thumbnails.length))
            )}
            thumbnailWidth={thumbnailDimensions.width}
            thumbnailHeight={thumbnailDimensions.height}
            thumbnailCount={frameGeneration.thumbnails.length}
            className='h-full w-full'
          />

          {/* Video preview overlay */}
          <div
            className='pointer-events-none absolute top-0 z-10 h-full w-18 overflow-hidden rounded-lg border-2 border-accent-pink bg-black'
            style={{
              left: (() => {
                // Calculate position based on current timestamp
                const percentage = (currentTimestamp / videoDuration) * 100;
                return `clamp(36px, ${percentage}%, calc(100% - 36px))`;
              })(),
              transform: 'translateX(-50%)',
            }}
          >
            {/* Hidden video element for frame capture */}
            <video
              ref={videoRef}
              className='hidden'
              src={videoUrl}
              muted
              playsInline
              preload='auto'
              onLoadedData={(e) => {
                const video = e.target as HTMLVideoElement;
                video.currentTime = currentTimestamp / 1000;

                // Draw initial frame to canvas immediately when video loads
                setTimeout(() => {
                  const canvas = canvasRef.current;
                  if (canvas && video.videoWidth > 0 && video.videoHeight > 0) {
                    const ctx = canvas.getContext('2d');
                    if (ctx) {
                      canvas.width = video.videoWidth;
                      canvas.height = video.videoHeight;
                      ctx.imageSmoothingEnabled = true;
                      ctx.imageSmoothingQuality = 'high';
                      ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
                    }
                  }
                }, 50);
              }}
              onSeeked={() => {
                // Draw frame to canvas when video seeks
                const video = videoRef.current;
                const canvas = canvasRef.current;
                if (
                  video &&
                  canvas &&
                  video.videoWidth > 0 &&
                  video.videoHeight > 0
                ) {
                  const ctx = canvas.getContext('2d');
                  if (ctx) {
                    ctx.imageSmoothingEnabled = true;
                    ctx.imageSmoothingQuality = 'high';
                    ctx.clearRect(0, 0, canvas.width, canvas.height);
                    ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
                  }
                }
              }}
              onError={(e) => {
                console.error('Video error:', e);
              }}
            />

            {/* Canvas for displaying the current frame */}
            <canvas
              ref={canvasRef}
              className='h-full w-full object-cover'
              style={{ pointerEvents: 'none' }}
            />

            {/* Loading placeholder for canvas */}
            {!frameGeneration.thumbnails.length && (
              <div className='absolute inset-0 flex items-center justify-center bg-gray-800'>
                <div className='h-3 w-3 animate-spin rounded-full border-2 border-accent-pink border-t-transparent' />
              </div>
            )}
          </div>
        </div>
      ) : null}
    </div>
  );
};
