// Hero Workflow Tape - plays one real KMO workflow row by row, ends in a
// concrete outcome, then cross-fades to the next. Three workflows total.
// Hero-safe per spec v2: no ERP / accounting / banking endpoints.
//
// Motion budget:
//   beat tween 180ms, stagger 700ms, outcome appears 400ms after last beat,
//   1s hold, 240ms cross-fade. ~6s/cycle, 3 cycles = ~18s loop.
//
// prefers-reduced-motion: render workflow #1 in its end-state, no rotation.

const HeroWorkflowPanel = ({ lang }) => {
  const workflows = (window.HERO_WORKFLOWS && window.HERO_WORKFLOWS[lang]) || [];
  const reduced = React.useRef(
    typeof window !== 'undefined' &&
    window.matchMedia('(prefers-reduced-motion: reduce)').matches
  );

  const [wfIdx, setWfIdx] = React.useState(0);
  const [beatIdx, setBeatIdx] = React.useState(reduced.current ? 5 : 0);
  const [showOutcome, setShowOutcome] = React.useState(reduced.current);
  const [fading, setFading] = React.useState(false);
  const [paused, setPaused] = React.useState(false);
  // restartTick increments every time the panel re-enters the viewport.
  // Including it in the sequencer effect deps restarts the loop cleanly,
  // fixing the 'panel stuck faded out' bug when the user scrolls away
  // mid-cycle and comes back later (timers in-flight skipped their
  // setFading(false) calls because inViewRef was false at the time).
  const [restartTick, setRestartTick] = React.useState(0);
  const inViewRef = React.useRef(true);
  const wrapRef = React.useRef(null);

  // Pause + restart when scrolling out of and back into the viewport
  React.useEffect(() => {
    if (!wrapRef.current) return;
    const io = new IntersectionObserver(
      ([entry]) => {
        const wasInView = inViewRef.current;
        inViewRef.current = entry.isIntersecting;
        // Edge: hidden -> visible. Bump the tick so the sequencer effect
        // restarts cleanly from the current wfIdx instead of staying stuck
        // on whatever half-faded state it was in.
        if (!wasInView && entry.isIntersecting) {
          setRestartTick(t => t + 1);
        }
      },
      { threshold: 0.2 }
    );
    io.observe(wrapRef.current);
    return () => io.disconnect();
  }, []);

  // Sequencer
  React.useEffect(() => {
    if (reduced.current) return;

    const wf = workflows[wfIdx];
    if (!wf) return;

    // PAUSED MODE (user hovered, focused, or clicked a dot while hovering)
    // Don't leave the panel half-faded. Snap to the workflow's final state -
    // all beats visible + outcome shown - so the user sees full content.
    // When the user unhovers/unfocuses, this effect re-runs with paused=false
    // and the normal sequence kicks in again.
    if (paused) {
      setBeatIdx(wf.beats.length);
      setShowOutcome(true);
      setFading(false);
      return;
    }

    const timers = [];

    // Reset to start of the new workflow
    setBeatIdx(0);
    setShowOutcome(false);
    setFading(true);

    // Fade-in delay: shorter than before (was 320ms) so the dark frame
    // between workflows is barely perceptible. The wrapper opacity
    // transition is 240ms; this gives enough time for the previous
    // workflow's fade-out to commit without leaving a long blank.
    const FADE_IN_DELAY = 180;
    timers.push(setTimeout(() => {
      if (inViewRef.current) setFading(false);
    }, FADE_IN_DELAY));

    // Beats
    const BEAT_STEP = 700;
    for (let i = 1; i <= wf.beats.length; i++) {
      timers.push(setTimeout(() => {
        if (inViewRef.current) setBeatIdx(i);
      }, FADE_IN_DELAY + i * BEAT_STEP));
    }
    // Outcome strap
    const outcomeAt = FADE_IN_DELAY + wf.beats.length * BEAT_STEP + 400;
    timers.push(setTimeout(() => {
      if (inViewRef.current) setShowOutcome(true);
    }, outcomeAt));

    // Hold + fade out
    const fadeAt = outcomeAt + 1400;
    timers.push(setTimeout(() => {
      if (inViewRef.current) setFading(true);
    }, fadeAt));

    // Advance after the fade-out transition has fully committed
    timers.push(setTimeout(() => {
      if (inViewRef.current) {
        setWfIdx((i) => (i + 1) % workflows.length);
      }
    }, fadeAt + 280));

    return () => timers.forEach(clearTimeout);
  }, [wfIdx, paused, workflows.length, restartTick]);

  // Safety watchdog: if `fading` stays true for >700ms, force it back to
  // false. Catches edge cases where state transitions get desynced (e.g.
  // paused toggled mid-cycle and a previous effect's timers cleared
  // before they could call setFading(false)). The normal fade dance is
  // 180ms (in) + 240ms transition + 280ms advance = ~700ms max in 'true'
  // state, so 700ms watchdog won't interfere with normal flow.
  React.useEffect(() => {
    if (!fading) return;
    const watchdog = setTimeout(() => setFading(false), 900);
    return () => clearTimeout(watchdog);
  }, [fading]);

  if (workflows.length === 0) return null;
  const wf = workflows[wfIdx];

  return (
    <div
      ref={wrapRef}
      onMouseEnter={() => setPaused(true)}
      onMouseLeave={() => setPaused(false)}
      onFocus={() => setPaused(true)}
      onBlur={() => setPaused(false)}
      tabIndex={0}
      aria-label={
        lang === 'nl'
          ? `Voorbeeld werkstroom: ${wf.pillTitle} in ${wf.outcome.after}.`
          : `Example workflow: ${wf.pillTitle} in ${wf.outcome.after}.`
      }
      style={{
        position: 'relative',
        width: '100%',
        maxWidth: 560,
        background: 'var(--bg-raised)',
        border: '1px solid var(--border)',
        borderRadius: 'var(--radius-card)',
        padding: 0,
        overflow: 'hidden',
        opacity: fading ? 0 : 1,
        transition: 'opacity 240ms var(--ease)',
        outline: 'none',
        fontFamily: 'var(--font-sans)',
      }}
    >
      {/* Header bar */}
      <div style={{
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'space-between',
        gap: 16,
        padding: '14px 18px',
        borderBottom: '1px solid var(--border)',
        background: 'color-mix(in oklab, var(--bg-muted) 50%, transparent)',
      }}>
        <div style={{ display: 'inline-flex', alignItems: 'center', gap: 10, minWidth: 0 }}>
          <span style={{
            width: 6, height: 6, borderRadius: 999,
            background: 'var(--accent)',
            boxShadow: '0 0 0 4px color-mix(in oklab, var(--accent) 25%, transparent)',
            flexShrink: 0,
          }}/>
          <span className="mono" style={{ color: 'var(--ink-muted)', fontSize: 10 }}>LIVE</span>
          <span style={{
            width: 1, height: 12, background: 'var(--border-strong)', flexShrink: 0,
          }}/>
          <span style={{
            fontFamily: 'var(--font-sans)',
            fontSize: 13,
            fontWeight: 500,
            color: 'var(--ink-primary)',
            whiteSpace: 'nowrap',
            overflow: 'hidden',
            textOverflow: 'ellipsis',
          }}>
            {wf.pillTitle}
          </span>
        </div>
        <span className="mono" style={{
          color: 'var(--ink-muted)', fontSize: 10, whiteSpace: 'nowrap', flexShrink: 0,
        }}>
          {wf.audience}
        </span>
      </div>

      {/* Beats */}
      <ol role="list" aria-live="off" style={{
        listStyle: 'none',
        margin: 0,
        padding: '12px 18px',
        display: 'flex',
        flexDirection: 'column',
        gap: 10,
      }}>
        {wf.beats.map((b, i) => {
          const visible = i < beatIdx;
          return (
            <li key={i} style={{
              display: 'grid',
              gridTemplateColumns: '60px 1fr auto auto',
              alignItems: 'center',
              gap: 12,
              opacity: visible ? 1 : 0,
              transform: visible ? 'translateY(0)' : 'translateY(4px)',
              transition: 'opacity 180ms var(--ease), transform 180ms var(--ease)',
              fontSize: 13,
              minHeight: 22,
            }}>
              <span className="mono" style={{ color: 'var(--ink-muted)', fontSize: 11 }}>
                {b.time}
              </span>
              <span style={{ color: 'var(--ink-primary)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                {b.label}
              </span>
              <span className="mono" style={{
                color: 'var(--ink-muted)',
                fontSize: 11,
                textAlign: 'right',
                whiteSpace: 'nowrap',
                overflow: 'hidden',
                textOverflow: 'ellipsis',
                maxWidth: 200,
              }}>
                {b.meta}
              </span>
              <span aria-hidden="true" style={{
                width: 14, height: 14, borderRadius: 999,
                display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                background: 'color-mix(in oklab, var(--accent) 18%, transparent)',
                color: 'var(--accent)',
                flexShrink: 0,
              }}>
                <svg width="9" height="9" viewBox="0 0 12 12" fill="none">
                  <path d="M2.5 6.2 5 8.7l4.5-5" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
                </svg>
              </span>
            </li>
          );
        })}
      </ol>

      {/* Outcome strap */}
      <div style={{
        borderTop: '1px solid var(--border)',
        padding: '14px 18px',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'space-between',
        gap: 16,
        flexWrap: 'wrap',
        background: 'color-mix(in oklab, var(--accent) 7%, transparent)',
        opacity: showOutcome ? 1 : 0,
        transform: showOutcome ? 'translateY(0)' : 'translateY(4px)',
        transition: 'opacity 220ms var(--ease), transform 220ms var(--ease)',
      }}>
        <div style={{ display: 'inline-flex', alignItems: 'baseline', gap: 8, minWidth: 0 }}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"
               style={{ color: 'var(--accent)', flexShrink: 0, transform: 'translateY(2px)' }}>
            <circle cx="12" cy="13" r="8"/>
            <path d="M12 9v4l2 2M9 2h6"/>
          </svg>
          <span className="mono" style={{
            color: 'var(--ink-muted)', fontSize: 12, textDecoration: 'line-through',
          }}>
            {wf.outcome.before}
          </span>
          <span className="mono" style={{ color: 'var(--ink-muted)', fontSize: 12 }}>{'→'}</span>
          <span className="mono-num" style={{
            color: 'var(--accent)', fontSize: 18, fontWeight: 600,
          }}>
            {wf.outcome.after}
          </span>
        </div>
        <div className="mono" style={{
          fontSize: 11, color: 'var(--ink-muted)', whiteSpace: 'nowrap',
        }}>
          {wf.outcome.counterPrefix}{' '}
          <span className="mono-num" style={{ color: 'var(--ink-primary)', fontWeight: 500 }}>
            {wf.outcome.counterValue}
          </span>
        </div>
      </div>

      {/* Stack footer */}
      <div style={{
        borderTop: '1px solid var(--border)',
        padding: '10px 18px',
        fontFamily: 'var(--font-mono)',
        fontSize: 10,
        letterSpacing: '0.06em',
        color: 'var(--ink-muted)',
        whiteSpace: 'nowrap',
        overflow: 'hidden',
        textOverflow: 'ellipsis',
      }}>
        {wf.stack}
      </div>

      {/* Workflow indicator dots */}
      <div style={{
        position: 'absolute',
        top: 14,
        right: 'calc(18px + 110px)',
      }}/>

      {/* Bottom-right dot indicator - clickable, skips to that workflow.
          Larger hit target (24x24 tap zone, 9px visible dot) than the
          previous 5px decorative dots. */}
      <div style={{
        position: 'absolute',
        right: 12,
        bottom: 10,
        display: 'inline-flex',
        gap: 2,
      }}>
        {workflows.map((_, i) => (
          <button
            key={i}
            type="button"
            aria-label={(lang === 'nl' ? 'Toon flow ' : 'Show flow ') + (i + 1)}
            aria-current={i === wfIdx ? 'true' : undefined}
            onClick={() => {
              if (i === wfIdx) return;
              // Just swap the index. The sequencer effect picks up the
              // change and handles fade/beats/outcome - if the user is
              // hovering (paused=true) it snaps to the final state of the
              // new workflow; otherwise it plays the full animation.
              setWfIdx(i);
            }}
            style={{
              width: 24,
              height: 24,
              padding: 0,
              border: 'none',
              background: 'transparent',
              cursor: 'pointer',
              display: 'inline-flex',
              alignItems: 'center',
              justifyContent: 'center',
              borderRadius: 999,
              transition: 'background-color 180ms var(--ease)',
            }}
            onMouseEnter={(e) => e.currentTarget.style.background = 'color-mix(in oklab, var(--accent) 12%, transparent)'}
            onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
          >
            <span aria-hidden="true" style={{
              width: i === wfIdx ? 22 : 9,
              height: 9,
              borderRadius: 999,
              background: i === wfIdx ? 'var(--accent)' : 'color-mix(in oklab, var(--ink-muted) 50%, transparent)',
              transition: 'background-color 200ms var(--ease), width 220ms var(--ease)',
              display: 'block',
            }}/>
          </button>
        ))}
      </div>
    </div>
  );
};

window.HeroWorkflowPanel = HeroWorkflowPanel;
// Keep old name available so app.jsx doesn't break if it still references it
window.HeroMotion = HeroWorkflowPanel;

// =============================================================
// HeroFlowBackdrop - ambient streamers behind the hero only.
// No labels, no nodes. Honours prefers-reduced-motion.
// =============================================================
const HeroFlowBackdrop = () => {
  const reduced = typeof window !== 'undefined' &&
    window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  const [tick, setTick] = React.useState(0);

  React.useEffect(() => {
    if (reduced) return;
    let raf;
    const start = performance.now();
    const loop = (t) => {
      setTick((t - start) / 1000);
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, [reduced]);

  // Abstract nodes - no labels. Positions across the hero canvas.
  const nodes = [
    { id: 'a', x:  90, y: 140, r: 6 },
    { id: 'b', x: 240, y: 280, r: 5 },
    { id: 'c', x: 130, y: 460, r: 6 },
    { id: 'd', x: 380, y: 550, r: 5 },
    { id: 'e', x: 500, y: 200, r: 7 },
    { id: 'f', x: 620, y: 380, r: 6 },
    { id: 'g', x: 770, y: 150, r: 5 },
    { id: 'h', x: 880, y: 360, r: 6 },
    { id: 'i', x: 820, y: 580, r: 5 },
  ];
  const nodeMap = Object.fromEntries(nodes.map(n => [n.id, n]));

  // Edges as cubic-bezier between nodes. Control points offset for organic curves.
  const edges = [
    { from: 'a', to: 'b', cx1: 140, cy1: 200, cx2: 200, cy2: 240, off: 0.00 },
    { from: 'b', to: 'e', cx1: 320, cy1: 250, cx2: 420, cy2: 220, off: 0.15 },
    { from: 'b', to: 'c', cx1: 200, cy1: 360, cx2: 160, cy2: 400, off: 0.30 },
    { from: 'c', to: 'd', cx1: 200, cy1: 510, cx2: 290, cy2: 540, off: 0.45 },
    { from: 'e', to: 'f', cx1: 540, cy1: 270, cx2: 590, cy2: 330, off: 0.10 },
    { from: 'e', to: 'g', cx1: 600, cy1: 170, cx2: 700, cy2: 150, off: 0.55 },
    { from: 'f', to: 'h', cx1: 720, cy1: 380, cx2: 820, cy2: 370, off: 0.65 },
    { from: 'd', to: 'f', cx1: 460, cy1: 510, cx2: 560, cy2: 440, off: 0.80 },
    { from: 'g', to: 'h', cx1: 820, cy1: 230, cx2: 870, cy2: 290, off: 0.20 },
    { from: 'h', to: 'i', cx1: 880, cy1: 450, cx2: 850, cy2: 520, off: 0.40 },
  ];

  // Inflow / outflow extensions: paths running off-canvas so the
  // workflow rails feel continuous past the hero edges.
  const offEdges = [
    // Entries from the left edge into 'a' and 'c'
    { x1: -150, y1:  60, c1x: -40, c1y:  90, c2x:  20, c2y: 130, x2:  90, y2: 140, off: 0.05, hasArrow: true },
    { x1: -150, y1: 420, c1x: -40, c1y: 440, c2x:  40, c2y: 460, x2: 130, y2: 460, off: 0.25, hasArrow: true },
    // Exits from 'g', 'h', 'i' off the right edge
    { x1: 770, y1: 150, c1x:  870, c1y: 130, c2x: 1000, c2y: 100, x2: 1170, y2:  60, off: 0.40, hasArrow: false },
    { x1: 880, y1: 360, c1x:  990, c1y: 360, c2x: 1060, c2y: 380, x2: 1170, y2: 400, off: 0.60, hasArrow: false },
    { x1: 820, y1: 580, c1x:  930, c1y: 620, c2x: 1040, c2y: 690, x2: 1170, y2: 760, off: 0.85, hasArrow: false },
    // Bottom-left exit from 'd'
    { x1: 380, y1: 550, c1x:  300, c1y: 660, c2x:  170, c2y: 740, x2:  -50, y2: 800, off: 0.70, hasArrow: false },
  ];

  const bz = (t, p0, p1, p2, p3) => {
    const u = 1 - t;
    return u*u*u*p0 + 3*u*u*t*p1 + 3*u*t*t*p2 + t*t*t*p3;
  };

  return (
    <svg
      viewBox="0 0 1000 800"
      preserveAspectRatio="xMidYMid slice"
      aria-hidden="true"
      style={{
        position: 'absolute', inset: 0,
        width: '100%', height: '100%',
        opacity: 0.45, pointerEvents: 'none',
        zIndex: 0,
        mixBlendMode: 'screen',
      }}
    >
      <defs>
        <marker id="hero-arrow" viewBox="0 0 10 10" refX="9" refY="5"
                markerWidth="5" markerHeight="5" orient="auto-start-reverse">
          <path d="M0,0 L10,5 L0,10 z" fill="var(--accent)" fillOpacity="0.9" />
        </marker>
      </defs>
      {/* Off-canvas extensions - workflow rails continuing past the hero edges */}
      {offEdges.map((e, i) => {
        const d = `M ${e.x1} ${e.y1} C ${e.c1x} ${e.c1y}, ${e.c2x} ${e.c2y}, ${e.x2} ${e.y2}`;
        const dashOffset = reduced ? 0 : -((tick * 14) + e.off * 18);
        return (
          <path key={`off-${i}`} d={d} fill="none"
                stroke="var(--accent)" strokeOpacity="0.55" strokeWidth="1.2"
                strokeDasharray="3 5" strokeDashoffset={dashOffset}
                strokeLinecap="round"
                markerEnd={e.hasArrow ? "url(#hero-arrow)" : undefined} />
        );
      })}
      {/* Inter-node edges - dashed workflow connectors with marching-ants animation + arrowheads */}
      {edges.map((e, i) => {
        const a = nodeMap[e.from], b = nodeMap[e.to];
        const d = `M ${a.x} ${a.y} C ${e.cx1} ${e.cy1}, ${e.cx2} ${e.cy2}, ${b.x} ${b.y}`;
        const dashOffset = reduced ? 0 : -((tick * 14) + e.off * 18);
        return (
          <path key={i} d={d} fill="none"
                stroke="var(--accent)" strokeOpacity="0.7" strokeWidth="1.2"
                strokeDasharray="3 5" strokeDashoffset={dashOffset}
                strokeLinecap="round" markerEnd="url(#hero-arrow)" />
        );
      })}
      {/* Nodes - workflow steps. Pulsing ring + solid core. */}
      {nodes.map((n, i) => {
        const phase = reduced ? 0 : (Math.sin(tick * 1.2 + i) + 1) / 2; // 0..1
        const ringR = n.r + 3 + phase * 5;
        const ringOpacity = (1 - phase) * 0.55;
        return (
          <g key={n.id}>
            {!reduced && (
              <circle cx={n.x} cy={n.y} r={ringR} fill="none"
                      stroke="var(--accent)" strokeOpacity={ringOpacity} strokeWidth="0.9" />
            )}
            <circle cx={n.x} cy={n.y} r={n.r * 0.55} fill="var(--accent)" fillOpacity="0.85" />
            <circle cx={n.x} cy={n.y} r={n.r} fill="none"
                    stroke="var(--accent)" strokeOpacity="0.7" strokeWidth="1" />
          </g>
        );
      })}
    </svg>
  );
};

window.HeroFlowBackdrop = HeroFlowBackdrop;
