/**
 * QR code scanner from camera. Decodes private key (pk) from QR.
 */
import jsQR from "./vendor/qr/jsqr.bundle.mjs";

/**
 * Scan QR code from camera video stream.
 * @param {HTMLVideoElement} videoEl - Video element to render the stream.
 * @param {AbortSignal} [signal] - AbortSignal to stop scanning (e.g. when dialog closes).
 * @returns {Promise<string>} Decoded QR text.
 */
export async function scanQRFromCamera(videoEl, signal) {
  const stream = await navigator.mediaDevices.getUserMedia({
    video: { facingMode: "environment" },
  });
  videoEl.srcObject = stream;
  await videoEl.play();

  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");

  return new Promise((resolve, reject) => {
    if (signal?.aborted) {
      stream.getTracks().forEach((t) => t.stop());
      reject(new DOMException("Aborted", "AbortError"));
      return;
    }
    signal?.addEventListener("abort", () => {
      stream.getTracks().forEach((t) => t.stop());
      videoEl.srcObject = null;
      reject(new DOMException("Aborted", "AbortError"));
    });

    function tick() {
      if (signal?.aborted) return;
      if (videoEl.readyState !== videoEl.HAVE_ENOUGH_DATA) {
        requestAnimationFrame(tick);
        return;
      }
      canvas.width = videoEl.videoWidth;
      canvas.height = videoEl.videoHeight;
      if (canvas.width === 0 || canvas.height === 0) {
        requestAnimationFrame(tick);
        return;
      }
      ctx.drawImage(videoEl, 0, 0);
      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
      const result = jsQR(imageData.data, imageData.width, imageData.height);
      if (result) {
        stream.getTracks().forEach((t) => t.stop());
        videoEl.srcObject = null;
        resolve(result.data);
        return;
      }
      requestAnimationFrame(tick);
    }
    tick();
  });
}
