// Shared primitives for Erobella Survey Tool

function Wordmark({ size = 22, color, style }) {
  return (
    <span className="wordmark" style={{
      fontSize: size,
      color: color || 'var(--ink)',
      fontFamily: 'var(--font-display)',
      fontWeight: 400,
      letterSpacing: '0.02em',
      lineHeight: 1,
      display: 'inline-block',
      ...style,
    }}>EROBELLA</span>
  );
}

function Eyebrow({ children, style }) {
  return <span className="eyebrow" style={style}>{children}</span>;
}

function Hair({ ink, style }) {
  return <hr className={`hair ${ink ? 'hair-ink' : ''}`} style={style} />;
}

// simple inline icons (stroke, 1.25px)
const Icon = {
  plus: (s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round">
      <path d="M12 5v14M5 12h14"/>
    </svg>
  ),
  arrow: (s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <path d="M5 12h14M13 5l7 7-7 7"/>
    </svg>
  ),
  back: (s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <path d="M19 12H5M11 19l-7-7 7-7"/>
    </svg>
  ),
  check: (s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <path d="M20 6L9 17l-5-5"/>
    </svg>
  ),
  x: (s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round">
      <path d="M18 6L6 18M6 6l12 12"/>
    </svg>
  ),
  drag: (s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="currentColor">
      <circle cx="9" cy="6" r="1.2"/><circle cx="15" cy="6" r="1.2"/>
      <circle cx="9" cy="12" r="1.2"/><circle cx="15" cy="12" r="1.2"/>
      <circle cx="9" cy="18" r="1.2"/><circle cx="15" cy="18" r="1.2"/>
    </svg>
  ),
  trash: (s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <path d="M3 6h18M8 6V4a2 2 0 012-2h4a2 2 0 012 2v2M6 6l1 14a2 2 0 002 2h6a2 2 0 002-2l1-14"/>
    </svg>
  ),
  copy: (s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <rect x="9" y="9" width="12" height="12" rx="1"/><path d="M5 15V5a2 2 0 012-2h10"/>
    </svg>
  ),
  eye: (s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/>
    </svg>
  ),
  link: (s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <path d="M10 13a5 5 0 007.07 0l3-3a5 5 0 00-7.07-7.07l-1 1"/>
      <path d="M14 11a5 5 0 00-7.07 0l-3 3a5 5 0 007.07 7.07l1-1"/>
    </svg>
  ),
  search: (s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round">
      <circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/>
    </svg>
  ),
  chev: (s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <path d="M6 9l6 6 6-6"/>
    </svg>
  ),
  settings: (s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="12" cy="12" r="3"/>
      <path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 01-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/>
    </svg>
  ),
  sparkle: (s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="currentColor">
      <path d="M12 2l1.6 5.4L19 9l-5.4 1.6L12 16l-1.6-5.4L5 9l5.4-1.6z"/>
    </svg>
  ),
  logout: (s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
      <path d="M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4M16 17l5-5-5-5M21 12H9"/>
    </svg>
  ),
};

// simple toggle
function Toggle({ on, onChange, size = 36 }) {
  return (
    <button
      onClick={() => onChange(!on)}
      style={{
        width: size, height: size * 0.56, borderRadius: 999,
        background: on ? 'var(--ink)' : 'var(--line)',
        border: 'none', padding: 2, cursor: 'pointer',
        position: 'relative', transition: 'background .15s',
      }}
    >
      <span style={{
        display: 'block', width: size * 0.44, height: size * 0.44, borderRadius: 999,
        background: '#fff', transition: 'transform .18s',
        transform: on ? `translateX(${size * 0.44}px)` : 'translateX(0)',
      }}/>
    </button>
  );
}

// star sparkle helper for thank-you (decorative, allowed: simple geometry)
function Asterisk({ size = 16, color = 'var(--red)' }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill={color}>
      <path d="M12 2v20M2 12h20M4.93 4.93l14.14 14.14M19.07 4.93L4.93 19.07" stroke={color} strokeWidth="1.6" strokeLinecap="round"/>
    </svg>
  );
}

