import { defineTrait } from '../define.js';

const FRICTION = 0.95;
const MIN_VELOCITY = 0.5;

export const inertiaDrag = defineTrait({
  name: 'inertia-drag',
  category: 'motion-positioning',
  description: 'Momentum-based dragging, smooth deceleration',
  attributes: ['data-inertia-drag-coasting'],
  events: ['drag-end'],
  config: [],
  setup({ host }) {
    let dragging = false;
    let coasting = false;
    let rafId = null;
    let startX = 0;
    let startY = 0;
    let originX = 0;
    let originY = 0;
    let currentX = 0;
    let currentY = 0;
    let velocityX = 0;
    let velocityY = 0;
    let lastTime = 0;
    let lastClientX = 0;
    let lastClientY = 0;

    function parseTranslate() {
      // The trait writes to `style.translate` (independent of `transform` in
      // the modern CSS model). Read translate first so subsequent drags pick
      // up the current position, then fall back to the transform matrix for
      // callers that nudged via `transform`.
      const style = getComputedStyle(host);
      const t = style.translate;
      if (t && t !== 'none') {
        const parts = t.split(/\s+/).map(parseFloat);
        return { x: parts[0] || 0, y: parts[1] || 0 };
      }
      const matrix = new DOMMatrixReadOnly(style.transform);
      return { x: matrix.m41, y: matrix.m42 };
    }

    function applyPosition(x, y) {
      host.style.translate = `${x}px ${y}px`;
      currentX = x;
      currentY = y;
    }

    function decelerate() {
      velocityX *= FRICTION;
      velocityY *= FRICTION;

      if (Math.abs(velocityX) < MIN_VELOCITY && Math.abs(velocityY) < MIN_VELOCITY) {
        coasting = false;
        host.removeAttribute('data-inertia-drag-coasting');
        host.dispatchEvent(new CustomEvent('drag-end', {
          bubbles: true,
          detail: { x: currentX, y: currentY },
        }));
        return;
      }

      applyPosition(currentX + velocityX, currentY + velocityY);
      rafId = requestAnimationFrame(decelerate);
    }

    function onPointerDown(e) {
      if (host.hasAttribute('disabled')) return;

      // Stop any coasting
      if (rafId !== null) {
        cancelAnimationFrame(rafId);
        rafId = null;
      }
      coasting = false;
      host.removeAttribute('data-inertia-drag-coasting');

      dragging = true;
      startX = e.clientX;
      startY = e.clientY;
      lastClientX = e.clientX;
      lastClientY = e.clientY;
      lastTime = performance.now();
      velocityX = 0;
      velocityY = 0;

      const pos = parseTranslate();
      originX = pos.x;
      originY = pos.y;

      host.setPointerCapture(e.pointerId);
      host.style.touchAction = 'none';
    }

    function onPointerMove(e) {
      if (!dragging) return;
      const now = performance.now();
      const dt = now - lastTime;

      if (dt > 0) {
        velocityX = (e.clientX - lastClientX) / dt * 16; // normalize to ~60fps frame
        velocityY = (e.clientY - lastClientY) / dt * 16;
      }

      lastClientX = e.clientX;
      lastClientY = e.clientY;
      lastTime = now;

      const dx = e.clientX - startX;
      const dy = e.clientY - startY;
      applyPosition(originX + dx, originY + dy);
    }

    function onPointerUp() {
      if (!dragging) return;
      dragging = false;

      if (Math.abs(velocityX) > MIN_VELOCITY || Math.abs(velocityY) > MIN_VELOCITY) {
        coasting = true;
        host.setAttribute('data-inertia-drag-coasting', '');
        rafId = requestAnimationFrame(decelerate);
      } else {
        host.dispatchEvent(new CustomEvent('drag-end', {
          bubbles: true,
          detail: { x: currentX, y: currentY },
        }));
      }
    }

    host.addEventListener('pointerdown', onPointerDown);
    host.addEventListener('pointermove', onPointerMove);
    host.addEventListener('pointerup', onPointerUp);

    return () => {
      host.removeEventListener('pointerdown', onPointerDown);
      host.removeEventListener('pointermove', onPointerMove);
      host.removeEventListener('pointerup', onPointerUp);
      if (rafId !== null) cancelAnimationFrame(rafId);
      host.removeAttribute('data-inertia-drag-coasting');
      host.style.touchAction = '';
    };
  },
});
