import React, {
  useState, useEffect, useRef, useCallback, useContext,
} from 'react';
import Webcam from 'react-webcam';
import FacingMode from '@constant/FacingMode';
import Loader from '@components/common/Loader';
import '@style/RootContainer.scss';
import RootContext from '@context/RootContext';
import { resetResults } from '@slice/RecognitionSlice';
import CameraOptions from './CameraOptions';
import Capture from './Capture';

const CAMERA_OPTIONS = {
  format: 'image/jpeg',
  minHeight: 1920,
  minWidth: 1080,
  quality: 0.75,
  defaultView: FacingMode.FRONT,
};

const CameraContainer = () => {
  const { dispatch } = useContext(RootContext);
  const ref = useRef(null);
  const [imgSrc, setImgSrc] = useState(null);
  const [captureError, setCaptureError] = useState(null);
  const [videoInputs, setVideoInputs] = useState([]);
  const [resetCameraView, setResetCameraView] = useState(false);
  const [videoConstraints, setVideoConstraints] = useState({
    facingMode: CAMERA_OPTIONS.defaultView,
  });

  useEffect(() => {
    const gotDevices = (mediaDevices) => new Promise((resolve, reject) => {
      const availableVideoInputs = mediaDevices
        .filter(({ kind }) => kind === 'videoinput')
        .map(({ deviceId, label }) => ({ deviceId, label }));
      if (availableVideoInputs.length > 0) {
        resolve(availableVideoInputs);
      } else {
        reject(new Error('ERR::NO_MEDIA_TO_STREAM'));
      }
    });

    navigator.mediaDevices
      .enumerateDevices()
      .then(gotDevices)
      .then((availableVideoInputs) => setVideoInputs({ availableVideoInputs }))
      .catch((err) => console.error(err));
  }, []);

  const changeCameraView = () => {
    if (videoInputs.length === 1) {
      console.error('ERR::AVAILABLE_MEDIA_STREAMS_IS_1');
      return;
    }

    setResetCameraView(true);

    setTimeout(() => {
      const { facingMode } = videoConstraints;
      const newFacingMode = facingMode === FacingMode.FRONT ? FacingMode.REAR : FacingMode.FRONT;
      setResetCameraView(false);
      setVideoConstraints({ ...videoConstraints, facingMode: newFacingMode });
    }, 100);
  };

  const capture = useCallback(() => {
    const imageSrc = ref.current.getScreenshot();
    setImgSrc(imageSrc);
    if (!imageSrc) {
      setCaptureError(['Failed to capture camera. Please try again.']);
    } else {
      setCaptureError(null);
    }
  }, [ref, setImgSrc]);

  const onBackClick = () => {
    setImgSrc(null);
    dispatch(resetResults());
  };

  const cameraView = () => (
    <>
      <div className="row">
        {resetCameraView ? (
          <Loader />
        ) : (
          <Webcam
            mirrored={false}
            width="100%"
            height="100%"
            audio={false}
            ref={ref}
            screenshotFormat={CAMERA_OPTIONS.format}
            minScreenshotWidth={CAMERA_OPTIONS.minWidth}
            minScreenshotHeight={CAMERA_OPTIONS.minHeight}
            screenshotQuality={CAMERA_OPTIONS.quality}
            videoConstraints={videoConstraints} />
        )}
      </div>
      <CameraOptions capture={capture} changeCameraView={changeCameraView} />
    </>
  );

  return (
    <>
      {imgSrc
        ? <Capture imgSrc={imgSrc} error={captureError} onBackClick={onBackClick} />
        : cameraView()}
    </>
  );
};

export default CameraContainer;
