import {
 forwardRef,
 useCallback,
 useEffect,
 useImperativeHandle,
 useRef,
 useState,
} from 'react';
import { FacingMode, WebCameraHandler, WebCameraProps } from './camera.types';

export const WebCamera = forwardRef<WebCameraHandler, WebCameraProps>(
 ({ active, videoStyle, onError }, ref) => {
  const captureQuality = 0.8;
  const captureType = 'jpeg';
  const videoRef = useRef<HTMLVideoElement>(null);
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const streamRef = useRef<MediaStream | null>(null);
  const [facingMode, setFacingMode] = useState<FacingMode>('environment');

  useEffect(() => {
   if (!active) {
    stopCamera();
    return;
   }

   let mounted = true;

   const startCamera = async () => {
    try {
     const stream = await navigator.mediaDevices.getUserMedia({
      video: { facingMode: { ideal: facingMode } },
     });

     if (!mounted || !videoRef.current) {
      stream.getTracks().forEach((t) => t.stop());
      return;
     }

     videoRef.current.srcObject = stream;
     await videoRef.current.play();
     streamRef.current = stream;
    } catch (err) {
     onError?.(err as Error);
    }
   };

   startCamera();

   return () => {
    mounted = false;
    stopCamera();
   };
  }, [active, facingMode]);

  const stopCamera = useCallback(() => {
   streamRef.current?.getTracks().forEach((t) => t.stop());
   streamRef.current = null;

   if (videoRef.current) {
    videoRef.current.pause();
    videoRef.current.srcObject = null;
   }
  }, []);

  const capture = useCallback(async (): Promise<File | null> => {
   if (!videoRef.current || !canvasRef.current) return null;

   const video = videoRef.current;
   const canvas = canvasRef.current;
   const ctx = canvas.getContext('2d');

   if (!ctx || video.readyState < 2) return null;

   canvas.width = video.videoWidth;
   canvas.height = video.videoHeight;
   ctx.drawImage(video, 0, 0);

   return new Promise((resolve) => {
    canvas.toBlob(
     (blob) => {
      if (!blob) return resolve(null);
      resolve(
       new File([blob], `capture-${Date.now()}.${captureType}`, {
        type: `image/${captureType}`,
       }),
      );
     },
     `image/${captureType}`,
     captureQuality,
    );
   });
  }, [captureType, captureQuality]);

  const switchCamera = useCallback(() => {
   setFacingMode((p) => (p === 'user' ? 'environment' : 'user'));
  }, []);

  useImperativeHandle(ref, () => ({ capture, switchCamera }), [
   capture,
   switchCamera,
  ]);

  return (
   <div style={{ height: '100%' }}>
    <video
     ref={videoRef}
     autoPlay
     muted
     playsInline
     style={{
      width: '100%',
      height: '100%',
      objectFit: 'cover',
      ...videoStyle,
     }}
    />
    <canvas ref={canvasRef} style={{ display: 'none' }} />
   </div>
  );
 },
);