// Whole-number stepper: big numeral, −/+ either side (tap = 1, hold = accelerating
// repeat), tap the numeral to type on the numeric keypad. Starts blank rather than
// at a default so the starting number doesn't anchor answers. 0 is a real answer.
function NumberStepper({ value, onChange, min, max, unit, unitOne, label }) {
  const t = useT();
  const lo = Number.isFinite(+min) && min !== '' && min !== null ? Math.round(+min) : 0;
  const hi = Number.isFinite(+max) && max !== '' && max !== null ? Math.max(lo, Math.round(+max)) : Infinity;
  const has = typeof value === 'number' && Number.isFinite(value);
  const clamp = (n) => Math.min(hi, Math.max(lo, n));

  const [draft, setDraft] = React.useState(null); // raw text while typing
  const [focus, setFocus] = React.useState(false);
  const inputRef = React.useRef(null);
  const valRef = React.useRef(value);
  const timerRef = React.useRef(null);
  const heldRef = React.useRef(false);
  valRef.current = value;

  React.useEffect(() => () => clearTimeout(timerRef.current), []);

  const roll = (dir) => {
    const el = inputRef.current;
    if (!el || !el.animate) return;
    if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
    el.animate(
      [{ transform: `translateY(${dir > 0 ? 14 : -14}px)`, opacity: 0.2 }, { transform: 'none', opacity: 1 }],
      { duration: 180, easing: 'cubic-bezier(.2,.8,.2,1)' }
    );
  };

  // Returns false when already at a bound (so a hold can stop).
  const step = (dir) => {
    const cur = valRef.current;
    let next;
    if (typeof cur === 'number') next = clamp(cur + dir);
    else next = (0 < lo || 0 > hi) ? clamp(0) : clamp(dir); // blank behaves like 0
    setDraft(null);
    if (next === cur) return false;
    valRef.current = next;
    onChange(next);
    roll(dir);
    return true;
  };

  const stopHold = () => { clearTimeout(timerRef.current); timerRef.current = null; };
  const startHold = (dir) => (e) => {
    if (e.button !== undefined && e.button !== 0) return;
    heldRef.current = false;
    stopHold();
    let n = 0;
    const tick = () => {
      heldRef.current = true;
      if (!step(dir)) { stopHold(); return; }
      n++;
      timerRef.current = setTimeout(tick, n > 14 ? 35 : n > 5 ? 70 : 120);
    };
    timerRef.current = setTimeout(tick, 380);
  };
  const tap = (dir) => (e) => {
    // A hold already stepped; swallow the click that follows release.
    if (heldRef.current && e.detail !== 0) { heldRef.current = false; return; }
    step(dir);
  };

  const onType = (e) => {
    const raw = e.target.value.replace(lo < 0 ? /[^\d-]/g : /\D/g, '').slice(0, 7);
    setDraft(raw);
    if (raw === '' || raw === '-') { onChange(undefined); return; }
    const n = parseInt(raw, 10);
    if (Number.isFinite(n) && n >= lo && n <= hi) onChange(n);
  };
  const commit = () => {
    setFocus(false);
    if (draft === null) return;
    const n = parseInt(draft, 10);
    onChange(draft === '' || !Number.isFinite(n) ? undefined : clamp(n));
    setDraft(null);
  };
  const onKey = (e) => {
    const d = { ArrowUp: 1, ArrowDown: -1, PageUp: 10, PageDown: -10 }[e.key];
    if (d) { e.preventDefault(); step(d); }
    else if (e.key === 'Enter') { e.preventDefault(); e.target.blur(); }
  };

  const typedN = draft !== null && draft !== '' ? parseInt(draft, 10) : null;
  const outOfRange = typedN !== null && Number.isFinite(typedN) && (typedN < lo || typedN > hi);
  const shown = draft !== null ? draft : (has ? String(value) : '');
  const unitText = has && value === 1 ? (unitOne || unit) : unit;

  let hint = null, hintRed = false;
  if (outOfRange) { hint = hi === Infinity ? t('stepMin', { lo }) : t('stepRange', { lo, hi }); hintRed = true; }
  else if (has && value >= hi && !focus) hint = t('stepMax', { hi });
  else if (!has && draft === null) hint = t('stepHint');

  const glyph = (plus) => (
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round">
      <path d={plus ? 'M12 5v14M5 12h14' : 'M5 12h14'}/>
    </svg>
  );
  const btn = (dir) => (
    <button
      type="button"
      className="nstep-btn"
      aria-label={dir > 0 ? t('inc') : t('dec')}
      disabled={has && (dir > 0 ? value >= hi : value <= lo)}
      onPointerDown={startHold(dir)}
      onPointerUp={stopHold}
      onPointerLeave={stopHold}
      onPointerCancel={stopHold}
      onContextMenu={e => e.preventDefault()}
      onClick={tap(dir)}
    >{glyph(dir > 0)}</button>
  );

  return (
    <div>
      <div className={'nstep' + (has ? ' is-set' : '') + (focus ? ' is-focus' : '')}>
        {btn(-1)}
        <div className="nstep-mid" onClick={() => inputRef.current && inputRef.current.focus()}>
          <input
            ref={inputRef}
            className="nstep-num"
            type="text"
            inputMode="numeric"
            pattern={lo < 0 ? undefined : '[0-9]*'}
            autoComplete="off"
            role="spinbutton"
            aria-label={label || t('numberAria')}
            aria-valuemin={lo}
            aria-valuemax={hi === Infinity ? undefined : hi}
            aria-valuenow={has ? value : undefined}
            placeholder="–"
            value={shown}
            onChange={onType}
            onFocus={e => { setFocus(true); const el = e.target; requestAnimationFrame(() => el.select()); }}
            onBlur={commit}
            onKeyDown={onKey}
          />
          <span className="nstep-rule"/>
          {unitText && <span className="nstep-unit">{unitText}</span>}
        </div>
        {btn(1)}
      </div>
      <div className={'nstep-hint' + (hintRed ? ' is-red' : '')} aria-live="polite">{hint || ' '}</div>
    </div>
  );
}

Object.assign(window, { Wordmark, Eyebrow, Hair, Icon, Toggle, Asterisk, NumberStepper });
