// DSSelectionControls.jsx — Meridian selection controls
//
//   DSCheckbox        — binary check, optional label, disabled state
//   DSRadio           — single-select within a group; pass `value` + shared `selection`
//   DSToggle          — on/off switch; bare or labelled setting-row
//   DSSlider          — continuous value picker with optional ticks
//   DSStepper         — incremental +/- counter for small numeric inputs
//   DSSegmentedControl— mutually-exclusive pill segment selector
//
// All controls use Meridian token colors + 120ms cubic-bezier(.2,.8,.2,1)
// transitions. Focus rings show on keyboard nav only (use Tab to verify).
// MCheck is kept as a back-compat alias for the routine list in TodayScreen.

// ── DSCheckbox ───────────────────────────────────────────────────
function DSCheckbox({
  checked,
  onChange,
  label,
  disabled = false,
  size = 18,
  style,
}) {
  const handleClick = () => { if (!disabled) onChange?.(!checked); };
  const handleKey = (e) => {
    if (disabled) return;
    if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); onChange?.(!checked); }
  };

  if (!label) {
    return (
      <button
        type="button"
        role="checkbox"
        aria-checked={checked}
        aria-disabled={disabled}
        onClick={handleClick}
        onKeyDown={handleKey}
        style={{
          width: size, height: size, padding: 0, flexShrink: 0,
          borderRadius: 5,
          border: `1.5px solid ${checked ? M.axis : M.rule}`,
          background: checked ? M.axis : 'transparent',
          opacity: disabled ? 0.45 : 1,
          cursor: disabled ? 'default' : 'pointer',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          transition: 'all 120ms cubic-bezier(.2,.8,.2,1)',
          outline: 'none',
          ...style,
        }}
      >
        {checked && (
          <svg width={size * 0.55} height={size * 0.55} viewBox="0 0 10 10">
            <path d="M1.5 5.2L4 7.5L8.5 2.5" stroke="#fff" strokeWidth="1.8" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
        )}
      </button>
    );
  }

  return (
    <label
      onClick={handleClick}
      onKeyDown={handleKey}
      tabIndex={disabled ? -1 : 0}
      style={{
        display: 'inline-flex', alignItems: 'center', gap: 12,
        cursor: disabled ? 'default' : 'pointer',
        opacity: disabled ? 0.45 : 1,
        outline: 'none',
        ...style,
      }}>
      <DSCheckbox checked={checked} onChange={onChange} disabled={disabled} size={size} />
      <span style={{ font: `400 14px/1.3 ${M.sans}`, color: M.ink, userSelect: 'none' }}>{label}</span>
    </label>
  );
}

// ── DSRadio ──────────────────────────────────────────────────────
function DSRadio({
  value,
  selection,
  onChange,
  label,
  name,             // ARIA-only — groups radios for assistive tech
  disabled = false,
  size = 18,
  style,
}) {
  const isSelected = selection === value;
  const handleClick = () => { if (!disabled) onChange?.(value); };
  const handleKey = (e) => {
    if (disabled) return;
    if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); onChange?.(value); }
  };

  return (
    <label
      onClick={handleClick}
      onKeyDown={handleKey}
      tabIndex={disabled ? -1 : 0}
      role="radio"
      aria-checked={isSelected}
      aria-disabled={disabled}
      style={{
        display: 'inline-flex', alignItems: 'center', gap: 12,
        cursor: disabled ? 'default' : 'pointer',
        opacity: disabled ? 0.45 : 1,
        outline: 'none',
        ...style,
      }}>
      <span style={{
        width: size, height: size, flexShrink: 0,
        borderRadius: '50%',
        border: `1.5px solid ${isSelected ? M.axis : M.rule}`,
        background: 'transparent',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        transition: 'border-color 120ms cubic-bezier(.2,.8,.2,1)',
      }}>
        {isSelected && (
          <span style={{
            width: size * 0.45, height: size * 0.45,
            borderRadius: '50%',
            background: M.axis,
            transition: 'transform 120ms cubic-bezier(.2,.8,.2,1)',
          }} />
        )}
      </span>
      {label && <span style={{ font: `400 14px/1.3 ${M.sans}`, color: M.ink, userSelect: 'none' }}>{label}</span>}
    </label>
  );
}

