/* =========================================================
   ALLEGEDLY — Shared components (atomic)
   Exported to window so other files can use them.
   ========================================================= */

const { useState, useEffect, useRef, useCallback, useMemo } = React;

/* ---- footnote "Allegedly" flip — period→comma + word slide-in on scroll ---- */
function Alg({ note }) {
  const [show, setShow] = useState(false);
  return (
    <span className="alg-wrap">
      {/* period → comma flip (mirrors vloop flip-from / flip-to) */}
      <span className="alg-punct" aria-hidden="true">
        <span className="alg-punct-from">.</span>
        <span className="alg-punct-to">,</span>
      </span>
      {/* "Allegedly" slides in after the comma */}
      <span className="alg-word"> Allegedly</span>
      {/* asterisk — hover opens footnote */}
      <span
        className="as"
        style={{ cursor:'help', position:'relative' }}
        onMouseEnter={() => setShow(true)}
        onMouseLeave={() => setShow(false)}
      >*{show && note && (
          <span style={{
            position:'absolute', left:'100%', top:'-4px',
            marginLeft:8,
            background:'var(--panel-2)', color:'var(--light)',
            fontFamily:'var(--mono)', fontSize:10, fontStyle:'normal',
            padding:'8px 12px', maxWidth:280, lineHeight:1.5, zIndex:50,
            border:'1px solid var(--line)', whiteSpace:'normal',
            pointerEvents:'none'
          }}>{note}</span>
        )}
      </span>
    </span>
  );
}

/* ---- inline footnote at the bottom of a block ---- */
function FN({ children }) {
  return <p className="fn">{children}</p>;
}

/* ---- redaction bar — peels off like tape on click (verlet physics) ---- */
function Redact({ children, peekRadius = 140 }) {
  const wrapRef = useRef(null);
  const tapeRef = useRef(null);
  const [shown, setShown] = useState(false);
  const [peeling, setPeeling] = useState(false);
  const [peek, setPeek] = useState(false);

  useEffect(() => {
    if (shown) return;
    function onMove(e) {
      const el = wrapRef.current; if (!el) return;
      const r = el.getBoundingClientRect();
      const cx = r.left + r.width/2;
      const cy = r.top + r.height/2;
      const d = Math.hypot(e.clientX - cx, e.clientY - cy);
      setPeek(d < peekRadius);
    }
    window.addEventListener('mousemove', onMove, { passive:true });
    return () => window.removeEventListener('mousemove', onMove);
  }, [shown, peekRadius]);

  // Verlet sim: anchor top-left, two free points (top-right, bottom-right).
  // Tape "peels" — top-right falls under gravity, drags bottom corners with it.
  function peelOff() {
    if (shown || peeling) return;
    setPeeling(true);
    const tape = tapeRef.current;
    if (!tape) { setShown(true); return; }
    const rect = tape.getBoundingClientRect();
    const w = rect.width, h = rect.height;
    // 4 corners as points: [x, y, prevX, prevY]
    // anchor = top-left = points[0]; fixed
    const p = [
      [0,   0,    0,   0   ],
      [w,   0,    w,   0   ],
      [w,   h,    w,   h   ],
      [0,   h,    0,   h   ],
    ];
    const G = 0.85;       // gravity
    const DAMP = 0.985;
    const KICK_X = 1.4 + Math.random() * 1.2; // initial right-ward kick
    const KICK_Y = -2.4 - Math.random() * 1.6; // initial up-ward kick
    // give corners 1,2 a kick so they lift up before falling
    p[1][2] -= KICK_X; p[1][3] -= KICK_Y;
    p[2][2] -= KICK_X * 0.7; p[2][3] -= KICK_Y * 0.4;
    p[3][2] += KICK_X * 0.3;

    // rest lengths for the 4 sides (rigid-ish)
    const restW = w, restH = h, restD = Math.hypot(w, h);
    // edges: (a, b, len)
    const edges = [
      [0,1,restW], [1,2,restH], [2,3,restW], [3,0,restH],
      [0,2,restD], [1,3,restD], // diagonals
    ];

    let frames = 0;
    let raf;
    function step() {
      // verlet integrate (skip anchor 0)
      for (let i = 1; i < 4; i++) {
        const vx = (p[i][0] - p[i][2]) * DAMP;
        const vy = (p[i][1] - p[i][3]) * DAMP;
        p[i][2] = p[i][0];
        p[i][3] = p[i][1];
        p[i][0] += vx;
        p[i][1] += vy + G;
      }
      // satisfy constraints a few iterations
      for (let it = 0; it < 4; it++) {
        for (let e = 0; e < edges.length; e++) {
          const a = edges[e][0], b = edges[e][1], len = edges[e][2];
          const dx = p[b][0] - p[a][0];
          const dy = p[b][1] - p[a][1];
          const d = Math.hypot(dx, dy) || 1;
          const diff = (d - len) / d;
          // anchor (0) is fixed; if a==0 push all into b
          if (a === 0) {
            p[b][0] -= dx * diff;
            p[b][1] -= dy * diff;
          } else {
            p[a][0] += dx * 0.5 * diff;
            p[a][1] += dy * 0.5 * diff;
            p[b][0] -= dx * 0.5 * diff;
            p[b][1] -= dy * 0.5 * diff;
          }
        }
      }
      // render via clip-path polygon
      const poly = `${p[0][0]}px ${p[0][1]}px, ${p[1][0]}px ${p[1][1]}px, ${p[2][0]}px ${p[2][1]}px, ${p[3][0]}px ${p[3][1]}px`;
      tape.style.clipPath = `polygon(${poly})`;
      // also rotate using avg of top-right vector
      const angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]) * 180 / Math.PI;
      tape.style.transform = `translateZ(0)`;
      tape.style.setProperty('--peel-angle', angle + 'deg');
      // opacity fade past 30 frames
      if (frames > 30) {
        tape.style.opacity = String(Math.max(0, 1 - (frames - 30) / 25));
      }
      frames++;
      if (frames > 60) {
        // finished
        setShown(true);
        setPeeling(false);
        return;
      }
      raf = requestAnimationFrame(step);
    }
    raf = requestAnimationFrame(step);
  }

  return (
    <span
      ref={wrapRef}
      className={`redact${shown ? ' shown' : ''}${peek && !shown ? ' peek' : ''}${peeling ? ' peeling' : ''}`}
      onClick={peelOff}
      title={shown ? '' : 'Click to declassify'}
    >
      {children}
      {!shown && (
        <span ref={tapeRef} className="redact-tape" aria-hidden="true">
          <span className="redact-tape-label">CLASSIFIED</span>
        </span>
      )}
    </span>
  );
}

