/* ============ Genius — composants de chargement (18/09/2026) ============
   CampaignGenerationLoader : « l'atelier Genius » (étoile + 3 cartes e-mail / SMS / visuel). variant = 'full' | 'compact' | 'card'.
   VideoGenerationLoader    : storyboard + timeline au format 9:16, étapes réelles si fournies.
   VideoPlayer              : lecteur avec poster + petit indicateur pendant le chargement / la mise en mémoire tampon.
   useLateNotice            : message calme quand ça dure plus longtemps que d'habitude (sans annoncer de délai).
   Décoratif et honnête : aucune progression inventée ; un résultat s'affiche dès qu'il arrive (le composant est simplement démonté). */
const GL_SPARK = 'brand/genius-spark.svg';

function useReducedMotion() {
  const [rm, setRm] = React.useState(() => { try { return window.matchMedia('(prefers-reduced-motion: reduce)').matches; } catch (e) { return false; } });
  React.useEffect(() => { try { const m = window.matchMedia('(prefers-reduced-motion: reduce)'); const f = e => setRm(e.matches); m.addEventListener ? m.addEventListener('change', f) : m.addListener(f); return () => { m.removeEventListener ? m.removeEventListener('change', f) : m.removeListener(f); }; } catch (e) {} }, []);
  return rm;
}
/* true quand le traitement dure plus de `afterMs` (défaut 3 min). Timer nettoyé au démontage. */
function useLateNotice(active, afterMs = 180000) {
  const [late, setLate] = React.useState(false);
  React.useEffect(() => { if (!active) { setLate(false); return; } const t = setTimeout(() => setLate(true), afterMs); return () => clearTimeout(t); }, [active, afterMs]);
  return late;
}
function GlSteps({ steps, current }) {
  if (!steps || !steps.length) return null;
  const idx = Math.max(0, steps.indexOf(current));
  return <div className="gl-steps" aria-hidden="true">{steps.map((s, i) => <span key={s} className={'gl-step' + (i < idx ? ' done' : i === idx ? ' now' : '')}><i />{s}</span>)}</div>;
}
/* variant : 'full' (zone de résultats vide), 'compact' (bandeau, anciens résultats conservés), 'card' (zone image d'une carte) */
function CampaignGenerationLoader({ variant = 'full', title = 'Genius prépare vos campagnes…', sub = 'Vos idées prennent forme.', steps, current, late, lateText = 'Toujours en cours : cela prend un peu plus de temps que d’habitude. Vos contenus déjà prêts sont conservés.' }) {
  const rm = useReducedMotion();
  const cls = 'gl-atelier' + (variant === 'compact' ? ' compact' : variant === 'card' ? ' card' : '') + (rm ? ' rm' : '');
  const stage = <div className="gl-stage" aria-hidden="true">
    <span className="gl-blob" />
    <div className="gl-card gl-card-mail"><i className="gl-line w65" /><i className="gl-line w50" /><i className="gl-img" /><i className="gl-btn" /></div>
    <div className="gl-card gl-card-sms"><i className="gl-line" /><i className="gl-line w65" /><i className="gl-line w50" /></div>
    <div className="gl-card gl-card-visual"><i className="gl-img" /><i className="gl-line w50" /></div>
    <img className="gl-star" src={GL_SPARK} alt="" />
  </div>;
  if (variant === 'compact') return <div className={cls} role="status" aria-live="polite">{stage}<div className="gl-txt"><span className="gl-t">{title}</span><span className="gl-s">{late ? lateText : sub}</span></div></div>;
  return <div className={cls} role="status" aria-live="polite">
    {stage}
    <div className="gl-t">{title}</div>
    <div className="gl-s">{sub}</div>
    <GlSteps steps={steps} current={current} />
    {late && <div className="gl-late">{lateText}</div>}
  </div>;
}
/* phase : 'prep' | 'gen' | 'final' (états réels du serveur : scénario → Sora → habillage) */
const VL_PHASES = [['prep', 'Préparation'], ['gen', 'Génération'], ['final', 'Finalisation']];
function VideoGenerationLoader({ phase, title = 'Genius prépare votre vidéo…', sub = 'Cela peut prendre quelques minutes.', late, lateText = 'Toujours en cours. La génération continue côté serveur, vous pouvez patienter ici.' }) {
  const rm = useReducedMotion();
  const idx = Math.max(0, VL_PHASES.findIndex(p => p[0] === phase));
  return <div className={'vl' + (rm ? ' rm' : '')} role="status" aria-live="polite" aria-label={title + ' ' + (VL_PHASES[idx] ? VL_PHASES[idx][1] : '')}>
    <span className="vl-blob" aria-hidden="true" />
    <img className="vl-star" src={GL_SPARK} alt="" />
    <div className="vl-frames" aria-hidden="true">
      <div className="vl-frame"><i className="vl-shape" /><div className="vl-lines"><i /><i /></div></div>
      <div className="vl-frame"><i className="vl-shape wide" /><div className="vl-lines"><i /><i /></div></div>
      <div className="vl-frame"><i className="vl-shape tall" /><div className="vl-lines"><i /><i /></div></div>
    </div>
    <div className="vl-timeline" aria-hidden="true"><span className="vl-cursor" /></div>
    <div className="vl-txt"><div className="vl-t">{title}</div><div className="vl-s">{late ? lateText : sub}</div></div>
    {phase && <div className="gl-steps" style={{ marginTop: 2 }} aria-hidden="true">{VL_PHASES.map(([k, l], i) => <span key={k} className={'gl-step' + (i < idx ? ' done' : i === idx ? ' now' : '')}><i />{l}</span>)}</div>}
  </div>;
}
/* Lecteur : poster conservé, petit anneau discret pendant le chargement initial et la mise en mémoire tampon. Jamais de lecture automatique. */
function VideoPlayer({ src, poster, width = 216, height = 384, className = '' }) {
  const [buf, setBuf] = React.useState(false);
  const [loaded, setLoaded] = React.useState(false);
  return <span className={'vp vl-player ' + className}>
    <video controls playsInline preload="metadata" poster={poster} src={src} style={{ width, height, borderRadius: 14, background: '#000', display: 'block', boxShadow: 'var(--shadow)' }}
      onWaiting={() => setBuf(true)} onPlaying={() => { setBuf(false); setLoaded(true); }} onCanPlay={() => { setBuf(false); setLoaded(true); }} onLoadStart={() => setLoaded(false)} onStalled={() => setBuf(true)} />
    {buf && <span className="vp-buf" role="status" aria-label="Chargement de la vidéo" />}
  </span>;
}
/* Image : régénération ciblée (kit v2 variante image). state : 'loading' | 'stalled' | 'error' ; onRetry affiché seulement en erreur et si fourni (l'appelant décide si relancer est sûr). */
function ImageGenerationLoader({ compact, state = 'loading', title, sub, onRetry }) {
  const rm = useReducedMotion();
  const t = title || (state === 'error' ? 'L’image n’a pas pu être générée.' : state === 'stalled' ? 'Toujours en cours…' : 'Genius prépare l’image…');
  const st = sub != null ? sub : (state === 'error' ? 'L’image actuelle est conservée.' : state === 'stalled' ? 'Le traitement continue côté serveur.' : 'L’image actuelle reste en place jusqu’à votre validation.');
  return <div className={'il' + (compact ? ' compact' : '') + (state === 'error' ? ' error' : '') + (rm ? ' rm' : '')} role={state === 'error' ? 'alert' : 'status'} aria-live="polite">
    <div className="il-stage" aria-hidden="true"><span className="il-circle" /><span className="il-card"><img src={GL_SPARK} alt="" /></span></div>
    <div><div className="il-t">{t}</div>{st && <div className="il-s">{st}</div>}{state === 'error' && onRetry && <div className="gl-retry"><Btn size="sm" kind="ghost" onClick={onRetry}>Réessayer</Btn></div>}</div>
  </div>;
}
Object.assign(window, { CampaignGenerationLoader, VideoGenerationLoader, ImageGenerationLoader, VideoPlayer, useReducedMotion, useLateNotice });
