import './style.css';

const PIE_SIZE = 160;
const EPSILON = 0.0001;

const toNumber = (value, fallback) => {
  const parsed = Number.parseFloat(String(value));
  return Number.isFinite(parsed) ? parsed : fallback;
};

const clamp = (value, min, max) => Math.min(max, Math.max(min, value));

const buildPath = (percentage, radius, center) => {
  let safePercentage = clamp(percentage, 0, 100);
  if (safePercentage >= 100) {
    safePercentage = 100 - EPSILON;
  }

  const angle = (safePercentage * Math.PI * 2) / 100;
  const startX = center + radius * Math.sin(0);
  const startY = center - radius * Math.cos(0);
  const endX = center + radius * Math.sin(angle);
  const endY = center - radius * Math.cos(angle);
  const largeArc = angle > Math.PI ? 1 : 0;

  return `M${startX},${startY} A${radius},${radius} 0 ${largeArc} 1 ${endX},${endY}`;
};

const observeOnce = (target, callback) => {
  if (!(target instanceof Element) || typeof callback !== 'function') {
    return;
  }

  if (!('IntersectionObserver' in window)) {
    callback();
    return;
  }

  const observer = new IntersectionObserver(
    (entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting && entry.intersectionRatio >= 1) {
          observer.unobserve(entry.target);
          observer.disconnect();
          callback();
        }
      });
    },
    {
      root: null,
      rootMargin: '0px',
      threshold: 1,
    }
  );

  observer.observe(target);
};

const setupPie = (pie) => {
  const lineWidth = Math.max(1, Math.round(toNumber(pie.dataset.barsize, 8)));
  const center = PIE_SIZE / 2;
  const radius = Math.max(1, center - lineWidth / 2);
  const goal = clamp(Math.round(toNumber(pie.dataset.goal, 75)), 0, 100);

  const previousSvg = pie.querySelector('.calio-progress-pie-svg');
  if (previousSvg) {
    previousSvg.remove();
  }

  const svgWrap = document.createElement('div');
  svgWrap.className = 'calio-progress-pie-svg';

  const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
  svg.setAttribute('version', '1.1');
  svg.setAttribute('preserveAspectRatio', 'xMinYMin meet');
  svg.setAttribute('viewBox', `0 0 ${PIE_SIZE} ${PIE_SIZE}`);

  const track = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse');
  track.setAttribute('rx', String(radius));
  track.setAttribute('ry', String(radius));
  track.setAttribute('cx', String(center));
  track.setAttribute('cy', String(center));
  track.setAttribute('stroke-width', String(lineWidth));

  const bar = document.createElementNS('http://www.w3.org/2000/svg', 'path');
  bar.setAttribute('stroke-width', String(lineWidth));
  bar.setAttribute('d', buildPath(goal === 0 ? EPSILON : goal, radius, center));

  svg.appendChild(track);
  svg.appendChild(bar);
  svgWrap.appendChild(svg);
  pie.appendChild(svgWrap);

  const totalLength = bar.getTotalLength();
  bar.style.strokeDasharray = `${totalLength} ${totalLength}`;
  bar.style.strokeDashoffset = `${totalLength}`;

  return {
    goal,
    bar,
    totalLength,
  };
};

const updatePie = (pie, state, value) => {
  const current = clamp(value, 0, state.goal);
  const goal = state.goal;
  const ratio = goal > 0 ? current / goal : 0;
  const dashOffset = state.totalLength * (1 - ratio);

  state.bar.style.strokeDashoffset = `${dashOffset}`;
  pie.setAttribute('aria-valuenow', String(Math.round(current)));

  const numberNode = pie.querySelector('.calio-progress-pie-number');
  if (numberNode) {
    numberNode.textContent = `${Math.round(current)}%`;
  }
};

const animatePie = (pie) => {
  const state = setupPie(pie);
  const speed = Math.max(1, toNumber(pie.dataset.speed, 15));
  const startValue = clamp(Math.round(toNumber(pie.getAttribute('aria-valuenow'), 0)), 0, 100);
  const targetValue = state.goal;
  const duration = Math.max(1, Math.abs(targetValue - startValue) * speed);

  if (duration <= 1 || targetValue === startValue) {
    updatePie(pie, state, targetValue);
    return;
  }

  const easeOut = (progress) => 1 - (1 - progress) ** 3;
  const startedAt = performance.now();

  const frame = (timestamp) => {
    const elapsed = timestamp - startedAt;
    const progress = clamp(elapsed / duration, 0, 1);
    const eased = easeOut(progress);
    const current = startValue + (targetValue - startValue) * eased;

    updatePie(pie, state, current);

    if (progress < 1) {
      window.requestAnimationFrame(frame);
    }
  };

  window.requestAnimationFrame(frame);
};

const initPie = (pie, force = false) => {
  if (!(pie instanceof HTMLElement)) {
    return;
  }

  if (!force && pie.dataset.calioProgressPieInit === 'yes') {
    return;
  }

  if (force) {
    pie.removeAttribute('data-calio-progress-pie-init');
  }

  pie.dataset.calioProgressPieInit = 'yes';

  const wrapper = pie.closest('.calio-progress-pie-wrapper');
  const isEditor = Boolean(window.elementorFrontend?.isEditMode?.());

  if (isEditor) {
    animatePie(pie);
    return;
  }

  observeOnce(wrapper || pie, () => {
    animatePie(pie);
  });
};

const initScope = (scope = document, options = {}) => {
  const force = Boolean(options.force);
  const root = scope instanceof Element ? scope : document;
  root.querySelectorAll('.calio-progress-pie').forEach((pie) => {
    initPie(pie, force);
  });
};

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

let calioProgressPieHookBound = false;
const bindElementorProgressPie = () => {
  if (calioProgressPieHookBound) {
    return true;
  }

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

  window.elementorFrontend.hooks.addAction(
    'frontend/element_ready/calio-progress-pie.default',
    ($scope) => {
      const root = $scope?.[0] || null;
      if (root) {
        initScope(root, { force: true });
      }
    }
  );

  calioProgressPieHookBound = true;
  return true;
};

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