/* global React, gsap, ScrollTrigger */
const { useRef, useEffect, useLayoutEffect, useState, useMemo } = React;

const prefersReduced =
  typeof window !== 'undefined' &&
  window.matchMedia &&
  window.matchMedia('(prefers-reduced-motion: reduce)').matches;

/* ---------------------------------- Icons --------------------------------- */
function Icon({ d, paths, size = 22, stroke = 1.6, className = '', style }) {
  return (
    <svg
      width={size} height={size} viewBox="0 0 24 24" fill="none"
      stroke="currentColor" strokeWidth={stroke} strokeLinecap="round"
      strokeLinejoin="round" className={className} style={style} aria-hidden="true"
    >
      {d ? <path d={d} /> : paths}
    </svg>
  );
}
const ChevronDown = (p) => <Icon {...p} d="M6 9l6 6 6-6" />;
const ArrowUpRight = (p) => <Icon {...p} paths={<><path d="M7 17L17 7" /><path d="M7 7h10v10" /></>} />;
const MailIcon = (p) => <Icon {...p} paths={<><rect x="2.5" y="4.5" width="19" height="15" rx="2.5" /><path d="M3 6.5l9 6 9-6" /></>} />;
const GithubIcon = (p) => (
  <Icon {...p} paths={<path d="M9 19c-4.3 1.4-4.3-2.5-6-3m12 5v-3.5c0-1 .1-1.4-.5-2 2.8-.3 5.5-1.4 5.5-6A4.6 4.6 0 0 0 18.5 6 4.3 4.3 0 0 0 18 2.5s-1.1-.3-3.5 1.3a12 12 0 0 0-6 0C6.1 2.2 5 2.5 5 2.5A4.3 4.3 0 0 0 4.5 6 4.6 4.6 0 0 0 3.5 9.5c0 4.6 2.7 5.7 5.5 6-.6.6-.6 1.2-.5 2V21" />} />
);
const LinkedinIcon = (p) => (
  <Icon {...p} paths={<><path d="M16 8a6 6 0 0 1 6 6v6h-4v-6a2 2 0 0 0-4 0v6h-4v-9h4v1.5" /><rect x="2" y="9" width="4" height="11" /><circle cx="4" cy="4" r="2" /></>} />
);

/* ------------------------------- Buttons ---------------------------------- */
function ContactButton({ label = 'Me contacter', href = '#contact', large = false, className = '' }) {
  return (
    <a
      href={href}
      className={
        'group inline-flex items-center gap-3 rounded-full border-[1.5px] border-sage ' +
        'uppercase tracking-[0.22em] font-medium text-bone transition-all duration-300 ' +
        'hover:bg-sage hover:text-night ' +
        (large ? 'px-10 py-4 md:px-14 md:py-5 text-sm md:text-lg ' : 'px-8 py-3 md:px-12 md:py-4 text-sm md:text-base ') +
        className
      }
    >
      <span>{label}</span>
      <ArrowUpRight size={large ? 20 : 17} className="transition-transform duration-300 group-hover:translate-x-0.5 group-hover:-translate-y-0.5" />
    </a>
  );
}

function GhostButton({ label = 'Voir le projet', href = '#', className = '' }) {
  return (
    <a
      href={href}
      onClick={(e) => { if (href === '#') e.preventDefault(); }}
      className={
        'group inline-flex items-center gap-2.5 rounded-full border-2 border-bone ' +
        'px-5 py-2.5 md:px-7 md:py-3 text-[0.7rem] md:text-xs uppercase tracking-[0.22em] font-medium ' +
        'text-bone transition-colors duration-300 hover:bg-[rgba(232,237,233,0.1)] ' + className
      }
    >
      <span>{label}</span>
      <ArrowUpRight size={15} className="transition-transform duration-300 group-hover:translate-x-0.5 group-hover:-translate-y-0.5" />
    </a>
  );
}

