// galandr.ai — primitives shared across all sections
// Eyebrow, NumberedHeader, CTA (with arrow + magnetic glow), CustomCursor

const { useEffect, useRef, useState } = React;

function useBreakpoint() {
  const [w, setW] = useState(typeof window !== 'undefined' ? window.innerWidth : 1200);
  useEffect(() => {
    const fn = () => setW(window.innerWidth);
    window.addEventListener('resize', fn, { passive: true });
    return () => window.removeEventListener('resize', fn);
  }, []);
  return { isMobile: w < 768, isTablet: w >= 768 && w < 1024 };
}

function Eyebrow({ children, accent = false }) {
  return (
    <span style={{
      fontFamily: 'var(--font-mono)',
      fontSize: '12px',
      fontWeight: 500,
      textTransform: 'uppercase',
      letterSpacing: '0.12em',
      color: accent ? 'var(--accent)' : 'var(--text-secondary)',
      display: 'inline-block',
    }}>{children}</span>
  );
}

function NumberedHeader({ num, title, sub }) {
  const { isMobile } = useBreakpoint();
  return (
    <div style={{
      display: 'grid',
      gridTemplateColumns: 'auto 1fr',
      gap: isMobile ? '12px' : '32px',
      alignItems: 'start',
      paddingBottom: isMobile ? '16px' : '24px',
      borderBottom: '1px solid var(--border)',
      marginBottom: isMobile ? '28px' : '40px',
    }}>
      <span style={{
        fontFamily: 'var(--font-mono)',
        fontSize: isMobile ? '11px' : '13px',
        color: 'var(--accent)',
        letterSpacing: '0.1em',
        paddingTop: isMobile ? '8px' : '12px',
      }}>{num} —</span>
      <div>
        <h2 style={{
          fontFamily: 'var(--font-display)',
          fontWeight: 700,
          fontSize: isMobile ? 'clamp(28px, 8vw, 40px)' : 'clamp(40px, 5vw, 72px)',
          lineHeight: 1.04,
          letterSpacing: '-0.025em',
          color: 'var(--text-primary)',
          margin: 0,
        }}>{title}</h2>
        {sub && <p style={{
          fontFamily: 'var(--font-body)',
          fontSize: isMobile ? '14px' : '17px',
          color: 'var(--text-secondary)',
          marginTop: isMobile ? '8px' : '14px',
          maxWidth: '52ch',
        }}>{sub}</p>}
      </div>
    </div>
  );
}

function CTA({ children, onClick, variant = 'primary', size = 'md', dataMagnet = true, fullWidth = false }) {
  const [hover, setHover] = useState(false);
  const [press, setPress] = useState(false);
  const isPrimary = variant === 'primary';

  const styles = {
    fontFamily: 'var(--font-display)',
    fontWeight: 600,
    fontSize: size === 'lg' ? '17px' : '15px',
    letterSpacing: '-0.01em',
    padding: size === 'lg' ? '18px 28px' : '14px 22px',
    borderRadius: '6px',
    border: isPrimary ? '1px solid transparent' : '1px solid var(--border-strong)',
    background: isPrimary
      ? (press ? 'var(--accent-deep)' : hover ? 'var(--accent-hover)' : 'var(--accent)')
      : (hover ? 'var(--surface-hover)' : 'transparent'),
    color: isPrimary ? '#fff' : 'var(--text-primary)',
    boxShadow: isPrimary && hover ? '0 0 40px rgba(247, 19, 63, 0.4)' : 'none',
    transform: press ? 'scale(0.98)' : 'scale(1)',
    transition: 'all 300ms cubic-bezier(0.65, 0, 0.35, 1)',
    display: 'inline-flex',
    alignItems: 'center',
    gap: '10px',
    cursor: 'pointer',
    width: fullWidth ? '100%' : undefined,
    justifyContent: fullWidth ? 'center' : undefined,
  };

  return (
    <button
      data-magnetic={dataMagnet ? 'true' : undefined}
      onClick={onClick}
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => { setHover(false); setPress(false); }}
      onMouseDown={() => setPress(true)}
      onMouseUp={() => setPress(false)}
      style={styles}
    >
      {children}
      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
        <path d="M5 12h14M13 6l6 6-6 6"/>
      </svg>
    </button>
  );
}

