// ReturnScribe — Motion primitives
// Each effect documents its SwiftUI equivalent for handoff.

const { useState: useStateM, useEffect: useEffectM, useRef: useRefM } = React;

// ─────────────────────────────────────────────────────────────
// PressScale — spring-physics tap feedback
// SwiftUI: ButtonStyle with .scaleEffect(isPressed ? 0.97 : 1) +
//          .animation(.spring(response: 0.3, dampingFraction: 0.5))
//          + UIImpactFeedbackGenerator(style: .light) haptic
// ─────────────────────────────────────────────────────────────
function PressScale({ children, scale = 0.97, onClick, style = {}, as: As = 'div', ...rest }) {
  const [pressed, setPressed] = useStateM(false);
  return (
    <As
      onPointerDown={() => setPressed(true)}
      onPointerUp={() => setPressed(false)}
      onPointerLeave={() => setPressed(false)}
      onClick={onClick}
      style={{
        transform: pressed ? `scale(${scale})` : 'scale(1)',
        filter: pressed ? 'brightness(1.08)' : 'brightness(1)',
        transition: pressed
          ? 'transform 90ms cubic-bezier(.2,.7,.3,1), filter 90ms'
          : 'transform 280ms cubic-bezier(.34,1.56,.64,1), filter 200ms',
        ...style,
      }}
      {...rest}
    >{children}</As>
  );
}

// ─────────────────────────────────────────────────────────────
// CardPress — card-row tap depth
// SwiftUI: ButtonStyle with .scaleEffect(0.99) + .offset(y: 1) on pressed
// ─────────────────────────────────────────────────────────────
function CardPress({ children, onClick, style = {} }) {
  const [pressed, setPressed] = useStateM(false);
  return (
    <div
      onPointerDown={() => setPressed(true)}
      onPointerUp={() => setPressed(false)}
      onPointerLeave={() => setPressed(false)}
      onClick={onClick}
      style={{
        transform: pressed ? 'scale(0.99) translateY(1px)' : 'none',
        transition: pressed ? 'transform 100ms ease-out' : 'transform 150ms cubic-bezier(.2,.7,.3,1)',
        cursor: onClick ? 'pointer' : 'default',
        ...style,
      }}
    >{children}</div>
  );
}

// ─────────────────────────────────────────────────────────────
// FluidNumber — slot-machine digit roll for currency
// SwiftUI: Text(value, format:).contentTransition(.numericText())
//          + .animation(.easeOut(duration: 0.4))
// ─────────────────────────────────────────────────────────────
function FluidDigit({ char, size, color, weight, dim }) {
  const isNum = /[0-9]/.test(char);
  if (!isNum) {
    return (
      <span style={{
        fontFamily: RSFont.num, fontWeight: weight, color,
        fontSize: size, lineHeight: 1.02, letterSpacing: '-0.04em',
        opacity: dim != null ? dim : 1,
        ...RSTabular,
      }}>{char}</span>
    );
  }
  const d = parseInt(char, 10);
  // Render a vertical strip 0..9 and translate to the active digit
  const cellH = size * 1.02;
  return (
    <span style={{
      display: 'inline-block', height: cellH, overflow: 'hidden',
      verticalAlign: 'top', lineHeight: cellH + 'px',
      opacity: dim != null ? dim : 1,
    }}>
      <span style={{
        display: 'block',
        transform: `translateY(${-d * cellH}px)`,
        transition: 'transform 460ms cubic-bezier(.22,.61,.36,1)',
      }}>
        {Array.from({ length: 10 }).map((_, i) => (
          <span key={i} style={{
            display: 'block', height: cellH,
            fontFamily: RSFont.num, fontWeight: weight, color,
            fontSize: size, lineHeight: cellH + 'px',
            letterSpacing: '-0.04em',
            ...RSTabular,
          }}>{i}</span>
        ))}
      </span>
    </span>
  );
}