/* ----------------------- Scroll-reveal: FadeIn ---------------------------- */
function FadeIn({ children, delay = 0, duration = 0.7, x = 0, y = 30, className = '', as = 'div' }) {
  const ref = useRef(null);
  useLayoutEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (prefersReduced) { gsap.set(el, { opacity: 1, x: 0, y: 0 }); return; }
    const ctx = gsap.context(() => {
      gsap.fromTo(
        el,
        { opacity: 0, x, y },
        {
          opacity: 1, x: 0, y: 0, duration, delay, ease: 'power3.out',
          scrollTrigger: { trigger: el, start: 'top 85%', once: true },
        }
      );
    }, el);
    return () => ctx.revert();
  }, []);
  const Tag = as;
  return <Tag ref={ref} className={className} style={{ opacity: 0 }}>{children}</Tag>;
}

/* ------------------- Scroll-scrubbed character reveal ---------------------- */
function AnimatedText({ text, className = '', style }) {
  const ref = useRef(null);
  const words = useMemo(() => text.split(' '), [text]);
  useLayoutEffect(() => {
    const el = ref.current;
    if (!el) return;
    const chars = el.querySelectorAll('.at-char');
    if (prefersReduced) { gsap.set(chars, { opacity: 1 }); return; }
    const ctx = gsap.context(() => {
      gsap.set(chars, { opacity: 0.18 });
      gsap.to(chars, {
        opacity: 1,
        ease: 'none',
        stagger: 0.5,
        scrollTrigger: {
          trigger: el,
          start: 'top 80%',
          end: 'bottom 35%',
          scrub: true,
        },
      });
    }, el);
    return () => ctx.revert();
  }, [text]);
  return (
    <p ref={ref} className={className} style={style}>
      {words.map((w, wi) => (
        <span key={wi} style={{ display: 'inline-block', whiteSpace: 'nowrap' }}>
          {w.split('').map((c, ci) => (
            <span key={ci} className="at-char" style={{ display: 'inline-block' }}>{c}</span>
          ))}
          {wi < words.length - 1 && <span className="at-char">&nbsp;</span>}
        </span>
      ))}
    </p>
  );
}

/* --------------------- Section label (— LABEL) ---------------------------- */
function SectionLabel({ children, center = false }) {
  return (
    <div className={'flex items-center gap-3 ' + (center ? 'justify-center' : '')}>
      <span className="h-px w-8 bg-sage" />
      <span className="text-sage uppercase tracking-[0.3em] text-xs md:text-sm font-medium">{children}</span>
    </div>
  );
}

/* --------------------- Responsive desktop-only GSAP builder ---------------- */
/* gsap.matchMedia's live MediaQueryList reactivity is unreliable in this preview
   environment (it latches to the mount-time width). This measures innerWidth after
   load + rAF and on a debounced resize, building/killing the desktop GSAP setup as
   the breakpoint is crossed. `build` must return its own cleanup (e.g. ctx.revert). */
function responsiveDesktop(build, breakpoint = 768) {
  let active = false, cleanup = null, raf = 0, to = 0;
  const apply = () => {
    const desktop = window.innerWidth >= breakpoint;
    if (desktop && !active) {
      active = true;
      cleanup = build() || null;
      if (window.ScrollTrigger) ScrollTrigger.refresh();
    } else if (!desktop && active) {
      active = false;
      if (cleanup) { cleanup(); cleanup = null; }
      if (window.ScrollTrigger) ScrollTrigger.refresh();
    }
  };
  // Defer to rAF (past React's synchronous layout phase) so the pin's scrub wires
  // correctly. refreshPriority on each pin keeps positions ordered regardless of
  // creation order, so we no longer need synchronous (document-order) creation.
  raf = requestAnimationFrame(apply);
  const onLoad = () => apply();
  const onResize = () => { clearTimeout(to); to = setTimeout(apply, 200); };
  if (document.readyState !== 'complete') window.addEventListener('load', onLoad);
  window.addEventListener('resize', onResize);
  return () => {
    if (raf) cancelAnimationFrame(raf);
    window.removeEventListener('load', onLoad);
    window.removeEventListener('resize', onResize);
    clearTimeout(to);
    if (cleanup) cleanup();
  };
}

Object.assign(window, {
  prefersReduced,
  Icon, ChevronDown, ArrowUpRight, MailIcon, GithubIcon, LinkedinIcon,
  ContactButton, GhostButton, FadeIn, AnimatedText, SectionLabel, responsiveDesktop,
});
