/* global React */
// convive · site — shared primitives, exported to window for other scripts
const { useState, useEffect, useRef, useCallback } = React;

// ── Smile mark (the brand underline) ─────────────────────────
function Smile({ width = 120, color = 'var(--terra)', stroke = 7, style }) {
  const h = width * 0.17;
  return (
    <svg width={width} height={h} viewBox="0 0 200 34" fill="none" aria-hidden="true" style={{ display: 'block', overflow: 'visible', ...style }}>
      <path d="M 6 9 Q 100 41 194 9" stroke={color} strokeWidth={stroke} strokeLinecap="round" />
    </svg>
  );
}

// ── Wordmark + Logo lockup ───────────────────────────────────
function Wordmark({ size = 34, color = 'var(--ink)', style }) {
  return (
    <span className="wordmark" style={{ fontSize: size, color, ...style }}>convive</span>
  );
}

function Logo({ size = 34, color = 'var(--ink)', smileColor = 'var(--terra)', gap = 0.12, smileScale = 0.72 }) {
  return (
    <span style={{ display: 'inline-flex', flexDirection: 'column', alignItems: 'center', gap: size * gap, lineHeight: 1 }}>
      <Wordmark size={size} color={color} />
      <Smile width={size * smileScale * 3.0} color={smileColor} stroke={Math.max(3, size * 0.10)} />
    </span>
  );
}

// ── Scroll-reveal hook (IntersectionObserver) ────────────────
function useInView(opts = {}) {
  const ref = useRef(null);
  const [inView, setInView] = useState(false);
  useEffect(() => {
    const el = ref.current;
    if (!el) { setInView(true); return; }
    if (typeof IntersectionObserver === 'undefined') { setInView(true); return; }

    // already on screen at mount? reveal immediately
    const r = el.getBoundingClientRect();
    const vh = window.innerHeight || document.documentElement.clientHeight;
    if (r.top < vh * 0.95 && r.bottom > 0) { setInView(true); return; }

    let done = false;
    const reveal = () => { if (!done) { done = true; setInView(true); } };
    const io = new IntersectionObserver(([e]) => {
      if (e.isIntersecting) { reveal(); io.unobserve(el); }
    }, { threshold: opts.threshold ?? 0.18, rootMargin: opts.rootMargin ?? '0px 0px -8% 0px' });
    io.observe(el);
    // failsafe: if IO never fires (non-scrolling harness, suppressed callbacks), reveal anyway
    const t = setTimeout(reveal, 1200);
    return () => { clearTimeout(t); io.disconnect(); };
  }, []);
  return [ref, inView];
}

// ── Reveal wrapper ───────────────────────────────────────────
function Reveal({ children, delay = 0, as: Tag = 'div', className = '', style, ...rest }) {
  const [ref, inView] = useInView();
  return (
    <Tag
      ref={ref}
      className={`reveal ${inView ? 'in' : ''} ${className}`}
      style={{ transitionDelay: `${delay}ms`, ...style }}
      {...rest}
    >
      {children}
    </Tag>
  );
}

