// ===========================================================
// BarcodeScanner — mobile-camera-first EAN-13 / UPC scanner.
//
// Drives the device's REAR camera via getUserMedia (facingMode
// 'environment') and decodes barcodes with ZXing (global `ZXing`,
// loaded as a UMD <script> — no build step). Full-screen, with a
// target frame, graceful permission/secure-context handling, and a
// manual text-entry fallback for desktop / camera-denied.
//
// Props:
//   onDetect(code)  — called once with the decoded barcode string
//   onManual(code)  — called with a hand-typed barcode (fallback)
//   onClose()       — dismiss the scanner
//
// getUserMedia requires a SECURE context: https:// or localhost.
// On a phone over the LAN it must be the deployed https site.
// ===========================================================
const { useState: useScanState, useEffect: useScanEffect, useRef: useScanRef } = React;

function BarcodeScanner({ onDetect, onManual, onClose }) {
  const videoRef = useScanRef(null);
  const readerRef = useScanRef(null);
  const doneRef = useScanRef(false);
  // starting | scanning | denied | insecure | unsupported | error
  const [status, setStatus] = useScanState("starting");
  const [errMsg, setErrMsg] = useScanState("");
  const [manual, setManual] = useScanState("");

  // Fully release the camera: reset the ZXing reader AND explicitly stop the
  // MediaStream tracks (reader.reset() doesn't always release them in every
  // ZXing build), so the camera light goes off when we stop scanning.
  function stopCamera() {
    try { readerRef.current && readerRef.current.reset(); } catch (e) {}
    const v = videoRef.current;
    if (v && v.srcObject) {
      try { v.srcObject.getTracks().forEach(t => t.stop()); } catch (e) {}
      v.srcObject = null;
    }
  }

  useScanEffect(() => {
    const Z = window.ZXing;
    const secure = window.isSecureContext ||
      ["localhost", "127.0.0.1"].includes(window.location.hostname);

    if (!Z || !Z.BrowserMultiFormatReader) { setStatus("unsupported"); return; }
    if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { setStatus("unsupported"); return; }
    if (!secure) { setStatus("insecure"); return; }

    const hints = new Map();
    hints.set(Z.DecodeHintType.POSSIBLE_FORMATS, [
      Z.BarcodeFormat.EAN_13, Z.BarcodeFormat.UPC_A,
      Z.BarcodeFormat.UPC_E,  Z.BarcodeFormat.EAN_8,
    ]);
    const reader = new Z.BrowserMultiFormatReader(hints);
    readerRef.current = reader;

    reader.decodeFromConstraints(
      { video: { facingMode: { ideal: "environment" } } },
      videoRef.current,
      (result) => {
        if (doneRef.current || !result) return;
        const text = result.getText ? result.getText() : result.text;
        if (!text) return;
        doneRef.current = true;
        try { navigator.vibrate && navigator.vibrate(40); } catch (e) {}
        try { reader.reset(); } catch (e) {}
        onDetect(text);
      }
    ).then(() => { if (!doneRef.current) setStatus("scanning"); })
     .catch((e) => {
        const name = (e && e.name) || "";
        if (name === "NotAllowedError" || name === "SecurityError" || name === "PermissionDeniedError") setStatus("denied");
        else if (name === "NotFoundError" || name === "DevicesNotFoundError") { setStatus("error"); setErrMsg("No camera found on this device."); }
        else { setStatus("error"); setErrMsg((e && e.message) || "Couldn't start the camera."); }
     });

    return () => { doneRef.current = true; stopCamera(); };
  }, []);

  function submitManual(e) {
    if (e) e.preventDefault();
    const code = manual.replace(/[^0-9]/g, "").trim();
    if (code.length >= 8) onManual(code);
  }

  const showManual = status === "denied" || status === "insecure" || status === "unsupported" || status === "error";

  return (
    <div className="bs-overlay">
      <div className="bs-topbar">
        <span className="bs-title">Scan barcode</span>
        <button className="bs-close" onClick={onClose} aria-label="Close scanner">×</button>
      </div>

      <div className="bs-stage">
        <video ref={videoRef} className="bs-video" muted playsInline autoPlay />
        {status === "scanning" && (
          <div className="bs-frame">
            <span className="bs-corner tl" /><span className="bs-corner tr" />
            <span className="bs-corner bl" /><span className="bs-corner br" />
            <div className="bs-scanline" />
          </div>
        )}
        {status === "starting" && <div className="bs-hint">Starting camera…</div>}
        {status === "scanning" && <div className="bs-hint bs-hint-bottom">Point at the barcode on the sleeve</div>}
      </div>

      {showManual && (
        <div className="bs-fallback">
          <div className="bs-fallback-msg">
            {status === "denied"  && "Camera access was blocked. Allow it in your browser settings, or type the barcode below."}
            {status === "insecure" && "The camera needs a secure (https) connection. Open the live site on your phone, or type the barcode below."}
            {status === "unsupported" && "This browser can't open the camera here. Type the barcode below instead."}
            {status === "error"   && (errMsg || "Camera unavailable.") + " You can type the barcode below."}
          </div>
          <form className="bs-manual" onSubmit={submitManual}>
            <input
              className="bs-manual-input"
              inputMode="numeric"
              autoComplete="off"
              placeholder="Barcode digits (EAN-13 / UPC)"
              value={manual}
              onChange={(e) => setManual(e.target.value)}
            />
            <button className="bs-manual-go" type="submit" disabled={manual.replace(/[^0-9]/g, "").length < 8}>
              Look up
            </button>
          </form>
        </div>
      )}

      {status === "scanning" && (
        <button className="bs-type-instead" onClick={() => { stopCamera(); setStatus("denied"); }}>
          Can't scan? Type the barcode instead
        </button>
      )}
    </div>
  );
}

Object.assign(window, { BarcodeScanner });
