import './style.css';

const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
const isAndroidDevice = () => /Android/i.test(window.navigator.userAgent || '');
const isIosDevice = () => /iPhone|iPad|iPod/i.test(window.navigator.userAgent || '');
const isTabletViewport = () => {
  const width = window.innerWidth || document.documentElement.clientWidth || 0;
  return width > 767 && width <= 1024;
};
const isDesktopViewport = () => {
  const width = window.innerWidth || document.documentElement.clientWidth || 0;
  return width > 1024;
};

const getOrientationVector = (orientation, legacyAxis = 'y') => {
  const map = {
    up: { x: 0, y: -1 },
    down: { x: 0, y: 1 },
    left: { x: -1, y: 0 },
    right: { x: 1, y: 0 },
    'up-left': { x: -0.72, y: -0.72 },
    'up-right': { x: 0.72, y: -0.72 },
    'down-left': { x: -0.72, y: 0.72 },
    'down-right': { x: 0.72, y: 0.72 },
  };

  if (map[orientation]) {
    return map[orientation];
  }

  const axis = legacyAxis.includes('x') ? 'x' : 'y';
  const direction = legacyAxis.includes('reverse') ? -1 : 1;

  return axis === 'x' ? { x: direction, y: 0 } : { x: 0, y: direction };
};

const getDepthMultiplier = (depth) => {
  if (depth === 'subtle') {
    return 0.65;
  }

  if (depth === 'strong') {
    return 1.35;
  }

  return 1;
};

const getScrollProgress = (element, viewportRange) => {
  const rect = element.getBoundingClientRect();
  const viewportHeight = window.innerHeight || document.documentElement.clientHeight || 1;
  const viewportCenter = viewportHeight / 2;
  const elementCenter = rect.top + rect.height / 2;
  const range = Math.max(1, viewportHeight * viewportRange);

  return clamp((viewportCenter - elementCenter) / range, -1, 1);
};

const setVar = (element, name, value) => {
  element.style.setProperty(name, value);
};

const ensureBackgroundLayer = (element) => {
  let layer = element.querySelector(':scope > .calio-parallax-bg-layer');
  const computed = window.getComputedStyle(element);
  const backgroundImage = computed.backgroundImage || 'none';

  if (!layer) {
    layer = document.createElement('span');
    layer.className = 'calio-parallax-bg-layer';
    layer.setAttribute('aria-hidden', 'true');
    element.insertBefore(layer, element.firstChild);
  }

  if (backgroundImage === 'none' && element.classList.contains('has-calio-parallax-bg-layer')) {
    return layer;
  }

  layer.style.backgroundImage = backgroundImage;
  layer.style.backgroundPosition = computed.backgroundPosition;
  layer.style.backgroundSize = computed.backgroundSize;
  layer.style.backgroundRepeat = computed.backgroundRepeat;
  layer.style.backgroundAttachment = 'scroll';
  layer.style.backgroundOrigin = computed.backgroundOrigin;
  layer.style.backgroundClip = computed.backgroundClip;

  if (backgroundImage !== 'none') {
    element.classList.add('has-calio-parallax-bg-layer');
    element.style.setProperty('--calio-parallax-bg-color', computed.backgroundColor || 'transparent');
    element.style.backgroundImage = 'none';
    element.style.backgroundColor = 'var(--calio-parallax-bg-color)';
  }

  return layer;
};