/* ---- marquee ticker ---- */
function Marquee({ items, variant = '', speed = 'normal' }) {
  // repeat items 2x for seamless loop
  const repeated = [...items, ...items];
  return (
    <div className={`marquee ${variant} ${speed === 'fast' ? 'fast' : ''}`} data-zone="MARQUEE">
      <div className="track">
        {repeated.map((it, i) => (
          <span key={i} style={{ display:'inline-flex', alignItems:'center', gap:'inherit' }}>
            {it}
            <span className="star" aria-hidden="true">★</span>
          </span>
        ))}
      </div>
    </div>
  );
}

/* ---- counts-up number ---- */
function CountUp({ to, prefix = '', suffix = '', decimals = 0, duration = 1400 }) {
  const ref = useRef(null);
  const [val, setVal] = useState(0);
  useEffect(() => {
    if (!ref.current) return;
    const io = new IntersectionObserver((es) => {
      es.forEach(e => {
        if (e.isIntersecting) {
          const start = performance.now();
          function tick(t) {
            const p = Math.min(1, (t - start) / duration);
            const ease = 1 - Math.pow(1 - p, 3);
            setVal(to * ease);
            if (p < 1) requestAnimationFrame(tick);
          }
          requestAnimationFrame(tick);
          io.disconnect();
        }
      });
    }, { threshold:0.4 });
    io.observe(ref.current);
    return () => io.disconnect();
  }, [to, duration]);
  const formatted = val.toLocaleString(undefined, { minimumFractionDigits:decimals, maximumFractionDigits:decimals });
  return <span ref={ref} className="tabular">{prefix}{formatted}{suffix}</span>;
}

/* ---- magnetic button ---- */
function Magnet({ children, className = '', strength = 0.25, as = 'button', ...rest }) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current; if (!el) return;
    function onMove(e) {
      const r = el.getBoundingClientRect();
      const cx = r.left + r.width/2;
      const cy = r.top + r.height/2;
      const dx = (e.clientX - cx) * strength;
      const dy = (e.clientY - cy) * strength;
      const d = Math.hypot(e.clientX - cx, e.clientY - cy);
      if (d < 140) el.style.transform = `translate(${dx}px, ${dy}px)`;
      else el.style.transform = '';
    }
    function onLeave(){ el.style.transform = ''; }
    window.addEventListener('mousemove', onMove, { passive:true });
    el.addEventListener('mouseleave', onLeave);
    return () => {
      window.removeEventListener('mousemove', onMove);
      el.removeEventListener('mouseleave', onLeave);
    };
  }, [strength]);
  const Tag = as;
  return <Tag ref={ref} className={className} {...rest}>{children}</Tag>;
}

/* ---- SKU can mockup ---- */
function SKU({ variant, name, sub, ingredients, vol = '12 FL OZ', batch = 'NO. 001/A' }) {
  return (
    <div className={`sku-can sku-${variant}`} data-zone={`SKU // ${name}`}>
      <div className="lid"></div>
      <div className="can-top">
        <span>★ ALLEGEDLY™</span>
        <span>{batch}</span>
      </div>
      <div>
        <div className="can-name">{name}</div>
        <div className="can-sub">"{sub}"</div>
      </div>
      <div className="can-bot">
        <div className="vol">{vol}</div>
        <div style={{marginTop:4, opacity:0.7}}>{ingredients}</div>
        <div style={{marginTop:8, opacity:0.5}}>SUPP. FACTS — TBD<br/>NOT EVALUATED BY FDA</div>
      </div>
    </div>
  );
}

/* ---- eyebrow label ---- */
function Eyebrow({ n, children }) {
  return (
    <p className="eyebrow reveal">
      {n && <span style={{ color:'var(--silver)', opacity:0.8, marginRight:8 }}>{n}</span>}
      {children}
    </p>
  );
}

/* ---- big section divider line ---- */
function HR() { return <div className="div-line"></div>; }

/* =========================================================
   LogoMark — the asterisk IS the brand
   The same * that trails every "Allegedly" claim.
   ========================================================= */
function LogoMark({ size, className = '' }) {
  return (
    <span
      className={'logo-mark ' + className}
      aria-label="Allegedly"
      style={size ? { fontSize: size } : undefined}
    >*</span>
  );
}

/* Logo lockup: mark + wordmark */
function Logo({ size = 'md' }) {
  return (
    <span className={'logo logo--' + size} aria-label="Allegedly">
      <LogoMark />
      <span className="logo-wordmark">ALLEGEDLY</span>
    </span>
  );
}

/* expose globally */
Object.assign(window, { Alg, FN, Redact, Marquee, CountUp, Magnet, SKU, Eyebrow, HR, LogoMark, Logo });