function FluidMoney({ value, size = 56, color, weight = 600 }) {
  const sign = value < 0 ? '−' : '';
  const abs = Math.abs(value);
  const whole = Math.floor(abs).toLocaleString('en-US');
  const frac = abs.toFixed(2).split('.')[1];
  const dollarStyle = {
    fontFamily: RSFont.num, fontWeight: weight, color,
    fontSize: size * 0.55, opacity: 0.55,
    verticalAlign: 'top', lineHeight: (size * 1.02) + 'px',
    marginRight: 1,
  };
  const fracStyle = {
    fontFamily: RSFont.num, fontWeight: weight, color,
    fontSize: size * 0.55, opacity: 0.42,
    verticalAlign: 'top', lineHeight: (size * 1.02) + 'px',
  };
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'flex-start',
      letterSpacing: '-0.04em', whiteSpace: 'nowrap',
    }}>
      <span style={dollarStyle}>$</span>
      {sign && <FluidDigit char={sign} size={size} color={color} weight={weight}/>}
      {whole.split('').map((c, i) => (
        <FluidDigit key={i + '-' + c} char={c} size={size} color={color} weight={weight}/>
      ))}
      <span style={fracStyle}>.</span>
      {frac.split('').map((c, i) => (
        <span key={'f' + i} style={{
          fontFamily: RSFont.num, fontWeight: weight, color,
          fontSize: size * 0.55, opacity: 0.42,
          lineHeight: (size * 1.02) + 'px', display: 'inline-block',
          ...RSTabular,
        }}>
          <FluidDigit char={c} size={size * 0.55} color={color} weight={weight} dim={1}/>
        </span>
      ))}
    </span>
  );
}