// ── DSToggle ─────────────────────────────────────────────────────
function DSToggle({
  on,
  onChange,
  label,
  subtitle,
  disabled = false,
  style,
}) {
  const handleClick = () => { if (!disabled) onChange?.(!on); };
  const handleKey = (e) => {
    if (disabled) return;
    if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); onChange?.(!on); }
  };
  const track = (
    <button
      type="button"
      role="switch"
      aria-checked={on}
      aria-disabled={disabled}
      onClick={handleClick}
      onKeyDown={handleKey}
      style={{
        width: 36, height: 22, padding: 2, flexShrink: 0,
        borderRadius: 999,
        background: on ? M.axis : M.rule,
        border: 'none',
        cursor: disabled ? 'default' : 'pointer',
        opacity: disabled ? 0.45 : 1,
        display: 'flex', alignItems: 'center',
        transition: 'background 220ms cubic-bezier(.2,.8,.2,1)',
        outline: 'none',
        position: 'relative',
      }}>
      <span style={{
        width: 18, height: 18, borderRadius: '50%',
        background: '#fff',
        transform: `translateX(${on ? 14 : 0}px)`,
        boxShadow: '0 1px 2px rgba(14,18,32,0.20)',
        transition: 'transform 220ms cubic-bezier(.2,.8,.2,1)',
      }} />
    </button>
  );

  if (!label) return track;

  // Setting row
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 12,
      padding: '12px 14px',
      background: M.surface,
      border: `1px solid ${M.rule}`,
      borderRadius: 10,
      opacity: disabled ? 0.45 : 1,
      ...style,
    }}>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ font: `500 14px/1.3 ${M.sans}`, color: M.ink }}>{label}</div>
        {subtitle && (
          <div style={{ font: `400 12px/1.4 ${M.sans}`, color: M.inkMuted, marginTop: 2 }}>{subtitle}</div>
        )}
      </div>
      {track}
    </div>
  );
}

// ── DSStepper ────────────────────────────────────────────────────
function DSStepper({
  value,
  onChange,
  min = 0,
  max = 99,
  step = 1,
  unit,
  disabled = false,
  style,
}) {
  const dec = () => { if (!disabled && value > min) onChange?.(Math.max(min, value - step)); };
  const inc = () => { if (!disabled && value < max) onChange?.(Math.min(max, value + step)); };
  const btn = (label, onClick, isDisabled) => (
    <button
      type="button"
      onClick={onClick}
      disabled={isDisabled}
      aria-label={label === '−' ? 'Decrement' : 'Increment'}
      style={{
        width: 36, height: 36,
        background: 'transparent', border: 'none',
        cursor: isDisabled ? 'default' : 'pointer',
        color: isDisabled ? M.inkFaint : M.ink,
        font: `500 16px/1 ${M.sans}`,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        outline: 'none',
      }}>{label}</button>
  );
  return (
    <div style={{
      display: 'inline-flex', alignItems: 'center',
      background: M.surface, border: `1px solid ${M.rule}`,
      borderRadius: 10,
      opacity: disabled ? 0.45 : 1,
      ...style,
    }}>
      {btn('−', dec, disabled || value <= min)}
      <div style={{
        width: 1, height: 22,
        background: M.rule,
      }} aria-hidden="true" />
      <div style={{
        minWidth: 44, padding: '0 8px', textAlign: 'center',
        font: `500 14px/1 ${M.mono}`, color: M.ink,
        fontVariantNumeric: 'tabular-nums',
        display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 2,
        height: 36,
      }}>
        <span>{value}</span>
        {unit && <span style={{ opacity: 0.65, fontSize: 12 }}>{unit}</span>}
      </div>
      <div style={{ width: 1, height: 22, background: M.rule }} aria-hidden="true" />
      {btn('+', inc, disabled || value >= max)}
    </div>
  );
}

// ── DSSegmentedControl ───────────────────────────────────────────
function DSSegmentedControl({
  options,         // [string] or [{ value, label }]
  selection,
  onChange,
  size = 'md',     // 'sm' | 'md'
  style,
}) {
  const norm = options.map((o, i) => (
    typeof o === 'string' ? { value: i, label: o } : o
  ));
  const sz = size === 'sm'
    ? { padY: 5, padX: 9, font: 11, gap: 1 }
    : { padY: 7, padX: 12, font: 12, gap: 2 };
  return (
    <div role="group" style={{
      display: 'inline-flex', gap: sz.gap,
      padding: 3,
      background: M.canvas,
      border: `1px solid ${M.rule}`,
      borderRadius: 8,
      ...style,
    }}>
      {norm.map((opt) => {
        const active = opt.value === selection;
        return (
          <button
            key={String(opt.value)}
            type="button"
            onClick={() => onChange?.(opt.value)}
            aria-pressed={active}
            style={{
              padding: `${sz.padY}px ${sz.padX}px`,
              background: active ? M.surface : 'transparent',
              border: 'none',
              borderRadius: 6,
              color: active ? M.ink : M.inkMuted,
              font: `500 ${sz.font}px/1 ${M.sans}`,
              cursor: 'pointer',
              boxShadow: active ? '0 1px 2px rgba(14,18,32,0.06)' : 'none',
              transition: 'all 120ms cubic-bezier(.2,.8,.2,1)',
              outline: 'none',
              whiteSpace: 'nowrap',
            }}
          >{opt.label}</button>
        );
      })}
    </div>
  );
}