function CustomCursor() {
  const dotRef = useRef(null);
  const ringRef = useRef(null);
  const [hover, setHover] = useState(false);

  useEffect(() => {
    let rx = -100, ry = -100, dx = -100, dy = -100;
    let mx = -100, my = -100;
    let raf;

    const onMove = (e) => {
      mx = e.clientX; my = e.clientY;
      // magnetic attract on data-magnetic
      const el = document.elementFromPoint(mx, my);
      const magnet = el && el.closest && el.closest('[data-magnetic]');
      if (magnet) {
        const r = magnet.getBoundingClientRect();
        const cx = r.left + r.width / 2, cy = r.top + r.height / 2;
        mx = mx + (cx - mx) * 0.35;
        my = my + (cy - my) * 0.35;
        setHover(true);
      } else {
        setHover(false);
      }
    };

    const tick = () => {
      // smoothed lag
      rx += (mx - rx) * 0.18;
      ry += (my - ry) * 0.18;
      dx += (mx - dx) * 0.5;
      dy += (my - dy) * 0.5;
      if (ringRef.current) ringRef.current.style.transform = `translate(${rx - 16}px, ${ry - 16}px)`;
      if (dotRef.current) dotRef.current.style.transform = `translate(${dx - 2}px, ${dy - 2}px)`;
      raf = requestAnimationFrame(tick);
    };

    window.addEventListener('mousemove', onMove);
    tick();
    return () => { window.removeEventListener('mousemove', onMove); cancelAnimationFrame(raf); };
  }, []);

  if (typeof window !== 'undefined' && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
    return null;
  }

  return (
    <>
      <div ref={ringRef} style={{
        position: 'fixed', top: 0, left: 0,
        width: hover ? '56px' : '32px',
        height: hover ? '56px' : '32px',
        marginLeft: hover ? '-12px' : 0,
        marginTop: hover ? '-12px' : 0,
        borderRadius: '999px',
        border: hover ? '1.5px solid var(--accent)' : '1.5px solid var(--text-secondary)',
        background: hover ? 'rgba(247, 19, 63, 0.08)' : 'transparent',
        pointerEvents: 'none',
        zIndex: 9999,
        transition: 'width 400ms cubic-bezier(0.65, 0, 0.35, 1), height 400ms cubic-bezier(0.65, 0, 0.35, 1), border-color 300ms, background 300ms, margin 400ms cubic-bezier(0.65, 0, 0.35, 1)',
        mixBlendMode: 'difference',
      }} />
      <div ref={dotRef} style={{
        position: 'fixed', top: 0, left: 0,
        width: '4px', height: '4px',
        borderRadius: '999px',
        background: 'var(--text-primary)',
        pointerEvents: 'none',
        zIndex: 9999,
      }} />
    </>
  );
}

function Wordmark({ size = 18 }) {
  // Galandr logo only
  const h = Math.round(size * 1.05);
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', lineHeight: 1 }}>
      <img
        src="../../assets/galandr-logo.webp"
        alt="Galandr"
        style={{ height: `${h}px`, width: 'auto', display: 'block', filter: 'brightness(0) invert(1)' }}
      />
    </span>
  );
}

function ScrollIndicator() {
  const { isMobile } = useBreakpoint();
  const [scrolled, setScrolled] = useState(false);
  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 80);
    window.addEventListener('scroll', onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener('scroll', onScroll);
  }, []);

  return (
    <>
      {/* Center — visible only at page top */}
      <div style={{
        position: 'fixed', bottom: isMobile ? '20px' : '32px', left: '50%',
        transform: 'translateX(-50%)',
        display: 'flex', flexDirection: 'column', alignItems: 'center', gap: isMobile ? '8px' : '12px',
        zIndex: 50, pointerEvents: 'none',
        opacity: scrolled ? 0 : 1,
        transition: 'opacity 600ms cubic-bezier(0.22, 1, 0.36, 1)',
      }}>
        <span style={{
          fontFamily: 'var(--font-mono)', fontSize: isMobile ? '9px' : '10px',
          color: 'var(--text-secondary)', textTransform: 'uppercase', letterSpacing: '0.18em',
        }}>scrolluj</span>
        <div style={{ width: '1px', height: isMobile ? '36px' : '60px', background: 'rgba(255,255,255,0.14)', position: 'relative', overflow: 'hidden' }}>
          <span style={{
            position: 'absolute', left: 0, top: 0, width: '1px', height: isMobile ? '14px' : '24px',
            background: 'var(--accent)',
            animation: 'travel 1800ms cubic-bezier(0.65, 0, 0.35, 1) infinite',
          }} />
        </div>
      </div>

      {/* Bottom-right corner — appears after scroll */}
      <div style={{
        position: 'fixed', bottom: '28px', right: '28px',
        display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '0',
        zIndex: 50, pointerEvents: 'none',
        opacity: scrolled ? 1 : 0,
        transition: 'opacity 600ms cubic-bezier(0.22, 1, 0.36, 1)',
      }}>
        <div style={{ width: '1px', height: '44px', background: 'rgba(255,255,255,0.10)', position: 'relative', overflow: 'hidden' }}>
          <span style={{
            position: 'absolute', left: 0, top: 0, width: '1px', height: '18px',
            background: 'var(--accent)',
            animation: 'travel 1800ms cubic-bezier(0.65, 0, 0.35, 1) infinite',
          }} />
        </div>
      </div>
    </>
  );
}

