// swipe_view.jsx — iOS純正風の横スワイプビュー
// 3枚（prev / current / next）を並べて、指の動きに追従。
// 指を離したら、一定距離超えてたら切替、そうでなければスナップバック。

function SwipeableView({
  current,                // 現在選択中の「何か」(Date, number, string いずれでもOK)
  onChange,               // (next) => void  新しいcurrentを親に通知
  renderAt,               // (value) => JSX  ある値のビューをレンダー
  prevOf,                 // (value) => value (前の値)
  nextOf,                 // (value) => value (次の値)
  keyOf,                  // (value) => string (React key用。省略時はそのまま)
  disabled = false,
}) {
  const containerRef = React.useRef(null);
  const [w, setW] = React.useState(0);
  const [dragX, setDragX] = React.useState(0);   // 現在の指オフセット
  const [anim, setAnim] = React.useState(false); // スナップアニメ中か
  const startX = React.useRef(null);
  const startY = React.useRef(null);
  const dragging = React.useRef(false);
  const decidedAxis = React.useRef(null);        // 'x' | 'y' | null

  // コンテナ幅を取得
  React.useEffect(() => {
    const el = containerRef.current;
    if (!el) return;
    const measure = () => setW(el.clientWidth);
    measure();
    const ro = new ResizeObserver(measure);
    ro.observe(el);
    window.addEventListener('resize', measure);
    return () => {
      ro.disconnect();
      window.removeEventListener('resize', measure);
    };
  }, []);

  const onTouchStart = (e) => {
    if (disabled) return;
    const t = e.touches[0];
    startX.current = t.clientX;
    startY.current = t.clientY;
    dragging.current = true;
    decidedAxis.current = null;
    setAnim(false);
  };

  const onTouchMove = (e) => {
    if (!dragging.current) return;
    const t = e.touches[0];
    const dx = t.clientX - startX.current;
    const dy = t.clientY - startY.current;
    // どちらの軸で動かすかを最初の数pxで決定
    if (!decidedAxis.current) {
      const absX = Math.abs(dx);
      const absY = Math.abs(dy);
      if (absX < 8 && absY < 8) return;
      decidedAxis.current = absX > absY ? 'x' : 'y';
    }
    if (decidedAxis.current === 'y') return; // 縦スクロールに任せる

    // 横ドラッグ
    if (e.cancelable) e.preventDefault();
    setDragX(dx);
  };

  const onTouchEnd = () => {
    if (!dragging.current) return;
    dragging.current = false;
    if (decidedAxis.current !== 'x') {
      setDragX(0);
      return;
    }

    // しきい値: コンテナ幅の 22% or 60px 以上
    const threshold = Math.min(Math.max(w * 0.22, 60), 180);
    setAnim(true);
    if (dragX > threshold) {
      // 右に引いた = 前へ
      setDragX(w);
      setTimeout(() => {
        setAnim(false);
        setDragX(0);
        onChange(prevOf(current));
      }, 280);
    } else if (dragX < -threshold) {
      setDragX(-w);
      setTimeout(() => {
        setAnim(false);
        setDragX(0);
        onChange(nextOf(current));
      }, 280);
    } else {
      // 戻る
      setDragX(0);
      setTimeout(() => setAnim(false), 280);
    }
  };

  const prev = prevOf(current);
  const next = nextOf(current);
  const k = keyOf || ((v) => (v && typeof v.getTime === 'function') ? v.getTime() : String(v));

  const translateX = -w + dragX; // 中央パネルの左端が見える位置
  const transition = anim
    ? 'transform 0.28s cubic-bezier(0.22, 0.61, 0.36, 1)'
    : 'none';

  return (
    <div
      ref={containerRef}
      style={{
        overflow: 'hidden',
        position: 'relative',
        width: '100%',
        touchAction: 'pan-y',  // 縦スクロールは許可、横はこっちで処理
      }}
      onTouchStart={onTouchStart}
      onTouchMove={onTouchMove}
      onTouchEnd={onTouchEnd}
      onTouchCancel={onTouchEnd}
    >
      <div style={{
        display: 'flex',
        width: w ? w * 3 : '300%',
        transform: `translate3d(${translateX}px, 0, 0)`,
        transition,
        willChange: 'transform',
      }}>
        <div style={{ width: w || '33.333%', flexShrink: 0 }} key={'p-' + k(prev)}>
          {renderAt(prev)}
        </div>
        <div style={{ width: w || '33.333%', flexShrink: 0 }} key={'c-' + k(current)}>
          {renderAt(current)}
        </div>
        <div style={{ width: w || '33.333%', flexShrink: 0 }} key={'n-' + k(next)}>
          {renderAt(next)}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { SwipeableView });
