/* ==========================================
   EFFECTS — Count-up, 3D Tilt, Magnetic buttons
   Auto-attaches to existing DOM via MutationObserver,
   so pages don't need to be edited individually.
   ========================================== */

(function () {
  const isTouch = window.matchMedia('(hover: none)').matches;
  const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  /* ----------- Count-up ----------- */
  const NUM_RE = /^\s*([+-]?)\s*([\d]+(?:[.,]\d+)?)\s*([%hxXkKmM]?)\s*$/;

  const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);

  const animateCount = (el, target, decimals, prefix, suffix, sep) => {
    const duration = 1400;
    const start = performance.now();
    const from = 0;
    const tick = (now) => {
      const t = Math.min(1, (now - start) / duration);
      const v = from + (target - from) * easeOutCubic(t);
      const fixed = decimals > 0 ? v.toFixed(decimals) : Math.round(v).toString();
      el.textContent = prefix + (sep === ',' ? fixed.replace('.', ',') : fixed) + suffix;
      if (t < 1) requestAnimationFrame(tick);
    };
    requestAnimationFrame(tick);
  };

  const countObserver = new IntersectionObserver((entries) => {
    entries.forEach((e) => {
      if (!e.isIntersecting) return;
      const el = e.target;
      countObserver.unobserve(el);
      const m = el.dataset.countParsed;
      if (!m) return;
      const { target, decimals, prefix, suffix, sep } = JSON.parse(m);
      if (reducedMotion) return;
      animateCount(el, target, decimals, prefix, suffix, sep);
    });
  }, { threshold: 0.4 });

  const attachCount = (el) => {
    if (el.dataset.countBound) return;
    const text = el.textContent.trim();
    const m = text.match(NUM_RE);
    if (!m) return;
    const sign = m[1] || '';
    const numStr = m[2];
    const suffix = m[3] || '';
    const sep = numStr.includes(',') ? ',' : '.';
    const target = parseFloat(numStr.replace(',', '.'));
    if (isNaN(target)) return;
    const decimals = numStr.includes('.') || numStr.includes(',')
      ? numStr.split(/[.,]/)[1].length : 0;
    el.dataset.countBound = '1';
    el.dataset.countParsed = JSON.stringify({
      target, decimals, prefix: sign, suffix, sep
    });
    el.textContent = sign + (decimals > 0 ? (sep === ',' ? '0,' + '0'.repeat(decimals) : '0.' + '0'.repeat(decimals)) : '0') + suffix;
    countObserver.observe(el);
  };

  /* ----------- 3D Tilt ----------- */
  const attachTilt = (el) => {
    if (el.dataset.tiltBound || isTouch || reducedMotion) return;
    el.dataset.tiltBound = '1';
    el.classList.add('tilt-card');
    const max = 8;
    let rect;
    let raf;
    const onEnter = () => { rect = el.getBoundingClientRect(); };
    const onMove = (ev) => {
      if (!rect) rect = el.getBoundingClientRect();
      const x = (ev.clientX - rect.left) / rect.width - 0.5;
      const y = (ev.clientY - rect.top) / rect.height - 0.5;
      if (raf) cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        el.style.transform = `perspective(900px) rotateX(${(-y * max).toFixed(2)}deg) rotateY(${(x * max).toFixed(2)}deg) translateZ(0)`;
      });
    };
    const onLeave = () => {
      if (raf) cancelAnimationFrame(raf);
      el.style.transform = '';
      rect = null;
    };
    el.addEventListener('mouseenter', onEnter);
    el.addEventListener('mousemove', onMove);
    el.addEventListener('mouseleave', onLeave);
  };

  /* ----------- Magnetic buttons ----------- */
  const attachMagnetic = (el) => {
    if (el.dataset.magneticBound || isTouch || reducedMotion) return;
    el.dataset.magneticBound = '1';
    el.classList.add('magnetic');
    const strength = 0.35;
    let rect;
    let raf;
    const onEnter = () => { rect = el.getBoundingClientRect(); };
    const onMove = (ev) => {
      if (!rect) rect = el.getBoundingClientRect();
      const dx = ev.clientX - (rect.left + rect.width / 2);
      const dy = ev.clientY - (rect.top + rect.height / 2);
      if (raf) cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        el.style.transform = `translate(${(dx * strength).toFixed(2)}px, ${(dy * strength).toFixed(2)}px)`;
      });
    };
    const onLeave = () => {
      if (raf) cancelAnimationFrame(raf);
      el.style.transform = '';
      rect = null;
    };
    el.addEventListener('mouseenter', onEnter);
    el.addEventListener('mousemove', onMove);
    el.addEventListener('mouseleave', onLeave);
  };

  /* ----------- Auto-scan ----------- */
  // Selectors that look like stat numbers (big bold numbers).
  const COUNT_SELECTORS = [
    '.stat-num',
    '.hero-stat-num',
    '[data-countup]'
  ];
  // Selectors that should tilt.
  const TILT_SELECTORS = [
    '.project-card',
    '.solution-card',
    '.feature-card',
    '.metodo-pillar',
    '.blog-card',
    '.case-card',
    '[data-tilt]'
  ];
  // Selectors that should be magnetic.
  const MAGNETIC_SELECTORS = [
    '.btn-primary',
    '[data-magnetic]'
  ];

  const scan = (root) => {
    if (!root || root.nodeType !== 1) return;
    COUNT_SELECTORS.forEach((s) => {
      root.querySelectorAll(s).forEach(attachCount);
      if (root.matches && root.matches(s)) attachCount(root);
    });
    TILT_SELECTORS.forEach((s) => {
      root.querySelectorAll(s).forEach(attachTilt);
      if (root.matches && root.matches(s)) attachTilt(root);
    });
    MAGNETIC_SELECTORS.forEach((s) => {
      root.querySelectorAll(s).forEach(attachMagnetic);
      if (root.matches && root.matches(s)) attachMagnetic(root);
    });
  };

  const start = () => {
    scan(document.body);
    const mo = new MutationObserver((mutations) => {
      for (const m of mutations) {
        m.addedNodes.forEach((n) => scan(n));
      }
    });
    mo.observe(document.body, { childList: true, subtree: true });
  };

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', start);
  } else {
    // Defer one frame so React's first render is done.
    requestAnimationFrame(start);
  }

  /* ----------- React component wrappers (opt-in) ----------- */
  const CountUp = ({ value, prefix = '', suffix = '', decimals = 0, className = '', style = {} }) => {
    return (
      <span
        className={className}
        style={style}
        data-countup="1"
      >{prefix}{decimals > 0 ? Number(value).toFixed(decimals) : value}{suffix}</span>
    );
  };

  const TiltCard = ({ children, className = '', style = {}, ...rest }) => (
    <div data-tilt="1" className={className} style={style} {...rest}>{children}</div>
  );

  const MagneticButton = ({ children, className = '', as: Tag = 'button', ...rest }) => (
    <Tag data-magnetic="1" className={className} {...rest}>{children}</Tag>
  );

  window.CountUp = CountUp;
  window.TiltCard = TiltCard;
  window.MagneticButton = MagneticButton;
})();
