// Shared interactions used across the three directions.
// Exposed on window so each direction file can pull them in.

// ─── useSitTimer ────────────────────────────────────────────────
// Counts up while the user is "sitting" (button held / toggled).
// Returns { seconds, sitting, start, stop, toggle, reset, formatted, escalation }.
function useSitTimer() {
  const [sitting, setSitting] = React.useState(false);
  const [seconds, setSeconds] = React.useState(0);
  React.useEffect(() => {
    if (!sitting) return;
    const id = setInterval(() => setSeconds(s => s + 1), 1000);
    return () => clearInterval(id);
  }, [sitting]);
  const fmt = (s) => {
    const m = Math.floor(s / 60).toString().padStart(2, '0');
    const ss = (s % 60).toString().padStart(2, '0');
    return `${m}:${ss}`;
  };
  // Escalating commentary
  const escalation = React.useMemo(() => {
    if (seconds < 5) return { stage: 0, label: 'Sit-bones: contact established.' };
    if (seconds < 15) return { stage: 1, label: 'A faint awareness of the rolled lip.' };
    if (seconds < 30) return { stage: 2, label: 'Hamstrings: filing first complaint.' };
    if (seconds < 60) return { stage: 3, label: 'Sacrum: subpoena issued.' };
    if (seconds < 120) return { stage: 4, label: 'Femurs forgetting circulation.' };
    if (seconds < 240) return { stage: 5, label: 'Lumbar drafting class action.' };
    return { stage: 6, label: 'GET UP. GET UP. GET UP.' };
  }, [seconds]);
  return {
    seconds, sitting, formatted: fmt(seconds), escalation,
    start: () => setSitting(true),
    stop: () => setSitting(false),
    toggle: () => setSitting(s => !s),
    reset: () => { setSeconds(0); setSitting(false); },
  };
}

// ─── usePetitionCount ───────────────────────────────────────────
// Persists in localStorage. Starts at a believably-high count and
// clicks add to it. Slow auto-increment to feel "live".
function usePetitionCount(seed = 24817) {
  const KEY = 'ftc-petition-count';
  const [count, setCount] = React.useState(() => {
    const raw = typeof localStorage !== 'undefined' ? localStorage.getItem(KEY) : null;
    return raw ? parseInt(raw, 10) : seed;
  });
  React.useEffect(() => {
    if (typeof localStorage !== 'undefined') localStorage.setItem(KEY, String(count));
  }, [count]);
  // Background drip — a new signature every 8-22s.
  React.useEffect(() => {
    let live = true;
    const tick = () => {
      if (!live) return;
      setCount(c => c + 1);
      setTimeout(tick, 8000 + Math.random() * 14000);
    };
    const t = setTimeout(tick, 4000 + Math.random() * 6000);
    return () => { live = false; clearTimeout(t); };
  }, []);
  return [count, () => setCount(c => c + 1)];
}

// ─── Flinch ───────────────────────────────────────────────────
// Wraps children; on hover, applies a tiny tremor and a winced label.
// Use as <Flinch>...</Flinch>; the consumer styles the wrapping span.
function Flinch({ children, intensity = 1, as = 'span', style, className, title, ...rest }) {
  const Tag = as;
  const [hover, setHover] = React.useState(false);
  const tremor = hover
    ? `translate(${(Math.random() - 0.5) * intensity}px, ${(Math.random() - 0.5) * intensity}px) rotate(${(Math.random() - 0.5) * 0.6 * intensity}deg)`
    : 'none';
  return (
    <Tag
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      className={className}
      style={{ display: 'inline-block', transform: tremor, transition: 'transform .04s linear', ...style }}
      title={title}
      {...rest}
    >
      {children}
    </Tag>
  );
}

// ─── Marquee ────────────────────────────────────────────────────
function Marquee({ children, speed = 40, className, style }) {
  const ref = React.useRef(null);
  return (
    <div className={className} style={{ overflow: 'hidden', whiteSpace: 'nowrap', ...style }}>
      <div ref={ref} style={{ display: 'inline-block', animation: `ftc-marquee ${speed}s linear infinite` }}>
        {children}{children}
      </div>
    </div>
  );
}

// inject marquee keyframes once
if (typeof document !== 'undefined' && !document.getElementById('ftc-shared-css')) {
  const s = document.createElement('style');
  s.id = 'ftc-shared-css';
  s.textContent = `
    @keyframes ftc-marquee { 0% { transform: translateX(0) } 100% { transform: translateX(-50%) } }
    @keyframes ftc-shake { 0%,100%{transform:translate(0,0) rotate(0)} 20%{transform:translate(-1px,.5px) rotate(-.4deg)} 40%{transform:translate(.8px,-.6px) rotate(.3deg)} 60%{transform:translate(-.6px,.4px) rotate(-.2deg)} 80%{transform:translate(.5px,-.3px) rotate(.4deg)} }
    @keyframes ftc-flicker { 0%,18%,22%,25%,53%,57%,100%{opacity:1} 20%,24%,55%{opacity:.4} }
    @keyframes ftc-pulse { 0%,100%{opacity:.7} 50%{opacity:1} }
    @keyframes ftc-buzz { 0%,100%{transform:translate(0,0)} 50%{transform:translate(.4px,-.4px)} }
  `;
  document.head.appendChild(s);
}

