import { engineManager } from './EngineManager';

export interface DisposableRuntimeResource {
  dispose(): void;
}

type DeferredCallback = (callback: () => void) => void;

/**
 * Reference-counts an application-wide runtime resource. Final disposal is
 * deferred by one microtask so React StrictMode's development cleanup/remount
 * cycle can reacquire the same singleton without terminating a healthy engine.
 */
export class DeferredResourceLifecycle {
  readonly #resource: DisposableRuntimeResource;
  readonly #defer: DeferredCallback;
  #leaseCount = 0;
  #generation = 0;

  constructor(
    resource: DisposableRuntimeResource,
    defer: DeferredCallback = globalThis.queueMicrotask.bind(globalThis)
  ) {
    this.#resource = resource;
    this.#defer = defer;
  }

  get leaseCount(): number {
    return this.#leaseCount;
  }

  acquire(): () => void {
    this.#leaseCount += 1;
    this.#generation += 1;
    let active = true;

    return () => {
      if (!active) return;
      active = false;
      this.#leaseCount -= 1;
      const releaseGeneration = ++this.#generation;
      if (this.#leaseCount !== 0) return;

      this.#defer(() => {
        if (this.#leaseCount === 0 && this.#generation === releaseGeneration) {
          this.#resource.dispose();
        }
      });
    };
  }
}

export const engineManagerLifecycle = new DeferredResourceLifecycle(
  engineManager
);
