/* Lecteur — caméra avec segmentation d'arrière-plan (MediaPipe Selfie Segmentation, lazy-load) */

const BG_PRESETS = [
  { id: 'none',      label: 'Aucun',           kind: 'pass'  },
  { id: 'blur',      label: 'Flou doux',       kind: 'blur'  },
  { id: 'blur-strong', label: 'Flou prononcé', kind: 'blur-strong' },
  { id: 'signature', label: 'Signature Yehi-Or', kind: 'signature' },
  { id: 'logo',      label: 'Logo Yehi-Or (visible)', kind: 'logo' },
  { id: 'vert',      label: 'Vert Yehi-Or', kind: 'solid', color: '#003333' },
  { id: 'vertDeep',  label: 'Vert profond',    kind: 'solid', color: '#001e1e' },
  { id: 'parchemin', label: 'Parchemin',       kind: 'solid', color: '#f4ede2' },
  { id: 'ivoire',    label: 'Ivoire',          kind: 'solid', color: '#fbf7f1' },
  { id: 'gradient',  label: 'Vignette charte', kind: 'gradient' },
  { id: 'image',     label: 'Image personnalisée', kind: 'image' },
];

const MP_BASE = 'https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation@0.1.1675465747/';
const MP_SCRIPT = MP_BASE + 'selfie_segmentation.js';

function loadScript(src) {
  return new Promise((resolve, reject) => {
    if ([...document.scripts].find(s => s.src === src)) return resolve();
    const s = document.createElement('script');
    s.src = src; s.crossOrigin = 'anonymous';
    s.onload = () => resolve();
    s.onerror = () => reject(new Error('Échec du chargement de ' + src));
    document.head.appendChild(s);
  });
}

