import React, { useEffect, useRef, useState } from 'react';
import { Box, Button, color, config, Text } from 'folds';
import jsQR from 'jsqr';

type QrScannerProps = {
  onScan: (bytes: Uint8ClampedArray) => void;
  onCancel: () => void;
};

// Camera QR scanner. Decodes frames with jsQR and hands back the raw byte
// segment (`result.binaryData`) — Matrix QR verification needs the raw bytes,
// not a decoded string, so the string-only `BarcodeDetector` can't be used.
export function QrScanner({ onScan, onCancel }: QrScannerProps) {
  const videoRef = useRef<HTMLVideoElement>(null);
  const [error, setError] = useState<string>();
  const doneRef = useRef(false);

  useEffect(() => {
    let stream: MediaStream | undefined;
    let raf = 0;
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d', { willReadFrequently: true });

    const tick = () => {
      const video = videoRef.current;
      if (!doneRef.current && video && ctx && video.readyState === video.HAVE_ENOUGH_DATA) {
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;
        ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
        const image = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const result = jsQR(image.data, image.width, image.height);
        if (result && result.binaryData.length > 0) {
          doneRef.current = true;
          onScan(new Uint8ClampedArray(result.binaryData));
          return;
        }
      }
      raf = requestAnimationFrame(tick);
    };

    (async () => {
      try {
        stream = await navigator.mediaDevices.getUserMedia({
          video: { facingMode: 'environment' },
        });
        if (videoRef.current) {
          videoRef.current.srcObject = stream;
          await videoRef.current.play();
        }
        raf = requestAnimationFrame(tick);
      } catch {
        setError(
          'Could not access the camera. Grant camera permission, or verify with emojis instead.',
        );
      }
    })();

    return () => {
      doneRef.current = true;
      cancelAnimationFrame(raf);
      stream?.getTracks().forEach((track) => track.stop());
    };
  }, [onScan]);

  if (error) {
    return (
      <Box direction="Column" gap="400">
        <Text style={{ color: color.Critical.Main }} size="T300">
          {error}
        </Text>
        <Button variant="Secondary" fill="Soft" onClick={onCancel}>
          <Text size="B400">Back</Text>
        </Button>
      </Box>
    );
  }

  return (
    <Box direction="Column" gap="400" alignItems="Center">
      <Text size="T300" align="Center">
        Point your camera at the QR code shown on your other device.
      </Text>
      <video
        ref={videoRef}
        muted
        playsInline
        style={{
          width: '100%',
          maxWidth: 280,
          borderRadius: config.radii.R400,
          background: '#000',
        }}
      >
        <track kind="captions" />
      </video>
      <Button variant="Secondary" fill="Soft" onClick={onCancel}>
        <Text size="B400">Cancel</Text>
      </Button>
    </Box>
  );
}
