/**
 * Fichier  : assets/js/scanner/ScannerService.js
 * Auteur   : KREMER Régis
 * Version  : 2.2.0
 * Date     : 2026-02-21
 * Description : JS — Service scanner codes-barres (Quagga2 + Html5Qrcode)
 *
 * Changelog :
 *   v2.1.0 (2026-02-21) — Version initiale auditée et sécurisée
 *   v2.1.1 (2026-02-21) — Corrections sécurité, utils.js centralisé
 *   v2.2.0 (2026-02-21) — Permissions magasins, bons de commande, rapport d'écart,
 *                      import CSV, historique prix, bon de livraison multi-lignes
 */
/* ================================================================
 * StockMaster Pro — ScannerService (v1.0.1)
 * - Caméra (AUTO / BARCODE / QRCODE) via Html5Qrcode (stable mobile/tablette)
 * - Scan image (upload) via Html5Qrcode.scanFile
 * - Sélection caméra robuste (évite NotFoundError sur desktop)
 * - Singleton : une seule session caméra à la fois (page + modales)
 * ================================================================ */
(function () {
  const hasHtml5 = () => (typeof Html5Qrcode !== "undefined");

  const pickBestCamera = (cams) => {
    if (!Array.isArray(cams) || cams.length === 0) return null;
    // Prefer back/rear/environment if available
    const score = (label) => {
      const s = (label || "").toLowerCase();
      let v = 0;
      if (s.includes("back") || s.includes("rear")) v += 5;
      if (s.includes("environment")) v += 4;
      if (s.includes("camera 0") || s.includes("0")) v += 1;
      return v;
    };
    let best = cams[0];
    let bestScore = score(best.label);
    for (const c of cams) {
      const sc = score(c.label);
      if (sc > bestScore) { best = c; bestScore = sc; }
    }
    return best;
  };

  const ScannerService = {
    _html5: null,
    _running: false,
    _targetId: null,
    _cooldownUntil: 0,
    _lastText: "",
    _lastTs: 0,

    _ensureTargetId(target) {
      if (!target) return null;
      if (typeof target === "string") return target;
      if (target.id) return target.id;
      target.id = "scanTarget_" + Math.random().toString(36).slice(2);
      return target.id;
    },

    async _getCameraConfig() {
      // Prefer explicit deviceId when possible (avoids Firefox "object can not be found here")
      try {
        const cams = await Html5Qrcode.getCameras();
        const best = pickBestCamera(cams);
        if (best?.id) return { deviceId: { exact: best.id } };
      } catch (e) {
        // ignore, fallback below
      }
      // Fallbacks
      return { facingMode: "environment" };
    },

    async start({ target, mode = "auto", onDetected, onError, fps = 12 }) {
      if (!hasHtml5()) {
        const msg = "html5-qrcode non chargé.";
        if (onError) onError(new Error(msg));
        else console.warn(msg);
        return;
      }

      await this.stop();

      const targetId = this._ensureTargetId(target);
      if (!targetId) return;

      this._targetId = targetId;
      this._html5 = new Html5Qrcode(targetId);
      this._running = true;

      const handleDetected = (decodedText) => {
        const raw = (decodedText || "").trim();
        if (!raw) return;

        const now = Date.now();
        if (now < this._cooldownUntil) return;
        if (raw === this._lastText && (now - this._lastTs) < 1500) return;

        this._lastText = raw;
        this._lastTs = now;
        this._cooldownUntil = now + 800;

        if (typeof onDetected === "function") onDetected(raw);
      };

      const formats = (mode === "qrcode")
        ? [Html5QrcodeSupportedFormats.QR_CODE]
        : (mode === "barcode")
          ? [
              Html5QrcodeSupportedFormats.EAN_13,
              Html5QrcodeSupportedFormats.EAN_8,
              Html5QrcodeSupportedFormats.CODE_128,
              Html5QrcodeSupportedFormats.CODE_39,
              Html5QrcodeSupportedFormats.UPC_A,
              Html5QrcodeSupportedFormats.UPC_E
            ]
          : null;

      const config = {
        fps,
        qrbox: (viewW, viewH) => {
          const size = Math.floor(Math.min(viewW, viewH) * 0.72);
          return { width: size, height: size };
        }
      };
      if (formats) config.formatsToSupport = formats;

      // Try best camera (deviceId) then fallbacks
      try {
        const camConfig = await this._getCameraConfig();
        await this._html5.start(
          camConfig,
          config,
          handleDetected,
          () => { /* ignore per-frame errors */ }
        );
        return;
      } catch (e1) {
        // Fallback #1: remove facingMode strictness
        try {
          await this._html5.start(
            { video: true },
            config,
            handleDetected,
            () => {}
          );
          return;
        } catch (e2) {
          this._running = false;
          if (onError) onError(e2);
          else console.error(e2);
        }
      }
    },

    async stop() {
      try {
        if (this._html5 && this._running) {
          await this._html5.stop();
        }
      } catch (e) {
        // ignore stop errors (detached stream etc.)
      }
      try {
        if (this._html5) {
          await this._html5.clear();
        }
      } catch (e) {}
      this._html5 = null;
      this._running = false;
      this._targetId = null;
    },

    async scanImage(file, { mode = "auto", onDetected, onError }) {
      if (!hasHtml5()) {
        const msg = "html5-qrcode non chargé.";
        if (onError) onError(new Error(msg));
        else console.warn(msg);
        return;
      }
      try {
        const tempId = "scanTemp_" + Math.random().toString(36).slice(2);
        const temp = document.createElement("div");
        temp.id = tempId;
        temp.style.position = "fixed";
        temp.style.left = "-99999px";
        temp.style.top = "-99999px";
        document.body.appendChild(temp);

        const h = new Html5Qrcode(tempId);

        const formats = (mode === "qrcode")
          ? [Html5QrcodeSupportedFormats.QR_CODE]
          : (mode === "barcode")
            ? [
                Html5QrcodeSupportedFormats.EAN_13,
                Html5QrcodeSupportedFormats.EAN_8,
                Html5QrcodeSupportedFormats.CODE_128,
                Html5QrcodeSupportedFormats.CODE_39,
                Html5QrcodeSupportedFormats.UPC_A,
                Html5QrcodeSupportedFormats.UPC_E
              ]
            : null;

        const cfg = {};
        if (formats) cfg.formatsToSupport = formats;

        const res = await h.scanFile(file, true, cfg);
        const raw = (res || "").trim();
        if (raw && typeof onDetected === "function") onDetected(raw);

        await h.clear();
        temp.remove();
      } catch (e) {
        if (onError) onError(e);
        else console.error(e);
      }
    }
  };

  window.ScannerService = ScannerService;
})();