// ── Count-up number (animates when in view) ──────────────────
function CountUp({ to, duration = 1300, prefix = '', suffix = '', decimals = 0, style, className }) {
  const [ref, inView] = useInView({ threshold: 0.4 });
  const [val, setVal] = useState(0);
  useEffect(() => {
    if (!inView) return;
    let raf, start;
    const step = (t) => {
      if (start == null) start = t;
      const p = Math.min(1, (t - start) / duration);
      const eased = 1 - Math.pow(1 - p, 3);
      setVal(to * eased);
      if (p < 1) raf = requestAnimationFrame(step);
    };
    raf = requestAnimationFrame(step);
    return () => cancelAnimationFrame(raf);
  }, [inView, to]);
  const fmt = val.toLocaleString('pt-BR', { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
  return <span ref={ref} className={`tnum ${className || ''}`} style={style}>{prefix}{fmt}{suffix}</span>;
}

// ── Scroll parallax engine ───────────────────────────────────
// Elements with [data-px] drift as you scroll. data-px = speed (px-per-viewport),
// data-px-rot = degrees of tilt, data-px-x = horizontal drift speed.
// Runs one rAF loop for all elements; respects prefers-reduced-motion.
function ParallaxEngine() {
  useEffect(() => {
    if (typeof window === 'undefined') return;
    if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
    let els = [];
    let raf = null;
    const collect = () => { els = Array.from(document.querySelectorAll('[data-px]')); };
    const update = () => {
      raf = null;
      const vh = window.innerHeight || 800;
      for (const el of els) {
        const r = el.getBoundingClientRect();
        if (r.bottom < -200 || r.top > vh + 200) continue; // offscreen: skip
        const center = r.top + r.height / 2;
        const delta = (center - vh / 2) / vh; // ~ -0.6..0.6 across viewport
        const sp = parseFloat(el.dataset.px) || 0;
        const spx = parseFloat(el.dataset.pxX) || 0;
        const rot = parseFloat(el.dataset.pxRot) || 0;
        const y = (-delta * sp).toFixed(2);
        const x = (-delta * spx).toFixed(2);
        let tf = `translate3d(${x}px, ${y}px, 0)`;
        if (rot) tf += ` rotate(${(-delta * rot).toFixed(2)}deg)`;
        el.style.transform = tf;
      }
    };
    const onScroll = () => { if (raf == null) raf = requestAnimationFrame(update); };
    collect(); update();
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', () => { collect(); onScroll(); });
    const t1 = setTimeout(() => { collect(); update(); }, 300);
    const t2 = setTimeout(() => { collect(); update(); }, 1200);
    return () => { window.removeEventListener('scroll', onScroll); clearTimeout(t1); clearTimeout(t2); if (raf) cancelAnimationFrame(raf); };
  }, []);
  return null;
}

// ── Minimal stroke icons ─────────────────────────────────────
function Icon({ name, size = 22, stroke = 1.8, color = 'currentColor', style }) {
  const common = { width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke: color, strokeWidth: stroke, strokeLinecap: 'round', strokeLinejoin: 'round', style };
  switch (name) {
    case 'arrow': return <svg {...common}><path d="M5 12h14M13 6l6 6-6 6"/></svg>;
    case 'arrow-left': return <svg {...common}><path d="M19 12H5M11 6l-6 6 6 6"/></svg>;
    case 'arrow-down': return <svg {...common}><path d="M12 5v14M6 13l6 6 6-6"/></svg>;
    case 'check': return <svg {...common}><path d="M4 12.5l5 5L20 6.5"/></svg>;
    case 'whatsapp': return <svg {...common}><path d="M3 21l1.7-5A8.5 8.5 0 1 1 8 18.3L3 21z"/><path d="M8.5 9.5c0 3.5 2.5 6 6 6"/></svg>;
    case 'mail': return <svg {...common}><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 7l9 6 9-6"/></svg>;
    case 'instagram': return <svg {...common}><rect x="3" y="3" width="18" height="18" rx="5"/><circle cx="12" cy="12" r="4"/><path d="M17 7h.01"/></svg>;
    case 'pin': return <svg {...common}><path d="M12 21s7-6.2 7-11a7 7 0 1 0-14 0c0 4.8 7 11 7 11z"/><circle cx="12" cy="10" r="2.5"/></svg>;
    case 'spark': return <svg {...common}><path d="M12 3v4M12 17v4M3 12h4M17 12h4M6 6l2.5 2.5M15.5 15.5L18 18M18 6l-2.5 2.5M8.5 15.5L6 18"/></svg>;
    case 'layers': return <svg {...common}><path d="M12 3l9 5-9 5-9-5 9-5z"/><path d="M3 13l9 5 9-5"/></svg>;
    case 'shield': return <svg {...common}><path d="M12 3l7 3v5c0 4.5-3 8-7 10-4-2-7-5.5-7-10V6l7-3z"/></svg>;
    case 'pulse': return <svg {...common}><path d="M3 12h4l2 6 4-14 2 8h6"/></svg>;
    case 'briefcase': return <svg {...common}><rect x="3" y="7" width="18" height="13" rx="2"/><path d="M8 7V5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>;
    case 'utensils': return <svg {...common}><path d="M5 3v7a2 2 0 0 0 4 0V3M7 11v10M17 3c-1.5 0-3 1.8-3 5s1.5 4 3 4v9"/></svg>;
    case 'handshake': return <svg {...common}><path d="M8 12l2.5 2.5a1.5 1.5 0 0 0 2.1 0l4.4-4.4M3 9l4-3 5 3 5-3 4 3"/><path d="M3 9v6l5 4M21 9v6l-5 4"/></svg>;
    case 'palette': return <svg {...common}><path d="M12 3a9 9 0 1 0 0 18c1.2 0 2-.9 2-2 0-1.6 1.3-2 2.5-2H18a3 3 0 0 0 3-3 8 8 0 0 0-9-8z"/><circle cx="7.5" cy="11" r="1"/><circle cx="11" cy="7.5" r="1"/><circle cx="15.5" cy="8.5" r="1"/></svg>;
    case 'plus': return <svg {...common}><path d="M12 5v14M5 12h14"/></svg>;
    case 'search': return <svg {...common}><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>;
    case 'x': return <svg {...common}><path d="M6 6l12 12M18 6L6 18"/></svg>;
    case 'menu': return <svg {...common}><path d="M4 7h16M4 12h16M4 17h16"/></svg>;
    case 'wallet': return <svg {...common}><rect x="3" y="6" width="18" height="13" rx="2"/><path d="M3 10h18M16 14h2"/></svg>;
    case 'kanban': return <svg {...common}><rect x="3" y="4" width="5" height="16" rx="1"/><rect x="10" y="4" width="5" height="10" rx="1"/><rect x="17" y="4" width="4" height="13" rx="1"/></svg>;
    case 'users': return <svg {...common}><circle cx="9" cy="8" r="3"/><path d="M3 20c0-3 2.5-5 6-5s6 2 6 5"/><path d="M16 6a3 3 0 0 1 0 5M21 20c0-2.4-1.3-4-3.5-4.6"/></svg>;
    case 'chart': return <svg {...common}><path d="M4 19V5M4 19h16M8 16v-4M12 16V8M16 16v-7"/></svg>;
    case 'route': return <svg {...common}><circle cx="6" cy="6" r="2.5"/><circle cx="18" cy="18" r="2.5"/><path d="M6 8.5V13a4 4 0 0 0 4 4h5.5"/></svg>;
    default: return null;
  }
}

// ── Scroll progress bar ──────────────────────────────────────
function ScrollProgress() {
  const ref = useRef(null);
  useEffect(() => {
    let raf;
    const update = () => {
      const el = ref.current; if (!el) return;
      const h = document.documentElement.scrollHeight - window.innerHeight;
      el.style.width = (h > 0 ? (window.scrollY / h) * 100 : 0) + '%';
    };
    const onScroll = () => { cancelAnimationFrame(raf); raf = requestAnimationFrame(update); };
    window.addEventListener('scroll', onScroll, { passive: true });
    window.addEventListener('resize', onScroll);
    update();
    return () => { window.removeEventListener('scroll', onScroll); window.removeEventListener('resize', onScroll); cancelAnimationFrame(raf); };
  }, []);
  return <div id="scroll-progress" ref={ref} />;
}

// ── Kinetic brand ribbon ─────────────────────────────────────
function Marquee({ items }) {
  const data = items || [
    'Tecnologia que convive com você',
    'Complexo por trás, simples na frente',
    'Comercial · Operação · Financeiro num lugar só',
    'Sob medida, de verdade',
    'Vem conviver',
  ];
  const Copy = ({ hidden }) => (
    <div style={{ display: 'inline-flex' }} aria-hidden={hidden}>
      {data.map((t, i) => (
        <span key={i} className="marquee-item">{t}<span className="marquee-dot" /></span>
      ))}
    </div>
  );
  return (
    <div className="marquee">
      <div className="marquee-track">
        <Copy hidden={false} /><Copy hidden={true} />
      </div>
    </div>
  );
}

// ── Animated nav logo: smiles + i-dot hops periodically ──────
function AnimatedLogo({ size = 26, color = 'var(--ink)', smileColor = 'var(--terra)' }) {
  const smileW = size * 2.16;
  const stroke = Math.max(3, size * 0.10);
  return (
    <span style={{ display: 'inline-flex', flexDirection: 'column', alignItems: 'center', gap: size * 0.12, lineHeight: 1 }}>
      <span className="wordmark" style={{ fontSize: size, color, position: 'relative' }}>
        conv<span style={{ position: 'relative', display: 'inline-block' }}>ı<span className="cv-idot" style={{ position: 'absolute', left: '0.12em', bottom: '0.72em', width: '0.14em', height: '0.16em', borderRadius: '50%', background: 'currentColor' }}></span></span>ve
      </span>
      <svg className="cv-asmile" width={smileW} height={smileW * 0.17} viewBox="0 0 200 34" fill="none" aria-hidden="true" style={{ display: 'block', overflow: 'visible' }}>
        <path d="M 6 9 Q 100 41 194 9" stroke={smileColor} strokeWidth={stroke} strokeLinecap="round" />
      </svg>
    </span>
  );
}

// ── View router (menu = troca de tela, sem scroll) ───────────
// Mapeia cada âncora usada no site para uma "tela" (view key).
const ANCHOR_TO_VIEW = {
  top: 'top',
  'para-quem': 'para-quem', 'como-ajuda': 'para-quem',
  produtos: 'produtos',
  designacao: 'designacao',
  produto: 'produto',
  precificacao: 'precificacao', filosofia: 'precificacao',
  sobre: 'sobre', faq: 'sobre', atendimento: 'contato', cases: 'produto',
  contato: 'contato', 'fale-conosco': 'contato',
  conviver: 'conviver',
};
function routeFromHash() {
  const a = (typeof location !== 'undefined' ? (location.hash || '#top') : '#top').replace(/^#/, '') || 'top';
  return ANCHOR_TO_VIEW[a] || 'top';
}
// Hook: current view key, updates on hashchange (and scrolls to top on change).
function useRoute() {
  const [route, setRoute] = useState(routeFromHash());
  useEffect(() => {
    const on = () => {
      setRoute(routeFromHash());
      window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
    };
    window.addEventListener('hashchange', on);
    return () => window.removeEventListener('hashchange', on);
  }, []);
  return route;
}

Object.assign(window, { Smile, Wordmark, Logo, AnimatedLogo, useInView, Reveal, CountUp, Icon, ScrollProgress, Marquee, ParallaxEngine, routeFromHash, useRoute, ANCHOR_TO_VIEW });
