import './style.css';

const toInt = (value, fallback = 0) => {
  const parsed = Number.parseInt(String(value), 10);
  return Number.isFinite(parsed) ? parsed : fallback;
};

const isYes = (value) => value === 'yes' || value === 'true' || value === '1' || value === true;

const isVerticalDirection = (direction) => direction === 'bottom-top' || direction === 'top-bottom';

const initImageFlow = (root) => {
  root.querySelectorAll('.calio-image-flow').forEach((widget) => {
    if (typeof widget.__calioImageFlowDestroy === 'function') {
      widget.__calioImageFlowDestroy();
    }

    const viewport = widget.querySelector('.calio-image-flow-viewport');
    const track = widget.querySelector('.calio-image-flow-track');
    const image = widget.querySelector('.calio-image-flow-media');

    if (!(viewport instanceof HTMLElement) || !(track instanceof HTMLElement) || !(image instanceof HTMLImageElement)) {
      return;
    }

    const direction = String(widget.dataset.direction || 'bottom-top');
    const autoScroll = isYes(widget.dataset.autoScroll);
    const hasDragBar = isYes(widget.dataset.dragBar);
    const dragReverse = isYes(widget.dataset.dragReverse);
    const pauseOnHover = isYes(widget.dataset.pauseOnHover);
    const speed = Math.max(300, toInt(widget.dataset.scrollSpeed, 3800));
    const loopPause = Math.max(0, toInt(widget.dataset.loopPause, 600));
    const dragInput = widget.querySelector('.calio-image-flow-drag-range');

    widget.style.setProperty('--calio-flow-speed', `${speed}ms`);

    let timeoutId = null;
    let paused = false;
    let autoTick = false;
    let dragProgress = 0;
    let resizeFrame = null;
    let resizeObserver = null;
    let windowResizeHandler = null;
    let startEnd = { xStart: 0, yStart: 0, xEnd: 0, yEnd: 0 };

    const clearTimer = () => {
      if (timeoutId !== null) {
        window.clearTimeout(timeoutId);
        timeoutId = null;
      }
    };

    const setActive = (isActive) => {
      widget.classList.toggle('is-active', Boolean(isActive));
    };

    const computeDistance = () => {
      if (isVerticalDirection(direction)) {
        return Math.max(0, image.offsetHeight - viewport.clientHeight);
      }

      return Math.max(0, image.offsetWidth - viewport.clientWidth);
    };

    const applyOffsets = () => {
      const distance = computeDistance();
      let xStart = 0;
      let yStart = 0;
      let xEnd = 0;
      let yEnd = 0;

      switch (direction) {
        case 'top-bottom':
          yStart = -distance;
          yEnd = 0;
          break;
        case 'left-right':
          xStart = -distance;
          xEnd = 0;
          break;
        case 'right-left':
          xStart = 0;
          xEnd = -distance;
          break;
        case 'bottom-top':
        default:
          yStart = 0;
          yEnd = -distance;
          break;
      }

      widget.style.setProperty('--calio-flow-x-start', `${xStart}px`);
      widget.style.setProperty('--calio-flow-y-start', `${yStart}px`);
      widget.style.setProperty('--calio-flow-x-end', `${xEnd}px`);
      widget.style.setProperty('--calio-flow-y-end', `${yEnd}px`);
      widget.classList.toggle('has-no-distance', distance <= 0);
      startEnd = { xStart, yStart, xEnd, yEnd };

      return distance;
    };

    const setDragTransform = (progress) => {
      const ratio = Math.max(0, Math.min(1, progress));
      const x = startEnd.xStart + (startEnd.xEnd - startEnd.xStart) * ratio;
      const y = startEnd.yStart + (startEnd.yEnd - startEnd.yStart) * ratio;
      track.style.transform = `translate3d(${x}px, ${y}px, 0)`;
    };

    const syncDragProgressVisual = (progress) => {
      if (!(dragInput instanceof HTMLInputElement)) {
        return;
      }
      const value = Math.max(0, Math.min(100, Math.round(progress * 100)));
      dragInput.value = String(value);
      dragInput.style.setProperty('--calio-flow-drag-progress', String(value));
    };

    const scheduleAuto = () => {
      clearTimer();
      if (!autoScroll || paused || widget.classList.contains('has-no-distance')) {
        return;
      }

      timeoutId = window.setTimeout(() => {
        autoTick = !autoTick;
        setActive(autoTick);
        scheduleAuto();
      }, speed + loopPause);
    };

    const recalc = () => {
      clearTimer();
      setActive(false);
      autoTick = false;

      const distance = applyOffsets();
      if (distance <= 0) {
        if (hasDragBar && !autoScroll && dragInput instanceof HTMLInputElement) {
          syncDragProgressVisual(0);
        }
        return;
      }

      if (hasDragBar && !autoScroll && dragInput instanceof HTMLInputElement) {
        syncDragProgressVisual(dragProgress);
        setDragTransform(dragProgress);
        return;
      }

      if (autoScroll) {
        timeoutId = window.setTimeout(() => {
          if (paused) {
            return;
          }
          autoTick = true;
          setActive(true);
          scheduleAuto();
        }, 120);
      }
    };

    const onMouseEnter = () => {
      if (autoScroll) {
        if (!pauseOnHover) {
          return;
        }
        paused = true;
        clearTimer();
        return;
      }

      if (hasDragBar) {
        return;
      }

      if (widget.classList.contains('has-no-distance')) {
        return;
      }
      setActive(true);
    };

    const onMouseLeave = () => {
      if (autoScroll) {
        if (!pauseOnHover) {
          return;
        }
        paused = false;
        scheduleAuto();
        return;
      }

      if (hasDragBar) {
        return;
      }

      setActive(false);
    };

    const queueRecalc = () => {
      if (resizeFrame !== null) {
        window.cancelAnimationFrame(resizeFrame);
      }
      resizeFrame = window.requestAnimationFrame(() => {
        resizeFrame = null;
        recalc();
      });
    };

    widget.addEventListener('mouseenter', onMouseEnter);
    widget.addEventListener('mouseleave', onMouseLeave);

    const onDragInput = () => {
      if (!(dragInput instanceof HTMLInputElement)) {
        return;
      }
      const rawProgress = Math.max(0, Math.min(1, toInt(dragInput.value, 0) / 100));
      dragProgress = dragReverse ? 1 - rawProgress : rawProgress;
      setActive(false);
      syncDragProgressVisual(rawProgress);
      setDragTransform(dragProgress);
    };

    if (hasDragBar && !autoScroll && dragInput instanceof HTMLInputElement) {
      dragInput.addEventListener('input', onDragInput);
      dragInput.addEventListener('change', onDragInput);
      syncDragProgressVisual(dragReverse ? 1 : 0);
      dragProgress = dragReverse ? 1 : 0;
    }

    if (typeof window.ResizeObserver === 'function') {
      resizeObserver = new window.ResizeObserver(() => queueRecalc());
      resizeObserver.observe(viewport);
      resizeObserver.observe(image);
    } else {
      windowResizeHandler = () => queueRecalc();
      window.addEventListener('resize', windowResizeHandler);
    }

    if (!image.complete) {
      image.addEventListener('load', recalc, { once: true });
    }

    recalc();

    widget.__calioImageFlowDestroy = () => {
      clearTimer();
      if (resizeFrame !== null) {
        window.cancelAnimationFrame(resizeFrame);
      }
      widget.removeEventListener('mouseenter', onMouseEnter);
      widget.removeEventListener('mouseleave', onMouseLeave);
      if (hasDragBar && !autoScroll && dragInput instanceof HTMLInputElement) {
        dragInput.removeEventListener('input', onDragInput);
        dragInput.removeEventListener('change', onDragInput);
      }
      if (resizeObserver !== null && typeof resizeObserver.disconnect === 'function') {
        resizeObserver.disconnect();
      }
      if (windowResizeHandler !== null) {
        window.removeEventListener('resize', windowResizeHandler);
      }
      widget.__calioImageFlowDestroy = null;
    };
  });
};

const boot = (scope = document) => {
  initImageFlow(scope);
};

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

let calioImageFlowHookBound = false;
const bindElementorImageFlow = () => {
  if (calioImageFlowHookBound) {
    return true;
  }

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

  window.elementorFrontend.hooks.addAction('frontend/element_ready/calio-image-flow.default', ($scope) => {
    const root = $scope?.[0] || document;
    boot(root);
  });

  calioImageFlowHookBound = true;
  return true;
};

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