function CameraView({
  enabled, mode, deviceId, mirror, shape,
  bgPreset = 'none', bgImageDataUrl = null,
}) {
  const videoRef = React.useRef(null);
  const canvasRef = React.useRef(null);
  const bgImgRef = React.useRef(null);
  const signatureLogoRef = React.useRef(null);
  const segRef = React.useRef(null);
  const rafRef = React.useRef(null);
  const [stream, setStream] = React.useState(null);
  const [error, setError] = React.useState(null);
  const [segLoading, setSegLoading] = React.useState(false);
  const [segError, setSegError] = React.useState(null);
  const [segReady, setSegReady] = React.useState(false);

  const preset = BG_PRESETS.find(p => p.id === bgPreset) || BG_PRESETS[0];
  const usesSegmentation = preset.kind !== 'pass' && mode === 'webcam' && enabled;

  // Camera stream
  React.useEffect(() => {
    let active = true;
    let local = null;
    if (!enabled || mode !== 'webcam') { setStream(null); return; }
    (async () => {
      try {
        const constraints = {
          video: deviceId
            ? { deviceId: { exact: deviceId }, width: { ideal: 1280 }, height: { ideal: 720 } }
            : { width: { ideal: 1280 }, height: { ideal: 720 } },
          audio: false,
        };
        local = await navigator.mediaDevices.getUserMedia(constraints);
        if (!active) { local.getTracks().forEach(t => t.stop()); return; }
        setStream(local); setError(null);
      } catch (e) {
        if (active) { setError(e.message || 'Caméra indisponible'); setStream(null); }
      }
    })();
    return () => { active = false; if (local) local.getTracks().forEach(t => t.stop()); };
  }, [enabled, mode, deviceId]);

  React.useEffect(() => {
    if (videoRef.current && stream) videoRef.current.srcObject = stream;
  }, [stream]);

  // Load custom bg image when provided
  React.useEffect(() => {
    if (!bgImageDataUrl) { bgImgRef.current = null; return; }
    const img = new Image();
    img.onload = () => { bgImgRef.current = img; };
    img.src = bgImageDataUrl;
  }, [bgImageDataUrl]);

  // Pre-load brand logo for the "Signature Yehi-Or" preset
  React.useEffect(() => {
    if (signatureLogoRef.current) return;
    const img = new Image();
    img.crossOrigin = 'anonymous';
    img.onload = () => { signatureLogoRef.current = img; };
    img.src = 'assets/icone-yehi-or.png';
  }, []);

  // Segmentation pipeline
  React.useEffect(() => {
    let cancelled = false;
    let seg = null;

    async function init() {
      setSegError(null);
      setSegReady(false);
      if (!usesSegmentation || !stream) return;
      setSegLoading(true);
      try {
        if (!window.SelfieSegmentation) {
          await loadScript(MP_SCRIPT);
        }
        if (cancelled) return;
        seg = new window.SelfieSegmentation({
          locateFile: (file) => MP_BASE + file,
        });
        seg.setOptions({ modelSelection: 1, selfieMode: false });
        seg.onResults(onResults);
        await seg.initialize();
        if (cancelled) { try { seg.close(); } catch {} return; }
        segRef.current = seg;
        setSegLoading(false);
        setSegReady(true);
        tick();
      } catch (e) {
        console.error('[Lecteur] Segmentation init failed:', e);
        if (!cancelled) {
          setSegLoading(false);
          setSegError(e?.message || 'Le module de fond n\'a pas pu se charger (vérifiez votre connexion internet — il a besoin d\'être téléchargé une fois depuis le CDN MediaPipe).');
        }
      }
    }

    function onResults(results) {
      const canvas = canvasRef.current;
      if (!canvas) return;
      const w = canvas.width = results.image.width;
      const h = canvas.height = results.image.height;
      const ctx = canvas.getContext('2d');
      ctx.save();
      ctx.clearRect(0, 0, w, h);

      // Person mask
      ctx.drawImage(results.segmentationMask, 0, 0, w, h);
      ctx.globalCompositeOperation = 'source-in';
      ctx.drawImage(results.image, 0, 0, w, h);

      // Background behind
      ctx.globalCompositeOperation = 'destination-over';
      const p = BG_PRESETS.find(x => x.id === bgPreset);
      if (!p) {}
      else if (p.kind === 'blur') {
        ctx.filter = 'blur(10px) saturate(0.95)';
        ctx.drawImage(results.image, 0, 0, w, h);
        ctx.filter = 'none';
      }
      else if (p.kind === 'blur-strong') {
        ctx.filter = 'blur(22px) saturate(0.85) brightness(0.95)';
        ctx.drawImage(results.image, 0, 0, w, h);
        ctx.filter = 'none';
      }
      else if (p.kind === 'solid') {
        ctx.fillStyle = p.color;
        ctx.fillRect(0, 0, w, h);
      }
      else if (p.kind === 'logo') {
        // Bold brand background: solid vert + Yehi-Or logo prominent
        ctx.fillStyle = '#003333';
        ctx.fillRect(0, 0, w, h);
        const halo = ctx.createRadialGradient(w*0.5, h*0.5, 0, w*0.5, h*0.5, w*0.6);
        halo.addColorStop(0, 'rgba(251,176,59,0.12)');
        halo.addColorStop(1, 'rgba(251,176,59,0)');
        ctx.fillStyle = halo;
        ctx.fillRect(0, 0, w, h);
        const logo = signatureLogoRef.current;
        if (logo && logo.complete && logo.naturalWidth) {
          // Center, big, jaune doux — use the logo silhouette but recolour to gold
          const ratio = Math.min((w*0.45) / logo.naturalWidth, (h*0.7) / logo.naturalHeight);
          const lw = logo.naturalWidth * ratio;
          const lh = logo.naturalHeight * ratio;
          const lx = (w - lw) / 2;
          const ly = (h - lh) / 2;
          ctx.save();
          ctx.globalAlpha = 0.55;
          // Recolour silhouette to gold using offscreen tint
          const off = document.createElement('canvas');
          off.width = lw; off.height = lh;
          const octx = off.getContext('2d');
          octx.drawImage(logo, 0, 0, lw, lh);
          octx.globalCompositeOperation = 'source-in';
          octx.fillStyle = '#fbb03b';
          octx.fillRect(0, 0, lw, lh);
          ctx.drawImage(off, lx, ly);
          ctx.restore();
        }
      }
      else if (p.kind === 'gradient') {
        const g = ctx.createRadialGradient(w * 0.5, h * 0.45, h * 0.1,
          w * 0.5, h * 0.5, h * 0.85);
        g.addColorStop(0, '#0a4a47');
        g.addColorStop(0.55, '#003333');
        g.addColorStop(1, '#001e1e');
        ctx.fillStyle = g;
        ctx.fillRect(0, 0, w, h);
        // soft warm halo
        const h2 = ctx.createRadialGradient(w * 0.5, h * 0.4, 0,
          w * 0.5, h * 0.4, w * 0.55);
        h2.addColorStop(0, 'rgba(251,176,59,0.15)');
        h2.addColorStop(1, 'rgba(251,176,59,0)');
        ctx.fillStyle = h2;
        ctx.fillRect(0, 0, w, h);
      }
      else if (p.kind === 'signature') {
        // Deep brand-green base with warm halo behind the speaker
        const base = ctx.createRadialGradient(w * 0.5, h * 0.4, h * 0.05,
          w * 0.5, h * 0.55, h * 0.95);
        base.addColorStop(0, '#0a4a47');
        base.addColorStop(0.5, '#003333');
        base.addColorStop(1, '#001a18');
        ctx.fillStyle = base;
        ctx.fillRect(0, 0, w, h);
        // golden key-light halo, top-right (matches a 45° lighting setup)
        const halo = ctx.createRadialGradient(w * 0.78, h * 0.18, 0,
          w * 0.78, h * 0.18, w * 0.7);
        halo.addColorStop(0, 'rgba(251,176,59,0.28)');
        halo.addColorStop(0.45, 'rgba(251,176,59,0.06)');
        halo.addColorStop(1, 'rgba(251,176,59,0)');
        ctx.fillStyle = halo;
        ctx.fillRect(0, 0, w, h);
        // Yehi-Or tree logo in filigree, large, bottom-left
        const logo = signatureLogoRef.current;
        if (logo && logo.complete && logo.naturalWidth) {
          const scale = (h * 1.1) / logo.naturalHeight;
          const lw = logo.naturalWidth * scale;
          const lh = logo.naturalHeight * scale;
          ctx.save();
          ctx.globalAlpha = 0.10;
          ctx.drawImage(logo, -lw * 0.25, h - lh * 0.92, lw, lh);
          ctx.restore();
        }
        // subtle bottom vignette to anchor
        const vg = ctx.createLinearGradient(0, h * 0.6, 0, h);
        vg.addColorStop(0, 'rgba(0,0,0,0)');
        vg.addColorStop(1, 'rgba(0,0,0,0.35)');
        ctx.fillStyle = vg;
        ctx.fillRect(0, 0, w, h);
      }
      else if (p.kind === 'image' && bgImgRef.current) {
        const img = bgImgRef.current;
        const r = Math.max(w / img.width, h / img.height);
        const cw = img.width * r, ch = img.height * r;
        ctx.drawImage(img, (w - cw) / 2, (h - ch) / 2, cw, ch);
      } else {
        ctx.fillStyle = '#001e1e';
        ctx.fillRect(0, 0, w, h);
      }

      ctx.restore();
    }

    async function tick() {
      if (cancelled || !segRef.current) return;
      const v = videoRef.current;
      if (v && v.readyState >= 2 && v.videoWidth > 0) {
        try { await segRef.current.send({ image: v }); }
        catch (e) { /* ignore frame errors */ }
      }
      rafRef.current = requestAnimationFrame(tick);
    }

    init();
    return () => {
      cancelled = true;
      if (rafRef.current) cancelAnimationFrame(rafRef.current);
      if (segRef.current) {
        try { segRef.current.close(); } catch {}
      }
      segRef.current = null;
    };
  }, [usesSegmentation, stream, bgPreset, bgImageDataUrl]);

  const radius = shape === 'circle' ? '50%' : shape === 'pill' ? '40px' : '14px';

  // Placeholder mode
  if (mode === 'placeholder') {
    return (
      <div style={{
        position:'absolute', inset:0, borderRadius: radius,
        border:`2px dashed ${LBRAND.or}90`,
        background:'rgba(0, 51, 51, 0.15)',
        display:'flex', alignItems:'center', justifyContent:'center',
        color:LBRAND.or, fontFamily:'"Noto Sans", serif',
        fontSize:12, letterSpacing:'0.35em', textTransform:'uppercase',
        textAlign:'center', padding:14,
      }}>
        Zone caméra<br/>OBS overlay
      </div>
    );
  }

  // Camera OFF — brand placard with Yehi-Or logo (no caption)
  if (!enabled) {
    return (
      <div style={{
        position:'absolute', inset:0, borderRadius: radius, overflow:'hidden',
        background: `radial-gradient(70% 70% at 50% 40%, #0a4a47 0%, ${LBRAND.vert} 60%, ${LBRAND.vertDeep} 100%)`,
        boxShadow:'0 24px 60px rgba(0,0,0,0.35)',
        display:'flex', alignItems:'center', justifyContent:'center',
        padding: 30,
      }}>
        {/* warm halo */}
        <div style={{position:'absolute', inset:0,
          background:'radial-gradient(40% 40% at 75% 25%, rgba(251,176,59,0.18) 0%, rgba(251,176,59,0) 70%)'}}/>
        <img src="assets/logo-yehi-or-blanc.svg"
          style={{ width:'78%', maxWidth: 260, opacity: 0.98,
            filter: 'drop-shadow(0 8px 18px rgba(0,0,0,0.35))', position: 'relative' }}/>
      </div>
    );
  }

  if (error) {
    return (
      <div style={{
        position:'absolute', inset:0, borderRadius: radius,
        background: LBRAND.vertDeep, color:'#fff',
        display:'flex', alignItems:'center', justifyContent:'center',
        fontSize:12, padding:18, textAlign:'center',
        fontFamily:'"Noto Sans", sans-serif',
      }}>
        <div>
          <div style={{color: LBRAND.or, marginBottom:8, letterSpacing:'0.3em',
            textTransform:'uppercase', fontSize:10}}>Caméra indisponible</div>
          {error}
        </div>
      </div>
    );
  }

  const mirrorStyle = mirror ? { transform: 'scaleX(-1)' } : {};

  // Fallback CSS filter for blur presets if segmentation fails — at least
  // we get a (whole-frame) blur effect instead of a frozen green canvas.
  const fallbackFilter =
    segError && (bgPreset === 'blur' || bgPreset === 'blur-strong')
      ? (bgPreset === 'blur' ? 'blur(8px)' : 'blur(18px)')
      : 'none';

  return (
    <div style={{
      position:'absolute', inset:0, borderRadius: radius, overflow:'hidden',
      background: LBRAND.vertDeep,
      boxShadow:'0 24px 60px rgba(0,0,0,0.35)',
    }}>
      {/* Video stays mounted and playing so MediaPipe always gets fresh frames.
          When segmentation succeeds, the opaque canvas above hides it.
          When segmentation fails, the canvas isn't rendered → video shows through. */}
      <video
        ref={videoRef}
        autoPlay muted playsInline
        style={{
          position: 'absolute', inset: 0,
          width:'100%', height:'100%', objectFit:'cover',
          background: LBRAND.vertDeep,
          filter: fallbackFilter,
          ...mirrorStyle,
        }}
      />
      {usesSegmentation && !segError && (
        <canvas
          ref={canvasRef}
          style={{
            position: 'absolute', inset: 0,
            width:'100%', height:'100%', objectFit:'cover',
            background: 'transparent',
            opacity: segReady ? 1 : 0,
            transition: 'opacity 200ms',
            ...mirrorStyle,
          }}
        />
      )}
      {segLoading && (
        <div style={{
          position:'absolute', inset:0, background:'rgba(0,30,30,0.75)',
          color:'#fff', display:'flex', alignItems:'center', justifyContent:'center',
          fontSize:11, letterSpacing:'0.3em', textTransform:'uppercase',
          fontFamily:'"Noto Sans", sans-serif',
        }}>
          Chargement du fond…
        </div>
      )}
      {segError && (
        <div style={{
          position:'absolute', left:10, right:10, bottom:10,
          background:'rgba(168,69,61,0.92)', color:'#fff',
          padding:'8px 12px', borderRadius:8,
          fontSize:10, letterSpacing:'0.18em', textTransform:'uppercase',
          textAlign:'center', fontFamily:'"Noto Sans", sans-serif',
          lineHeight: 1.5,
        }}>
          Fond indisponible — vidéo brute affichée
        </div>
      )}
    </div>
  );
}

// Device picker (unchanged)
function CameraDevicePicker({ deviceId, onChange }) {
  const [devices, setDevices] = React.useState([]);
  React.useEffect(() => {
    let active = true;
    (async () => {
      try {
        if (navigator.mediaDevices?.enumerateDevices) {
          const list = await navigator.mediaDevices.enumerateDevices();
          if (active) setDevices(list.filter(d => d.kind === 'videoinput'));
        }
      } catch {}
    })();
    return () => { active = false; };
  }, []);
  return (
    <select value={deviceId || ''} onChange={e => onChange(e.target.value || null)}
      className="ed-select">
      <option value="">Caméra par défaut</option>
      {devices.map(d => (
        <option key={d.deviceId} value={d.deviceId}>
          {d.label || `Caméra ${d.deviceId.slice(0,6)}…`}
        </option>
      ))}
    </select>
  );
}

Object.assign(window, { CameraView, CameraDevicePicker, BG_PRESETS });