// ─────────────────────────────────────────────────────────────
// GradientOrb — slow-drifting radial blobs behind hero counter
// SwiftUI: TimelineView(.animation) + Canvas drawing two radial gradients
//          whose centers animate on .sine curves. OR MeshGradient (iOS 18+).
// ─────────────────────────────────────────────────────────────
function GradientOrb({ colorA, colorB }) {
  const [phase, setPhase] = useStateM(0);
  useEffectM(() => {
    let id;
    const start = performance.now();
    const tick = () => {
      const ph = ((performance.now() - start) / 10000) % 1;
      setPhase(ph);
      id = requestAnimationFrame(tick);
    };
    id = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(id);
  }, []);

  const t = phase * Math.PI * 2;
  const ax = 30 + Math.sin(t) * 14;
  const ay = 40 + Math.cos(t * 0.8) * 12;
  const bx = 70 + Math.cos(t * 1.2) * 14;
  const by = 60 + Math.sin(t * 0.6) * 12;

  return (
    <div style={{
      position: 'absolute', inset: 0, borderRadius: 24, overflow: 'hidden',
      pointerEvents: 'none',
      transition: 'background 800ms cubic-bezier(.2,.7,.3,1)',
    }}>
      <div style={{
        position: 'absolute', inset: 0,
        background: `radial-gradient(circle at ${ax}% ${ay}%, ${colorA} 0%, transparent 55%)`,
        opacity: 0.55,
        transition: 'background 800ms cubic-bezier(.2,.7,.3,1)',
      }}/>
      <div style={{
        position: 'absolute', inset: 0,
        background: `radial-gradient(circle at ${bx}% ${by}%, ${colorB} 0%, transparent 60%)`,
        opacity: 0.45,
        transition: 'background 800ms cubic-bezier(.2,.7,.3,1)',
      }}/>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Shimmer — loading placeholder with diagonal gradient sweep
// SwiftUI: LinearGradient on .mask with .phaseAnimator sliding offset
// ─────────────────────────────────────────────────────────────
function Shimmer({ t, width = '100%', height = 14, radius = 6, style = {} }) {
  const base = t.surface2;
  const sweep = t === RSTokens.dark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.04)';
  return (
    <div style={{
      width, height, borderRadius: radius, background: base,
      position: 'relative', overflow: 'hidden', ...style,
    }}>
      <div style={{
        position: 'absolute', inset: 0,
        background: `linear-gradient(110deg, transparent 30%, ${sweep} 50%, transparent 70%)`,
        animation: 'rsShimmer 1500ms linear infinite',
      }}/>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// RevealOnScroll — staggered fade+translate as element enters viewport
// SwiftUI: .scrollTransition { content, phase in
//            content.opacity(phase.isIdentity ? 1 : 0)
//                   .offset(y: phase.isIdentity ? 0 : 12) }
// ─────────────────────────────────────────────────────────────
function RevealOnScroll({ children, delay = 0, root }) {
  const ref = useRefM(null);
  const [shown, setShown] = useStateM(false);
  useEffectM(() => {
    if (!ref.current) return;
    const obs = new IntersectionObserver(entries => {
      entries.forEach(e => {
        if (e.isIntersecting) {
          setTimeout(() => setShown(true), delay);
          obs.disconnect();
        }
      });
    }, { root: root && root.current ? root.current : null, threshold: 0.05, rootMargin: '0px 0px -8% 0px' });
    obs.observe(ref.current);
    return () => obs.disconnect();
  }, [delay, root]);
  return (
    <div ref={ref} style={{
      opacity: shown ? 1 : 0,
      transform: shown ? 'translateY(0)' : 'translateY(12px)',
      transition: 'opacity 320ms cubic-bezier(.2,.7,.3,1), transform 320ms cubic-bezier(.2,.7,.3,1)',
    }}>{children}</div>
  );
}

// ─────────────────────────────────────────────────────────────
// AnimatedFilterPills — accent background slides between active pills
// SwiftUI: matchedGeometryEffect(id: "activePill", in: namespace)
//          attached to a Capsule behind the active pill's label
// ─────────────────────────────────────────────────────────────
function AnimatedFilterPills({ t, items, active, onChange }) {
  const containerRef = useRefM(null);
  const itemRefs = useRefM({});
  const [box, setBox] = useStateM(null);

  useEffectM(() => {
    const el = itemRefs.current[active];
    const c = containerRef.current;
    if (!el || !c) return;
    setBox({ left: el.offsetLeft, top: el.offsetTop, width: el.offsetWidth, height: el.offsetHeight });
  }, [active, items.length]);

  return (
    <div ref={containerRef} style={{
      display: 'flex', gap: 8, padding: '0 20px 12px', overflowX: 'auto',
      position: 'relative',
    }}>
      {/* Moving ink capsule */}
      {box && (
        <div style={{
          position: 'absolute',
          left: box.left, top: box.top, width: box.width, height: box.height,
          background: t.text1,
          borderRadius: 7,
          transition: 'left 350ms cubic-bezier(.34,1.2,.64,1), width 350ms cubic-bezier(.34,1.2,.64,1), top 350ms cubic-bezier(.34,1.2,.64,1)',
          zIndex: 0,
        }}/>
      )}
      {items.map(it => {
        const a = it.id === active;
        return (
          <button key={it.id}
            ref={el => itemRefs.current[it.id] = el}
            onClick={() => onChange && onChange(it.id)}
            style={{
              position: 'relative', zIndex: 1,
              background: 'transparent',
              boxShadow: a ? 'none' : `inset 0 0 0 1px ${t.border}`,
              color: a ? (t === RSTokens.dark ? '#161310' : '#FBF8F1') : t.text2,
              border: 'none', borderRadius: 7, padding: '8px 14px',
              height: 34, ...applyMono(RSType.footnote, { fontSize: 12, fontWeight: 500 }),
              cursor: 'pointer', whiteSpace: 'nowrap',
              transition: 'color 250ms, box-shadow 250ms',
            }}>
            {it.label}{it.count != null && <span style={{ opacity: 0.6, marginLeft: 6 }}>{it.count}</span>}
          </button>
        );
      })}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// AnimatedTabBar — ledger footer. Flat paper bar with a double-rule top
// edge; tabs are icon + mono small-caps label; the active tab gets an
// ink dot above its icon. SwiftUI: HStack in safeAreaInset(.bottom).
// ─────────────────────────────────────────────────────────────
// ─────────────────────────────────────────────────────────────
// AnimatedTabBar — floating liquid-glass dock. Translucent pill with
// heavy blur + specular streak; active tab sits in a glass lens with
// the ledger ink dot above its icon. Labels stay mono small-caps.
// SwiftUI: HStack in safeAreaInset(.bottom) over .glassEffect() (iOS 26).
// ─────────────────────────────────────────────────────────────
function AnimatedTabBar({ t, active = 'home', onChange }) {
  const dark = t === RSTokens.dark;
  return (
    <div style={{ position: 'absolute', left: 12, right: 12, bottom: 14, zIndex: 30 }}>
      <div style={{
        position: 'relative', height: 66, borderRadius: 33, overflow: 'hidden',
        display: 'flex', alignItems: 'stretch', padding: '0 8px',
        background: dark ? 'rgba(28,24,19,0.40)' : 'rgba(252,250,244,0.46)',
        backdropFilter: 'blur(36px) saturate(185%)',
        WebkitBackdropFilter: 'blur(36px) saturate(185%)',
        border: dark ? '0.5px solid rgba(239,233,222,0.16)' : '0.5px solid rgba(33,29,23,0.10)',
        boxShadow: dark
          ? 'inset 1.5px 1.5px 1px rgba(255,255,255,0.13), inset -1px -1px 1px rgba(255,255,255,0.05), 0 10px 36px rgba(0,0,0,0.5)'
          : 'inset 1.5px 1.5px 1px rgba(255,255,255,0.85), inset -1px -1px 1px rgba(255,255,255,0.4), 0 8px 28px rgba(42,38,32,0.16)',
      }}>
        {/* specular streak along the top edge */}
        <div style={{
          position: 'absolute', top: 0, left: '10%', right: '10%', height: 1,
          background: dark
            ? 'linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent)'
            : 'linear-gradient(90deg, transparent, rgba(255,255,255,0.95), transparent)',
          pointerEvents: 'none',
        }}></div>
        {TABS.map(tab => {
          const a = tab.id === active;
          const c = a ? t.accent : t.text2;
          return (
            <PressScale key={tab.id} as="button" scale={0.93}
              onClick={() => onChange && onChange(tab.id)}
              style={{
                flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center',
                justifyContent: 'center', gap: 3,
                border: 'none', cursor: 'pointer', background: 'transparent',
                margin: '7px 1px', borderRadius: 26, position: 'relative',
              }}>
              {/* glass lens behind the active tab */}
              <span style={{
                position: 'absolute', inset: 0, borderRadius: 26, pointerEvents: 'none',
                background: dark ? 'rgba(239,233,222,0.10)' : 'rgba(255,255,255,0.72)',
                border: dark ? '0.5px solid rgba(239,233,222,0.18)' : '0.5px solid rgba(33,29,23,0.06)',
                boxShadow: dark
                  ? 'inset 1px 1px 0.5px rgba(255,255,255,0.16), 0 2px 10px rgba(0,0,0,0.3)'
                  : 'inset 1px 1px 0.5px rgba(255,255,255,0.95), 0 2px 10px rgba(42,38,32,0.10)',
                opacity: a ? 1 : 0,
                transform: a ? 'scale(1)' : 'scale(0.82)',
                transition: 'opacity 240ms ease, transform 280ms cubic-bezier(.34,1.3,.64,1)',
              }}></span>
              {/* ink dot — stamps above the active icon */}
              <span style={{
                position: 'absolute', top: 2.5, left: '50%',
                width: 4, height: 4, borderRadius: 99, background: t.accent,
                transform: `translateX(-50%) scale(${a ? 1 : 0})`,
                transition: 'transform 240ms cubic-bezier(.34,1.4,.64,1)',
              }}></span>
              <span style={{ position: 'relative', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3, paddingTop: 6 }}>
                <Icon name={a ? tab.icon + '.fill' : tab.icon} size={22} color={c} weight={1.8}/>
                <span style={{
                  color: c, fontFamily: RSFont.num, fontSize: 9.5, fontWeight: a ? 600 : 500,
                  letterSpacing: 1.1, textTransform: 'uppercase', whiteSpace: 'nowrap',
                  transition: 'color 200ms ease',
                }}>{tab.label}</span>
              </span>
            </PressScale>
          );
        })}
      </div>
    </div>
  );
}
// SwiftUI: GeometryReader-based offset tied to scroll position,
//          or .scrollTransition with custom phase
// ─────────────────────────────────────────────────────────────
function Parallax({ scrollRef, factor = 0.15, children, style }) {
  const [y, setY] = useStateM(0);
  useEffectM(() => {
    const el = scrollRef && scrollRef.current;
    if (!el) return;
    const onScroll = () => {
      const top = el.scrollTop;
      // Parallax only while above 240px; clamp
      setY(Math.min(top * factor, 60));
    };
    el.addEventListener('scroll', onScroll, { passive: true });
    onScroll();
    return () => el.removeEventListener('scroll', onScroll);
  }, [scrollRef, factor]);
  return (
    <div style={{
      transform: `translateY(${y}px)`,
      willChange: 'transform',
      ...style,
    }}>{children}</div>
  );
}

Object.assign(window, {
  PressScale, CardPress, FluidMoney, FluidDigit,
  GradientOrb, Shimmer, RevealOnScroll,
  AnimatedFilterPills, AnimatedTabBar, Parallax,
});
