/* Shared hooks + small components */

const { useEffect, useState, useRef, useLayoutEffect, useCallback } = React;

/* ---------- useInView ---------- */
function useInView(options = { threshold: 0.18, once: true }) {
  const ref = useRef(null);
  const [inView, setInView] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) {
        setInView(true);
        if (options.once) io.disconnect();
      } else if (!options.once) {
        setInView(false);
      }
    }, { threshold: options.threshold || 0.18, rootMargin: options.rootMargin || '0px' });
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return [ref, inView];
}

/* ---------- CountUp ---------- */
function CountUp({ to, duration = 1800, prefix = '', suffix = '', decimals = 0 }) {
  const [ref, inView] = useInView();
  const [val, setVal] = useState(0);
  useEffect(() => {
    if (!inView) return;
    let raf, start;
    const step = (t) => {
      if (!start) start = t;
      const p = Math.min(1, (t - start) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setVal(eased * to);
      if (p < 1) raf = requestAnimationFrame(step);
    };
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, [inView, to, duration]);
  const formatted = val.toLocaleString(undefined, {
    minimumFractionDigits: decimals,
    maximumFractionDigits: decimals,
  });
  return <span ref={ref}>{prefix}{formatted}{suffix}</span>;
}

/* ---------- Reveal ---------- */
const Reveal = React.forwardRef(function Reveal(
  { children, delay = 0, className = '', as: Tag = 'div', ...rest },
  forwardedRef
) {
  const [ref, inView] = useInView();
  const setRefs = (node) => {
    ref.current = node;
    if (typeof forwardedRef === 'function') forwardedRef(node);
    else if (forwardedRef) forwardedRef.current = node;
  };
  const cls = `reveal ${delay ? `delay-${delay}` : ''} ${inView ? 'in' : ''} ${className}`.trim();
  return <Tag ref={setRefs} className={cls} {...rest}>{children}</Tag>;
});

/* ---------- Magnetic button ---------- */
function Magnetic({ children, strength = 0.4, className = '', ...rest }) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    let raf;
    const onMove = (e) => {
      const r = el.getBoundingClientRect();
      const cx = r.left + r.width / 2;
      const cy = r.top + r.height / 2;
      const dx = (e.clientX - cx) * strength;
      const dy = (e.clientY - cy) * strength;
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        el.style.transform = `translate(${dx}px, ${dy}px)`;
      });
    };
    const onLeave = () => {
      cancelAnimationFrame(raf);
      el.style.transform = `translate(0,0)`;
    };
    el.addEventListener('mousemove', onMove);
    el.addEventListener('mouseleave', onLeave);
    return () => {
      el.removeEventListener('mousemove', onMove);
      el.removeEventListener('mouseleave', onLeave);
    };
  }, [strength]);
  return <div ref={ref} className={`magnetic ${className}`} {...rest}>{children}</div>;
}

