import { defineTrait } from '../define.js';
import { prefersReducedMotion } from '../motion.js';
import { getStage, viewportCenterOf, spawnParticle, SHARED_COLORS } from '../confetti/confetti-stage.js';

/**
 * `confetti-burst` — upward fountain on connect + on every `press`.
 *
 * Each burst pushes ~80 particles into the singleton body-level
 * `<div popover="manual" id="adia-confetti-stage">`. Particles are
 * anchored to the host's viewport position at fire-time via
 * `getBoundingClientRect()` — they escape `overflow: hidden`
 * ancestors and render above modals, drawers, and any z-stacking
 * context (the Popover API's top-layer promotion).
 *
 * The trait listens for `press` (button-ui's normalized click event)
 * so declarative usage — `<button-ui traits="confetti-burst">` —
 * fires a fresh burst on each click. Programmatic usage that calls
 * `confettiBurst().connect(el)` per-click gets its single burst at
 * connect-time, matching the existing animation-page contract.
 */
const BURST_DURATION_MS = 1500;

export const confettiBurst = defineTrait({
  name: 'confetti-burst',
  category: 'interaction-delight',
  description: 'Upward fountain particle burst — fires on each `press` event',
  attributes: ['data-confetti-burst-active'],
  events: ['confetti-burst-done'],
  config: [],
  setup({ host }) {
    const inflight = new Set();
    const burstTimers = new Set();

    function reducedMotionBurst() {
      host.setAttribute('data-confetti-burst-active', '');
      queueMicrotask(() => {
        host.removeAttribute('data-confetti-burst-active');
        host.dispatchEvent(new CustomEvent('confetti-burst-done', { bubbles: true }));
      });
    }

    function fireBurst() {
      if (prefersReducedMotion()) {
        reducedMotionBurst();
        return;
      }

      const stage = getStage();
      const center = viewportCenterOf(host);
      host.setAttribute('data-confetti-burst-active', '');

      // Detached host (rect = 0,0,0,0): mark active, fire done on
      // next tick. The original implementation's no-canvas path did
      // the same shape — graceful degrade, never throw.
      if (!center) {
        queueMicrotask(() => {
          host.removeAttribute('data-confetti-burst-active');
          host.dispatchEvent(new CustomEvent('confetti-burst-done', { bubbles: true }));
        });
        return;
      }

      const burstId = Symbol('burst');
      inflight.add(burstId);

      // Spawn ~80 fountain particles. Each gets a random vector
      // weighted upward (vy < 0), plus gravity baked into a steeper
      // dy so the trajectory looks parabolic. The keyframes do the
      // motion; we set dx/dy/rot via custom properties.
      for (let i = 0; i < 80; i++) {
        const angle = -Math.PI / 2 + (Math.random() - 0.5) * Math.PI * 0.9;
        const speed = 80 + Math.random() * 200;
        const dx = Math.cos(angle) * speed;
        // Gravity: launch up, fall back. dy is the final offset after
        // the keyframe — start up by `speed`, end below by gravity.
        const dy = Math.sin(angle) * speed + 280;
        const rot = (Math.random() - 0.5) * 720;
        const size = Math.random() * 4 + 2;
        const color = SHARED_COLORS[Math.floor(Math.random() * SHARED_COLORS.length)];
        const dur = 1200 + Math.random() * 300;

        const p = spawnParticle(stage, center.x, center.y, { size, color });
        p.style.setProperty('--dx', `${dx}px`);
        p.style.setProperty('--dy', `${dy}px`);
        p.style.setProperty('--rot', `${rot}deg`);
        p.style.animation = `adia-confetti-fountain ${dur}ms ease-out forwards`;
      }

      // Fire `confetti-burst-done` once the burst's lifetime elapses.
      // We use a single timeout per burst (vs listening for the last
      // particle's animationend) — robust to detached/early-removed
      // particles, and matches the original 2s tear-down semantic.
      const timer = setTimeout(() => {
        inflight.delete(burstId);
        if (inflight.size === 0) host.removeAttribute('data-confetti-burst-active');
        host.dispatchEvent(new CustomEvent('confetti-burst-done', { bubbles: true }));
      }, BURST_DURATION_MS);

      // Track the timer so disconnect can cancel.
      burstTimers.add(timer);
    }

    // Fire one burst at connect-time. Preserves the existing
    // animation-page semantic (`confettiBurst().connect(el)` on every
    // click of a separate trigger button).
    fireBurst();

    // Listen for `press` so the declarative case
    // (`<button-ui traits="confetti-burst">`) fires a fresh burst on
    // each click. button-ui dispatches `press` from its #onClick.
    const onPress = () => fireBurst();
    host.addEventListener('press', onPress);

    return () => {
      host.removeEventListener('press', onPress);
      for (const t of burstTimers) clearTimeout(t);
      burstTimers.clear();
      inflight.clear();
      host.removeAttribute('data-confetti-burst-active');
    };
  },
});
