// Lumiere LP – shared utilities & hooks
const { useState, useEffect, useRef, useCallback, useMemo } = React;

// Track analytics event with graceful no-op if IDs not configured
function track(eventName, params){
  try{
    if (typeof window.lumiereTrack === 'function'){
      window.lumiereTrack(eventName, params || {});
    }
  }catch(e){}
}

// Intersection-based reveal hook.  Returns ref + isIn boolean.
function useReveal(threshold = 0.18, once = true){
  const ref = useRef(null);
  const [isIn, setIsIn] = useState(false);
  useEffect(() => {
    const el = ref.current; if (!el) return;
    if (typeof IntersectionObserver === 'undefined'){ setIsIn(true); return; }
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting){
          setIsIn(true);
          if (once) io.unobserve(e.target);
        } else if (!once){
          setIsIn(false);
        }
      });
    }, { threshold, rootMargin: '0px 0px -8% 0px' });
    io.observe(el);
    return () => io.disconnect();
  }, [threshold, once]);
  return [ref, isIn];
}

// Reveal wrapper: applies .reveal + .is-in
function Reveal({ as: Tag = 'div', delay = 0, className = '', children, ...rest }){
  const [ref, isIn] = useReveal();
  const d = delay ? ` reveal-delay-${delay}` : '';
  return (
    <Tag ref={ref} className={`reveal${d} ${isIn ? 'is-in' : ''} ${className}`} {...rest}>
      {children}
    </Tag>
  );
}

// Text mask: each line slides up.  Pass `lines` (array of strings) or children spans.
function MaskLine({ children, delay = 1, className = '' }){
  const [ref, isIn] = useReveal();
  return (
    <span ref={ref} className={`mask-text d${delay} ${isIn ? 'is-in' : ''} ${className}`}>
      <span>{children}</span>
    </span>
  );
}

// Image with reveal mask
function MaskImage({ src, alt = '', className = '', style }){
  const [ref, isIn] = useReveal(0.12);
  return (
    <div ref={ref} className={`mask-image ${isIn ? 'is-in' : ''} ${className}`} style={style}>
      <img src={src} alt={alt} loading="lazy" />
    </div>
  );
}

// Counter that animates when in view
function CountUp({ to, duration = 1600, suffix = '', prefix = '' }){
  const [ref, isIn] = useReveal(0.4);
  const [val, setVal] = useState(0);
  useEffect(() => {
    if (!isIn) return;
    const start = performance.now();
    const from = 0;
    let raf;
    const tick = (now) => {
      const t = Math.min(1, (now - start) / duration);
      // easeOutCubic
      const eased = 1 - Math.pow(1 - t, 3);
      setVal(from + (to - from) * eased);
      if (t < 1) raf = requestAnimationFrame(tick);
      else setVal(to);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [isIn, to, duration]);
  const display = Number.isInteger(to) ? Math.floor(val).toLocaleString() : val.toFixed(1);
  return <span ref={ref}>{prefix}{display}{suffix}</span>;
}

// Salons – source of truth
const SALONS = [
  {
    id: 'sannomiya',
    name: 'Lumiere 三ノ宮店',
    area: 'SANNOMIYA / KOBE',
    address: '兵庫県神戸市中央区加納町6-6-3 摩耶松本ビル9F',
    hours: '9:00 - 19:00',
    closed: '不定休',
    bookingUrl: 'https://beauty.hotpepper.jp/slnH000748015/coupon/',
    reviewUrl: 'https://beauty.hotpepper.jp/slnH000748015/review/',
    image: 'assets/sannomiya.jpg'
  },
  {
    id: 'tsujii',
    name: 'Lumiere 姫路辻井店',
    area: 'HIMEJI TSUJII',
    address: '兵庫県姫路市東辻井3-151-1',
    hours: '平日・土曜 9:30 - 19:00 / 日曜・祝日 9:00 - 18:30',
    closed: '月曜日',
    bookingUrl: 'https://beauty.hotpepper.jp/slnH000375147/coupon/',
    reviewUrl: 'https://beauty.hotpepper.jp/slnH000375147/review/',
    image: 'assets/tsujii.jpg'
  },
  {
    id: 'agaho',
    name: 'Lumiere 姫路英賀保本店',
    area: 'HIMEJI AGAHO',
    address: '兵庫県姫路市飾磨区英賀清水町2-23-3',
    hours: '平日 9:30 - 19:00 / 日曜・祝日 9:00 - 18:30',
    closed: '—',
    bookingUrl: 'https://beauty.hotpepper.jp/slnH000446258/coupon/',
    reviewUrl: 'https://beauty.hotpepper.jp/slnH000446258/review/',
    image: 'assets/agaho.jpg'
  }
];

Object.assign(window, { useState, useEffect, useRef, useCallback, useMemo, useReveal, Reveal, MaskLine, MaskImage, CountUp, SALONS, track });