// ── DSSlider ─────────────────────────────────────────────────────
function DSSlider({
  value,
  onChange,
  min = 0,
  max = 1,
  step = 0.01,
  ticks,            // [string] — optional axis labels
  label,
  disabled = false,
  style,
}) {
  const trackRef = React.useRef(null);
  const [dragging, setDragging] = React.useState(false);
  const ratio = (value - min) / (max - min);
  const clampedRatio = Math.max(0, Math.min(1, ratio));

  const setFromClientX = (clientX) => {
    if (!trackRef.current) return;
    const rect = trackRef.current.getBoundingClientRect();
    const r = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
    let v = min + r * (max - min);
    // Quantize to step
    v = Math.round(v / step) * step;
    v = Math.max(min, Math.min(max, v));
    onChange?.(+v.toFixed(6));
  };

  const onDown = (e) => {
    if (disabled) return;
    setDragging(true);
    setFromClientX(e.clientX ?? e.touches?.[0]?.clientX);
  };
  const onMove = (e) => {
    if (!dragging) return;
    setFromClientX(e.clientX ?? e.touches?.[0]?.clientX);
  };
  const onUp = () => setDragging(false);
  React.useEffect(() => {
    if (!dragging) return;
    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseup', onUp);
    window.addEventListener('touchmove', onMove);
    window.addEventListener('touchend', onUp);
    return () => {
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseup', onUp);
      window.removeEventListener('touchmove', onMove);
      window.removeEventListener('touchend', onUp);
    };
  }, [dragging]);

  const onKey = (e) => {
    if (disabled) return;
    const big = step * 10;
    if (e.key === 'ArrowLeft')  { e.preventDefault(); onChange?.(Math.max(min, value - step)); }
    if (e.key === 'ArrowRight') { e.preventDefault(); onChange?.(Math.min(max, value + step)); }
    if (e.key === 'ArrowDown')  { e.preventDefault(); onChange?.(Math.max(min, value - big)); }
    if (e.key === 'ArrowUp')    { e.preventDefault(); onChange?.(Math.min(max, value + big)); }
    if (e.key === 'Home')       { e.preventDefault(); onChange?.(min); }
    if (e.key === 'End')        { e.preventDefault(); onChange?.(max); }
  };

  return (
    <div style={{ opacity: disabled ? 0.45 : 1, ...style }}>
      {label && (
        <div style={{
          font: `500 11px/1 ${M.sans}`, color: M.inkMuted,
          letterSpacing: '0.06em', textTransform: 'uppercase',
          marginBottom: 8,
        }}>{label}</div>
      )}
      <div
        ref={trackRef}
        onMouseDown={onDown}
        onTouchStart={onDown}
        role="slider"
        tabIndex={disabled ? -1 : 0}
        aria-valuemin={min} aria-valuemax={max} aria-valuenow={value}
        onKeyDown={onKey}
        style={{
          position: 'relative',
          height: 24,
          cursor: disabled ? 'default' : 'pointer',
          touchAction: 'none',
          outline: 'none',
          display: 'flex', alignItems: 'center',
        }}>
        {/* Track */}
        <div style={{
          width: '100%', height: 4, borderRadius: 999,
          background: M.rule,
        }} />
        {/* Fill */}
        <div style={{
          position: 'absolute', left: 0, top: '50%',
          transform: 'translateY(-50%)',
          width: `${clampedRatio * 100}%`,
          height: 4, borderRadius: 999,
          background: M.axis,
          pointerEvents: 'none',
        }} />
        {/* Thumb */}
        <div style={{
          position: 'absolute',
          left: `calc(${clampedRatio * 100}% )`,
          transform: 'translate(-50%, 0)',
          width: 16, height: 16,
          borderRadius: '50%',
          background: '#fff',
          border: `1.5px solid ${M.axis}`,
          boxShadow: '0 1px 2px rgba(14,18,32,0.15)',
          pointerEvents: 'none',
          transition: dragging ? 'none' : 'left 120ms cubic-bezier(.2,.8,.2,1)',
        }} />
      </div>
      {ticks && ticks.length > 0 && (
        <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 8 }}>
          {ticks.map((t, i) => (
            <span key={i} style={{
              font: `400 11px/1 ${M.mono}`, color: M.inkMuted,
              fontVariantNumeric: 'tabular-nums',
            }}>{t}</span>
          ))}
        </div>
      )}
    </div>
  );
}

// Back-compat alias for the existing routine-list usage in TodayScreen.
// MCheck used `checked` + `onChange()` (no value arg). Wrap accordingly.
function MCheck({ checked, onChange, size = 18 }) {
  return <DSCheckbox checked={checked} onChange={() => onChange?.()} size={size} />;
}

Object.assign(window, {
  DSCheckbox, DSRadio, DSToggle, DSSlider, DSStepper, DSSegmentedControl,
  MCheck, // legacy alias — TodayScreen still calls it
});