// ─── ChairAnnotations ───────────────────────────────────────────
// Hover hotspots on the chair photo. Pass children as <img>; absolute
// hotspots overlay it. positions are normalized 0..1.
function ChairAnnotations({ src, alt, points, style, captionStyle, hotspotStyle }) {
  const [active, setActive] = React.useState(null);
  return (
    <div style={{ position: 'relative', width: '100%', ...style }}>
      <img src={src} alt={alt} style={{ display: 'block', width: '100%', height: 'auto' }} />
      {points.map((p, i) => (
        <div
          key={i}
          onMouseEnter={() => setActive(i)}
          onMouseLeave={() => setActive(null)}
          style={{
            position: 'absolute', left: `${p.x * 100}%`, top: `${p.y * 100}%`,
            transform: 'translate(-50%, -50%)', cursor: 'crosshair',
            ...hotspotStyle,
          }}
        >
          <div style={{
            width: 28, height: 28, borderRadius: '50%',
            border: '1.5px solid rgba(255,255,255,.85)',
            background: active === i ? 'rgba(255,60,60,.5)' : 'rgba(255,255,255,.08)',
            boxShadow: active === i ? '0 0 0 6px rgba(255,60,60,.18)' : '0 0 0 0 rgba(255,255,255,0)',
            transition: 'all .18s', display:'flex', alignItems:'center', justifyContent:'center',
            fontFamily: 'IBM Plex Mono, monospace', fontSize: 11, color:'#fff', fontWeight:600,
          }}>{String(i + 1).padStart(2, '0')}</div>
          {active === i && (
            <div style={{
              position: 'absolute', left: '50%', top: 'calc(100% + 8px)', transform: 'translateX(-50%)',
              minWidth: 220, maxWidth: 280, padding: '10px 12px',
              background: '#fff', color: '#111', borderRadius: 2,
              boxShadow: '0 8px 28px rgba(0,0,0,.45)',
              fontFamily: 'IBM Plex Mono, monospace', fontSize: 11, lineHeight: 1.45,
              ...captionStyle,
            }}>
              <div style={{ fontWeight: 700, marginBottom: 4, letterSpacing: '.05em' }}>
                FIG. {String(i + 1).padStart(2, '0')} — {p.label}
              </div>
              <div style={{ opacity: .8 }}>{p.body}</div>
            </div>
          )}
        </div>
      ))}
    </div>
  );
}

// ─── shared chair facts ────────────────────────────────────────
const FTC_HOTSPOTS = [
  { x: 0.50, y: 0.42, label: 'THE DISH', body: 'A shallow steel basin pressed to locate, cradle, and immobilize the human pelvis. Concave by design. Forgiving by accident — never.' },
  { x: 0.18, y: 0.55, label: 'THE LIP', body: 'A 6 mm rolled hem along the seat\'s leading edge. Welcomes you with a guillotine\'s warmth across the hamstring.' },
  { x: 0.50, y: 0.30, label: 'VENTILATION', body: 'Eight perforations, ostensibly to drain rainwater. In practice: a sieve through which your last good posture quietly drains away.' },
  { x: 0.84, y: 0.62, label: 'THE WELD', body: 'Where leg meets seat. A point of structural pride and biographical inflection. Audible at room temperature.' },
];

const FTC_QUOTES = [
  { who: 'A. Marchetti', where: 'Brooklyn', text: 'I sat on it for the duration of one brunch. My posterior is still drafting a complaint.' },
  { who: 'D. Okonkwo', where: 'Austin', text: 'My chiropractor sends Tolix flowers every February.' },
  { who: 'Anonymous', where: 'Lyon', text: 'It is, somehow, both cold and hot at the same time. I do not understand how this is permitted by physics.' },
  { who: 'A cafe owner', where: 'off the record', text: 'We have a hundred of them. Customers stay for forty-six minutes on average. This is, we believe, the point.' },
  { who: 'M. Devereaux', where: 'Montréal', text: 'I have been to war. The war was kinder.' },
  { who: 'J. Park', where: 'Seoul', text: 'My grandmother\'s wooden bench from 1962 has more give. It also has more love.' },
];

const FTC_SPECS = [
  ['Year of origin', '1934'],
  ['Designer', 'Xavier Pauchard'],
  ['Material', 'Cold-pressed galvanized steel'],
  ['Weight', '11 lb / 5 kg of regret'],
  ['Padding', 'None // by design'],
  ['Seat temperature', 'Ambient, minus three degrees'],
  ['Primary contact zones', 'Sacrum · sit-bones · hamstring artery'],
  ['Time to first numbness', '≈ 12 minutes'],
  ['Acoustic profile', 'Cathedral-grade ring on contact'],
  ['Stackability', '25 high // the only honest virtue'],
  ['Lifespan', 'Infinite // unfortunately'],
  ['Comfort rating', '0 / 10'],
  ['Recommended use', 'Hostile architecture'],
  ['Not recommended for', 'Humans'],
];

const PETITION_MAILTO = 'mailto:petition@fuckthischair.com?subject=Add%20my%20name%20to%20the%20petition&body=I%2C%20%5Byour%20name%5D%2C%20hereby%20declare%20that%20I%20fucking%20hate%20this%20chair.';

Object.assign(window, {
  useSitTimer, usePetitionCount, Flinch, Marquee, ChairAnnotations,
  FTC_HOTSPOTS, FTC_QUOTES, FTC_SPECS, PETITION_MAILTO,
});