/* ---------- Arrow ---------- */
function Arrow({ size = 14 }) {
  return (
    <svg className="arrow" width={size} height={size} viewBox="0 0 14 14" fill="none">
      <path d="M2 7h10M8 3l4 4-4 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}

/* ---------- Eye Spark (glowing eyes anime motif) ---------- */
function GlowingEyes({ size = 100, pulse = true }) {
  return (
    <svg width={size} height={size * 0.45} viewBox="0 0 200 90" fill="none" style={{ overflow: 'visible' }}>
      <defs>
        <radialGradient id="eyeglow" cx="50%" cy="50%" r="50%">
          <stop offset="0%" stopColor="#FFD60A" stopOpacity="1"/>
          <stop offset="40%" stopColor="#FFC700" stopOpacity=".8"/>
          <stop offset="100%" stopColor="#FFC700" stopOpacity="0"/>
        </radialGradient>
        <filter id="eyeblur"><feGaussianBlur stdDeviation="4"/></filter>
      </defs>
      <g filter="url(#eyeblur)" opacity=".85">
        <ellipse cx="60" cy="45" rx="32" ry="20" fill="url(#eyeglow)">
          {pulse && <animate attributeName="ry" values="20;16;20" dur="2.8s" repeatCount="indefinite"/>}
        </ellipse>
        <ellipse cx="140" cy="45" rx="32" ry="20" fill="url(#eyeglow)">
          {pulse && <animate attributeName="ry" values="20;16;20" dur="2.8s" repeatCount="indefinite" begin=".15s"/>}
        </ellipse>
      </g>
      <ellipse cx="60" cy="45" rx="10" ry="6" fill="#FFF2C8"/>
      <ellipse cx="140" cy="45" rx="10" ry="6" fill="#FFF2C8"/>
    </svg>
  );
}

/* ---------- Geometric GNG logo (assembles on mount) ---------- */
function GNGLogo({ size = 64, animate = true }) {
  const [armed, setArmed] = useState(!animate);
  useEffect(() => {
    if (!animate) return;
    const t = setTimeout(() => setArmed(true), 60);
    return () => clearTimeout(t);
  }, [animate]);
  return (
    <svg width={size} height={size} viewBox="0 0 80 80" fill="none"
         style={{ filter: armed ? 'drop-shadow(0 0 12px rgba(139,92,246,.55))' : 'none', transition: 'filter 1.2s' }}>
      <defs>
        <linearGradient id="gnggrad" x1="0" y1="0" x2="1" y2="1">
          <stop offset="0%" stopColor="#16E07A"/>
          <stop offset="100%" stopColor="#8B5CF6"/>
        </linearGradient>
      </defs>
      {/* outer hex */}
      <polygon
        points="40,4 72,22 72,58 40,76 8,58 8,22"
        fill="none"
        stroke="url(#gnggrad)"
        strokeWidth="2.5"
        style={{
          transformOrigin: 'center',
          transform: armed ? 'rotate(0deg) scale(1)' : 'rotate(-30deg) scale(.6)',
          opacity: armed ? 1 : 0,
          transition: 'transform 1s var(--ease-overshoot), opacity .6s',
        }}
      />
      {/* inner triangle */}
      <polygon
        points="40,20 60,56 20,56"
        fill="url(#gnggrad)"
        opacity={armed ? 1 : 0}
        style={{
          transformOrigin: 'center',
          transform: armed ? 'scale(1)' : 'scale(0)',
          transition: 'transform .9s var(--ease-overshoot) .2s, opacity .5s .2s',
        }}
      />
      {/* center dot */}
      <circle cx="40" cy="46" r="4" fill="#0A0711"
        style={{ opacity: armed ? 1 : 0, transition: 'opacity .4s .6s' }}/>
      {/* spark */}
      <circle cx="40" cy="46" r="2" fill="#FFC700"
        style={{
          opacity: armed ? 1 : 0,
          transition: 'opacity .4s .8s',
        }}>
        {armed && <animate attributeName="r" values="2;3.4;2" dur="2.4s" repeatCount="indefinite"/>}
      </circle>
    </svg>
  );
}

/* ---------- Theme toggle (shared: public nav + admin) ---------- */
function ThemeToggle({ className = '' }) {
  const [theme, setTheme] = useState(() => document.documentElement.getAttribute('data-theme') || 'light');
  useEffect(() => {
    const sync = () => setTheme(document.documentElement.getAttribute('data-theme') || 'light');
    window.addEventListener('themechange', sync);
    return () => window.removeEventListener('themechange', sync);
  }, []);
  const toggle = () => {
    const next = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
    if (next === 'dark') document.documentElement.setAttribute('data-theme', 'dark');
    else document.documentElement.removeAttribute('data-theme');
    try { localStorage.setItem('webes-theme', next); } catch (e) {}
    window.dispatchEvent(new Event('themechange'));
  };
  const isDark = theme === 'dark';
  return (
    <button className={`theme-toggle hot ${className}`} onClick={toggle}
            aria-label={isDark ? 'Switch to light mode' : 'Switch to dark mode'} title="Toggle theme">
      <span className="tt-thumb">
        {isDark ? (
          <svg className="tt-ico" viewBox="0 0 24 24" fill="currentColor">
            <path d="M21 12.8A8.5 8.5 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/>
          </svg>
        ) : (
          <svg className="tt-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round">
            <circle cx="12" cy="12" r="4.2"/>
            <path d="M12 2v2M12 20v2M2 12h2M20 12h2M4.6 4.6l1.4 1.4M18 18l1.4 1.4M19.4 4.6L18 6M6 18l-1.4 1.4"/>
          </svg>
        )}
      </span>
    </button>
  );
}

/* Avatar - renders a real image if `img` is given, else the existing
   initial-on-gradient fallback. Drop-in: same size/shape as before.
   `style` lets each section keep its own gradient fallback + sizing. */
function Avatar({ img, name = '?', className = '', style = {}, alt }) {
  const [failed, setFailed] = useState(false);
  const initial = (name || '?').replace(/^@/, '').charAt(0).toUpperCase();
  if (img && !failed) {
    return (
      <div className={className} style={{ ...style, overflow: 'hidden', padding: 0 }}>
        <img
          src={img}
          alt={alt || name}
          loading="lazy"
          onError={() => setFailed(true)}
          style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}
        />
      </div>
    );
  }
  return <div className={className} style={style}>{initial}</div>;
}

window.ThemeToggle = ThemeToggle;
window.useInView = useInView;
window.CountUp = CountUp;
window.Reveal = Reveal;
window.Magnetic = Magnetic;
window.Arrow = Arrow;
window.GlowingEyes = GlowingEyes;
window.GNGLogo = GNGLogo;
window.Avatar = Avatar;
