import React, { useCallback, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';

import Button, {
  ButtonShape,
  ButtonSize,
  ButtonVariant,
} from '@/components/button/Button';
import SpinnerSVG from '@/components/svg/SpinnerSVG';
import { VideoFrameTimeline } from '@/components/video/VideoFrameTimeline';
import { ArrowLeftIcon } from '@/icons';
import { useApiClient } from '@/lib/apiClient';

// Canvas dimensions for video preview
const CANVAS_WIDTH = 294;
const CANVAS_HEIGHT = 427;
const CANVAS_ASPECT_RATIO = CANVAS_WIDTH / CANVAS_HEIGHT;

const VideoLoadingState = ({
  isGeneratingThumbnail,
  videoUrl,
  videoDuration,
}: {
  isGeneratingThumbnail: boolean;
  videoUrl: string;
  videoDuration: number;
}) => (
  <div className='flex h-full w-full items-center justify-center text-white'>
    {isGeneratingThumbnail ? (
      <div className='flex items-center gap-2'>
        <SpinnerSVG className='h-4 w-4' />
        <span>Generating preview...</span>
      </div>
    ) : (
      <div className='text-center'>
        <div className='mb-2 text-sm text-gray-400'>Video Preview</div>
        <div className='text-xs text-gray-500'>
          Select a frame from the timeline below
        </div>
        <div className='mt-2 text-xs text-gray-600'>
          Video: {videoUrl ? 'Loaded' : 'Not loaded'} | Duration:{' '}
          {videoDuration}ms
        </div>
      </div>
    )}
  </div>
);

interface PickCoverModalProps {
  isOpen: boolean;
  onClose: () => void;
  onSave: (timestamp: number, thumbnailS3Id?: string) => void;
  videoUrl: string;
  videoDuration: number;
  currentTimestamp: number;
  onTimestampChange: (timestamp: number) => void;
  uploadId: string;
}

export const PickCoverModal: React.FC<PickCoverModalProps> = ({
  isOpen,
  onClose,
  onSave,
  videoUrl,
  videoDuration,
  currentTimestamp,
  onTimestampChange,
  uploadId,
}) => {
  const [isGeneratingThumbnail, setIsGeneratingThumbnail] = useState(false);
  const [isVideoLoaded, setIsVideoLoaded] = useState(false);
  const videoRef = useRef<HTMLVideoElement>(null);
  const displayCanvasRef = useRef<HTMLCanvasElement>(null);
  const apiClient = useApiClient();
  const offscreenCanvasRef = useRef<OffscreenCanvas | null>(null);
  const lastSeekTimeRef = useRef<number>(0);
  const seekThrottleRef = useRef<NodeJS.Timeout | null>(null);
  const lastCanvasUpdateRef = useRef<number>(0);
  const canvasUpdateThrottleRef = useRef<NodeJS.Timeout | undefined>(undefined);

  useEffect(() => {
    const handleEscape = (e: KeyboardEvent) => {
      if (e.key === 'Escape' && isOpen) {
        onClose();
      }
    };

    if (isOpen) {
      document.addEventListener('keydown', handleEscape);
    }

    return () => {
      document.removeEventListener('keydown', handleEscape);
    };
  }, [isOpen, onClose]);

  useEffect(() => {
    if (isOpen && videoUrl && videoRef.current) {
      const video = videoRef.current;

      video.src = videoUrl;
      video.load();

      const handleLoadedData = () => {
        setIsVideoLoaded(true);
      };

      const handleError = (_e: Event) => {
        setIsVideoLoaded(false);
      };

      video.addEventListener('loadeddata', handleLoadedData, { once: true });
      video.addEventListener('error', handleError, { once: true });

      return () => {
        video.removeEventListener('loadeddata', handleLoadedData);
        video.removeEventListener('error', handleError);
      };
    }
  }, [isOpen, videoUrl]);

  useEffect(() => {
    if (isOpen) {
      setIsVideoLoaded(false);
      setIsGeneratingThumbnail(false);

      // Reset video element if it exists
      if (videoRef.current) {
        videoRef.current.currentTime = 0;
        videoRef.current.load();
      }

      // Initialize OffscreenCanvas when modal opens
      offscreenCanvasRef.current = new OffscreenCanvas(
        CANVAS_WIDTH,
        CANVAS_HEIGHT
      );
    } else {
      // Clean up OffscreenCanvas when modal closes
      offscreenCanvasRef.current = null;
      // Clear any pending throttles
      if (seekThrottleRef.current) {
        clearTimeout(seekThrottleRef.current);
        seekThrottleRef.current = null;
      }
      if (canvasUpdateThrottleRef.current) {
        clearTimeout(canvasUpdateThrottleRef.current);
        canvasUpdateThrottleRef.current = undefined;
      }
    }
  }, [isOpen]);

  const handleSave = useCallback(async () => {
    if (!uploadId) {
      return;
    }

    let thumbnailS3Id: string | undefined;

    try {
      setIsGeneratingThumbnail(true);

      const { data, error } = await (apiClient.POST as any)(
        '/api/uploads/video/{upload_id}/generate-thumbnail',
        {
          params: { path: { upload_id: uploadId } },
          body: { timestamp_ms: Math.round(currentTimestamp) },
        }
      );

      if (error) {
        throw new Error(`API error: ${JSON.stringify(error)}`);
      }

      thumbnailS3Id = data?.thumbnail_s3_id;
    } catch (error) {
    } finally {
      setIsGeneratingThumbnail(false);
    }

    onSave(currentTimestamp, thumbnailS3Id);
  }, [currentTimestamp, onSave, uploadId, apiClient]);

  const handleVideoFrameSelection = useCallback(
    (timestamp: number) => {
      onTimestampChange(timestamp);
    },
    [onTimestampChange]
  );

  // Shared function to render video frame to canvas
  const renderVideoFrame = useCallback(async () => {
    const video = videoRef.current;
    const displayCanvas = displayCanvasRef.current;
    const offscreenCanvas = offscreenCanvasRef.current;

    if (
      video &&
      displayCanvas &&
      offscreenCanvas &&
      video.videoWidth > 0 &&
      video.videoHeight > 0
    ) {
      const offscreenCtx = offscreenCanvas.getContext('2d');
      const displayCtx = displayCanvas.getContext('2d');

      if (offscreenCtx && displayCtx) {
        // Set OffscreenCanvas size to match the container (only if not already set)
        if (
          offscreenCanvas.width !== CANVAS_WIDTH ||
          offscreenCanvas.height !== CANVAS_HEIGHT
        ) {
          offscreenCanvas.width = CANVAS_WIDTH;
          offscreenCanvas.height = CANVAS_HEIGHT;
        }

        // Clear the OffscreenCanvas before drawing to prevent accumulation
        offscreenCtx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

        // Fill with black background
        offscreenCtx.fillStyle = 'black';
        offscreenCtx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

        // Calculate crop area for cover mode (fill canvas, crop video if needed)
        const videoAspectRatio = video.videoWidth / video.videoHeight;
        const canvasAspectRatio = CANVAS_ASPECT_RATIO;

        const isVideoWider = videoAspectRatio > canvasAspectRatio;
        const scale = isVideoWider
          ? video.videoHeight / CANVAS_HEIGHT
          : video.videoWidth / CANVAS_WIDTH;

        const sourceWidth = isVideoWider
          ? CANVAS_WIDTH * scale
          : video.videoWidth;
        const sourceHeight = isVideoWider
          ? video.videoHeight
          : CANVAS_HEIGHT * scale;
        const sourceX = isVideoWider ? (video.videoWidth - sourceWidth) / 2 : 0;
        const sourceY = isVideoWider
          ? 0
          : (video.videoHeight - sourceHeight) / 2;

        // Draw cropped video frame to fill canvas with high performance
        offscreenCtx.imageSmoothingEnabled = true;
        offscreenCtx.imageSmoothingQuality = 'high';
        offscreenCtx.drawImage(
          video,
          sourceX,
          sourceY,
          sourceWidth,
          sourceHeight, // Source crop area
          0,
          0,
          CANVAS_WIDTH,
          CANVAS_HEIGHT // Fill entire canvas
        );

        // Convert OffscreenCanvas to ImageBitmap and draw to display canvas
        const imageBitmap = await offscreenCanvas.transferToImageBitmap();

        // Ensure display canvas is properly sized
        if (
          displayCanvas.width !== CANVAS_WIDTH ||
          displayCanvas.height !== CANVAS_HEIGHT
        ) {
          displayCanvas.width = CANVAS_WIDTH;
          displayCanvas.height = CANVAS_HEIGHT;
        }

        displayCtx.drawImage(imageBitmap, 0, 0);
        imageBitmap.close(); // Clean up the ImageBitmap
      }
    }
  }, []);

  // Performance-optimized function to draw video frame to canvas using OffscreenCanvas
  const drawFrameToCanvas = useCallback(async () => {
    const video = videoRef.current;
    const displayCanvas = displayCanvasRef.current;
    const offscreenCanvas = offscreenCanvasRef.current;

    if (
      video &&
      displayCanvas &&
      offscreenCanvas &&
      video.videoWidth > 0 &&
      video.videoHeight > 0
    ) {
      const offscreenCtx = offscreenCanvas.getContext('2d');
      const displayCtx = displayCanvas.getContext('2d');

      if (offscreenCtx && displayCtx) {
        // 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(async () => {
            await renderVideoFrame();
            lastCanvasUpdateRef.current = now;
          });
        } else {
          // Schedule a delayed update for missed frames
          if (canvasUpdateThrottleRef.current) {
            clearTimeout(canvasUpdateThrottleRef.current);
          }
          canvasUpdateThrottleRef.current = setTimeout(async () => {
            requestAnimationFrame(async () => {
              await renderVideoFrame();
              lastCanvasUpdateRef.current = Date.now();
            });
          }, 8); // 8ms delay for 120fps equivalent
        }
      }
    }
  }, [renderVideoFrame]);

  // Update the large video preview when timestamp changes
  useEffect(() => {
    const video = videoRef.current;

    if (video && currentTimestamp !== undefined && isVideoLoaded) {
      const targetTime = currentTimestamp / 1000;

      // Throttle video seeking to 60fps for smooth performance
      const now = Date.now();
      if (now - lastSeekTimeRef.current > 16) {
        // Throttle to 60fps for smooth updates
        if (video.readyState >= 2) {
          video.currentTime = targetTime;
          lastSeekTimeRef.current = now;
        }
      }

      // Always draw canvas immediately for responsive feedback
      drawFrameToCanvas();
    }
  }, [currentTimestamp, videoRef, isVideoLoaded, drawFrameToCanvas]);

  if (!isOpen) return null;

  return createPortal(
    <div className='fixed inset-0 z-[100000] flex items-center justify-center bg-black/50'>
      <div className='h-[750px] w-[666px] rounded-lg bg-background-secondary p-6'>
        <div className='mb-6 flex items-center gap-3'>
          <Button
            onClick={onClose}
            variant={ButtonVariant.Tertiary}
            size={ButtonSize.Small}
            shape={ButtonShape.Pill}
            iconOnly
            icon={ArrowLeftIcon}
            className='h-10 w-10 bg-white/10 hover:bg-white/20'
          />
          <div className='flex flex-col'>
            <h2 className='font-["PP_Editorial_New"] text-2xl leading-none font-normal text-white'>
              Pick Cover Frame
            </h2>
            <p className='mt-1 font-["PP_Neue_Montreal"] text-xs leading-normal font-normal tracking-tight text-white opacity-50'>
              This is what people see in the home feed and your profile before
              they play the video
            </p>
          </div>
        </div>

        <div className='mb-6 flex justify-center'>
          <div
            className='overflow-hidden rounded-[20px] bg-black/60'
            style={{ width: CANVAS_WIDTH, height: CANVAS_HEIGHT }}
          >
            {/* Hidden video element for frame capture */}
            <video
              key={`video-${isOpen ? 'open' : 'closed'}`}
              ref={videoRef}
              className='hidden'
              src={videoUrl}
              muted
              playsInline
              preload='auto'
              crossOrigin='anonymous'
              onLoadedData={() => {
                setIsVideoLoaded(true);
                // Draw initial frame when video loads
                setTimeout(() => {
                  drawFrameToCanvas();
                }, 100);
              }}
              onSeeked={() => {
                // Draw frame to canvas when video seeks
                drawFrameToCanvas();
              }}
            />

            {/* Canvas for displaying the current frame */}
            <canvas
              ref={displayCanvasRef}
              className='h-full w-full object-cover'
              style={{ pointerEvents: 'none' }}
            />
            {!isVideoLoaded && (
              <VideoLoadingState
                isGeneratingThumbnail={isGeneratingThumbnail}
                videoUrl={videoUrl}
                videoDuration={videoDuration}
              />
            )}
          </div>
        </div>

        <div className='mb-6 flex justify-center'>
          <VideoFrameTimeline
            key={`timeline-${isOpen ? 'open' : 'closed'}`}
            videoUrl={videoUrl}
            videoDuration={videoDuration}
            currentTimestamp={currentTimestamp}
            onTimestampChange={handleVideoFrameSelection}
            videoRef={videoRef}
            className='w-full'
          />
        </div>

        <div className='flex items-center justify-end'>
          <div className='flex items-center gap-4'>
            <Button
              onClick={onClose}
              variant={ButtonVariant.Secondary}
              size={ButtonSize.Medium}
              shape={ButtonShape.Rounded}
              className='h-14 px-8'
            >
              Cancel
            </Button>
            <Button
              onClick={handleSave}
              disabled={isGeneratingThumbnail}
              variant={ButtonVariant.Primary}
              size={ButtonSize.Medium}
              shape={ButtonShape.Rounded}
              className='h-14 px-8'
            >
              {isGeneratingThumbnail ? 'Generating...' : 'Save'}
            </Button>
          </div>
        </div>
      </div>
    </div>,
    document.body
  );
};
