type Subscriber = () => void;

class FrameTicker {
  private subs = new Set<Subscriber>();
  private rafId: number | null = null;

  private loop = () => {
    // Snapshot to avoid mutation during iteration
    const current = Array.from(this.subs);
    for (const fn of current) {
      try {
        fn();
      } catch (err) {
        console.warn('FrameTicker subscriber error:', err);
      }
    }
    this.rafId = this.subs.size > 0 ? requestAnimationFrame(this.loop) : null;
  };

  subscribe(fn: Subscriber) {
    this.subs.add(fn);
    if (this.rafId == null) {
      this.rafId = requestAnimationFrame(this.loop);
    }
    let active = true;
    return () => {
      if (!active) return;
      active = false;
      this.subs.delete(fn);
      if (this.subs.size === 0 && this.rafId != null) {
        cancelAnimationFrame(this.rafId);
        this.rafId = null;
      }
    };
  }
}

export const frameTicker = new FrameTicker();