const initRevealParallax = (element) => {
  if (!element || element.dataset.calioRevealParallaxInit === 'yes') {
    return;
  }

  element.dataset.calioRevealParallaxInit = 'yes';

  const revealEnabled = element.dataset.calioRevealEnabled === 'yes';
  const revealOnce = element.dataset.calioRevealOnce !== 'no';
  const revealPreset = element.dataset.calioRevealPreset || 'fade-up';
  const revealDuration = Number.parseInt(element.dataset.calioRevealDuration || '700', 10) || 700;
  const revealDelay = Number.parseInt(element.dataset.calioRevealDelay || '0', 10) || 0;
  const revealDistance = Number.parseInt(element.dataset.calioRevealDistance || '28', 10) || 28;
  const revealThreshold = clamp((Number.parseFloat(element.dataset.calioRevealThreshold || '18') || 18) / 100, 0, 0.8);
  const revealEasingSkin = element.dataset.calioRevealEasingSkin || 'balanced';
  const parallaxEnabled = element.dataset.calioParallaxEnabled === 'yes';
  const parallaxType = element.dataset.calioParallaxType || 'scroll';
  const hasBackgroundImage = () => {
    const image = window.getComputedStyle(element).backgroundImage || 'none';
    return image !== 'none';
  };
  const parallaxTarget = element.dataset.calioParallaxTarget || (hasBackgroundImage() ? 'background' : 'element');
  const parallaxOrientation = element.dataset.calioParallaxOrientation || 'up';
  const parallaxAxis = element.dataset.calioParallaxAxis || 'y';
  const parallaxSpeed = Number.parseFloat(element.dataset.calioParallaxSpeed || '140') || 0;
  const parallaxScale = (Number.parseFloat(element.dataset.calioParallaxScale || '112') || 100) / 100;
  const parallaxDelay = clamp(Number.parseFloat(element.dataset.calioParallaxDelay || '18') || 0, 0, 60);
  const parallaxAndroidEnabled = element.dataset.calioParallaxAndroid === 'yes';
  const parallaxIosEnabled = element.dataset.calioParallaxIos === 'yes';
  const parallaxTabletEnabled = element.dataset.calioParallaxTablet === 'yes';
  const parallaxDesktopEnabled = element.dataset.calioParallaxDesktop !== 'no';
  const parallaxViewport = clamp((Number.parseFloat(element.dataset.calioParallaxViewport || '100') || 100) / 100, 0.4, 1.6);
  const parallaxDepth = element.dataset.calioParallaxDepth || 'balanced';
  const parallaxOverflow = element.dataset.calioParallaxOverflow || 'visible';
  const prefersReducedMotion = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
  const isEditMode = typeof window.elementorFrontend?.isEditMode === 'function'
    ? window.elementorFrontend.isEditMode()
    : document.body.classList.contains('elementor-editor-active');
  const computedBaseTransform = window.getComputedStyle(element).transform;
  const baseTransform = computedBaseTransform && computedBaseTransform !== 'none'
    ? computedBaseTransform
    : 'translate3d(0, 0, 0)';

  element.classList.add('calio-reveal-host');
  if (revealEnabled) {
    element.classList.add(`is-reveal-${revealPreset}`, `is-easing-${revealEasingSkin}`);
  }
  if (parallaxTarget === 'background') {
    element.classList.add('calio-parallax-target-background');
  } else {
    element.classList.add('calio-parallax-target-element');
  }

  setVar(element, '--calio-reveal-duration', `${revealDuration}ms`);
  setVar(element, '--calio-reveal-delay', `${revealDelay}ms`);
  setVar(element, '--calio-reveal-distance', `${revealDistance}px`);
  setVar(element, '--calio-parallax-scale', String(parallaxScale));
  setVar(element, '--calio-base-transform', baseTransform);
  setVar(element, '--calio-parallax-overflow', parallaxTarget === 'background' ? 'hidden' : parallaxOverflow);

  if (prefersReducedMotion) {
    element.classList.add('is-reveal-visible');
    return;
  }

  if (revealEnabled && !isEditMode) {
    const observer = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          element.classList.add('is-reveal-visible');
          if (revealOnce) {
            observer.unobserve(element);
          }
        } else if (!revealOnce) {
          element.classList.remove('is-reveal-visible');
        }
      });
    }, { threshold: revealThreshold, rootMargin: `0px 0px -${Math.round(revealThreshold * 22)}% 0px` });

    observer.observe(element);
  } else {
    element.classList.add('is-reveal-visible');
  }

  const shouldDisableForDevice = (isAndroidDevice() && !parallaxAndroidEnabled)
    || (isIosDevice() && !parallaxIosEnabled)
    || (isTabletViewport() && !parallaxTabletEnabled)
    || (isDesktopViewport() && !parallaxDesktopEnabled);

  if (!parallaxEnabled || shouldDisableForDevice) {
    return;
  }

  const vector = getOrientationVector(parallaxOrientation, parallaxAxis);
  const depthMultiplier = getDepthMultiplier(parallaxDepth);
  let backgroundLayer = parallaxTarget === 'background' ? ensureBackgroundLayer(element) : null;
  let currentX = 0;
  let currentY = 0;
  let currentScale = 1;
  let currentOpacity = 1;
  let targetX = 0;
  let targetY = 0;
  let targetScale = 1;
  let targetOpacity = 1;
  let animating = false;
  let lastFrameTime = 0;

  const applyValues = () => {
    const now = window.performance?.now?.() || Date.now();
    const delta = lastFrameTime ? clamp(now - lastFrameTime, 8, 48) : 16.7;
    lastFrameTime = now;

    const progress = getScrollProgress(element, parallaxViewport);
    const movement = progress * parallaxSpeed * depthMultiplier;
    const absProgress = Math.abs(progress);

    targetX = vector.x * movement;
    targetY = vector.y * movement;
    targetScale = parallaxScale;
    targetOpacity = 1;

    switch (parallaxType) {
      case 'scroll-zoom':
        targetScale = parallaxScale + absProgress * 0.08 * depthMultiplier;
        break;
      case 'fade':
        targetOpacity = clamp(1 - absProgress * 0.35 * depthMultiplier, 0.45, 1);
        break;
      case 'in-motion':
      case 'scroll':
      default:
        break;
    }

    const smoothing = parallaxDelay <= 0 ? 1 : clamp(parallaxDelay / 60, 0.08, 1);
    const ease = parallaxDelay <= 0 ? 1 : 1 - Math.exp(-delta / (80 + smoothing * 520));
    currentX += (targetX - currentX) * ease;
    currentY += (targetY - currentY) * ease;
    currentScale += (targetScale - currentScale) * ease;
    currentOpacity += (targetOpacity - currentOpacity) * ease;

    if (parallaxTarget === 'background') {
      backgroundLayer = ensureBackgroundLayer(element);
      setVar(element, '--calio-parallax-bg-x', `${currentX.toFixed(3)}px`);
      setVar(element, '--calio-parallax-bg-y', `${currentY.toFixed(3)}px`);
      setVar(element, '--calio-parallax-bg-scale', currentScale.toFixed(4));
      setVar(element, '--calio-parallax-bg-opacity', currentOpacity.toFixed(4));
    } else {
      const translate = `translate3d(${currentX.toFixed(3)}px, ${currentY.toFixed(3)}px, 0)`;
      setVar(element, '--calio-parallax-transform', `${translate} scale(${currentScale.toFixed(4)})`);
      setVar(element, '--calio-parallax-opacity', currentOpacity.toFixed(4));
    }

    window.requestAnimationFrame(applyValues);
  };

  const requestUpdate = () => {
    if (animating) {
      return;
    }

    animating = true;
    window.requestAnimationFrame(applyValues);
  };

  requestUpdate();
  window.addEventListener('scroll', requestUpdate, { passive: true });
  window.addEventListener('resize', requestUpdate);
};

const initAllRevealParallax = (scope = document) => {
  const hosts = scope.matches?.('[data-calio-reveal-parallax="yes"]')
    ? [scope]
    : Array.from(scope.querySelectorAll('[data-calio-reveal-parallax="yes"]'));

  hosts.forEach(initRevealParallax);
};

document.addEventListener('DOMContentLoaded', () => {
  initAllRevealParallax(document);
});

let calioRevealParallaxHookBound = false;
const bindElementorRevealParallax = () => {
  if (calioRevealParallaxHookBound) {
    return true;
  }

  if (!window.elementorFrontend?.hooks) {
    return false;
  }

  window.elementorFrontend.hooks.addAction('frontend/element_ready/global', ($scope) => {
    const root = $scope?.[0] || null;
    if (root) {
      initAllRevealParallax(root);
    }
  });

  calioRevealParallaxHookBound = true;
  return true;
};

window.addEventListener('elementor/frontend/init', bindElementorRevealParallax);
if (!bindElementorRevealParallax()) {
  let attempts = 0;
  const timer = window.setInterval(() => {
    attempts += 1;
    if (bindElementorRevealParallax() || attempts >= 40) {
      window.clearInterval(timer);
    }
  }, 250);
}