// Reveal — Intersection Observer wrapper for scroll-driven entrance.
// Synchronously checks viewport on mount to avoid FOUC for above-the-fold content.
function Reveal({ children, delay = 0, y = 24, as: Tag = 'div', style = {}, threshold = 0.15 }) {
  const ref = useRef(null);
  const [shown, setShown] = useState(false);
  useEffect(() => {
    const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reduced) { setShown(true); return; }
    const el = ref.current;
    if (!el) return;
    // Sync viewport check: if already visible on mount, reveal immediately.
    const r = el.getBoundingClientRect();
    const vh = window.innerHeight || document.documentElement.clientHeight;
    if (r.top < vh && r.bottom > 0) { setShown(true); return; }
    const io = new IntersectionObserver(entries => {
      entries.forEach(e => { if (e.isIntersecting) { setShown(true); io.unobserve(el); } });
    }, { threshold, rootMargin: '0px 0px -8% 0px' });
    io.observe(el);
    return () => io.disconnect();
  }, [threshold]);
  return (
    <Tag ref={ref} style={{
      ...style,
      opacity: shown ? 1 : 0,
      transform: shown ? 'translateY(0)' : `translateY(${y}px)`,
      transition: `opacity 700ms cubic-bezier(0.22, 1, 0.36, 1) ${delay}ms, transform 700ms cubic-bezier(0.22, 1, 0.36, 1) ${delay}ms`,
      willChange: shown ? 'auto' : 'opacity, transform',
    }}>{children}</Tag>
  );
}

// SplitText — splits a string into word spans with stagger reveal on enter
function SplitText({ text, baseDelay = 0, perWord = 70, y = 28, style = {} }) {
  const ref = useRef(null);
  const [shown, setShown] = useState(false);
  useEffect(() => {
    const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reduced) { setShown(true); return; }
    const el = ref.current;
    if (!el) return;
    const r = el.getBoundingClientRect();
    const vh = window.innerHeight || document.documentElement.clientHeight;
    if (r.top < vh && r.bottom > 0) { setShown(true); return; }
    const io = new IntersectionObserver(entries => {
      entries.forEach(e => { if (e.isIntersecting) { setShown(true); io.unobserve(el); } });
    }, { threshold: 0.2 });
    io.observe(el);
    return () => io.disconnect();
  }, []);
  // Split by spaces but preserve <br/> inputs by accepting array children too
  const words = typeof text === 'string' ? text.split(/(\s+)/) : [text];
  return (
    <span ref={ref} style={{ display: 'inline-block', ...style }}>
      {words.map((w, i) => {
        if (/^\s+$/.test(w)) return <span key={i}>{w}</span>;
        return (
          <span key={i} style={{ display: 'inline-block', overflow: 'hidden', verticalAlign: 'top' }}>
            <span style={{
              display: 'inline-block',
              transform: shown ? 'translateY(0)' : `translateY(${y}px)`,
              opacity: shown ? 1 : 0,
              transition: `opacity 700ms cubic-bezier(0.22, 1, 0.36, 1) ${baseDelay + i * perWord}ms, transform 800ms cubic-bezier(0.22, 1, 0.36, 1) ${baseDelay + i * perWord}ms`,
            }}>{w}</span>
          </span>
        );
      })}
    </span>
  );
}

// CountUp — number tween from 0 → target on enter view (supports decimals)
function CountUp({ to, duration = 1400, prefix = '', suffix = '', decimals = 0, thousands = false, style = {} }) {
  const ref = useRef(null);
  const [val, setVal] = useState(0);
  const startedRef = useRef(false);
  const fmt = (n) => {
    const factor = Math.pow(10, decimals);
    const rounded = Math.round(n * factor) / factor;
    const str = decimals > 0 ? rounded.toFixed(decimals) : String(Math.round(n));
    if (thousands) return str.replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
    return str;
  };
  useEffect(() => {
    const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (reduced) { setVal(to); return; }
    const el = ref.current; if (!el) return;
    const startTween = () => {
      if (startedRef.current) return;
      startedRef.current = true;
      const t0 = performance.now();
      const tick = (t) => {
        const p = Math.min(1, (t - t0) / duration);
        const eased = 1 - Math.pow(1 - p, 3);
        setVal(to * eased);
        if (p < 1) requestAnimationFrame(tick);
      };
      requestAnimationFrame(tick);
    };
    const r = el.getBoundingClientRect();
    const vh = window.innerHeight || document.documentElement.clientHeight;
    if (r.top < vh && r.bottom > 0) { startTween(); return; }
    const io = new IntersectionObserver(entries => {
      entries.forEach(e => {
        if (e.isIntersecting && !startedRef.current) {
          startTween();
          io.unobserve(el);
        }
      });
    }, { threshold: 0.4 });
    io.observe(el);
    return () => io.disconnect();
  }, [to, duration]);
  return <span ref={ref} style={style}>{prefix}{fmt(val)}{suffix}</span>;
}

Object.assign(window, { useBreakpoint, Eyebrow, NumberedHeader, CTA, CustomCursor, Wordmark, ScrollIndicator, Reveal, SplitText, CountUp });
