import { handleError } from '$lib/utils/handle-error';

/**
 * Tracks the state of asynchronous invocations to handle race conditions and stale operations.
 * This class helps manage concurrent operations by tracking which invocations are active
 * and allowing operations to check if they're still valid.
 */
export class InvocationTracker {
  /** Counter for the number of invocations that have been started */
  invocationsStarted = 0;
  /** Counter for the number of invocations that have been completed */
  invocationsEnded = 0;

  constructor() {}

  /**
   * Starts a new invocation and returns an object with utilities to manage the invocation lifecycle.
   * @returns  An object containing methods to manage the invocation:
   *   - isInvalidInvocationError: Checks if an error is an invalid invocation error
   *   - checkStillValid: Throws an error if the invocation is no longer valid
   *   - endInvocation: Marks the invocation as complete
   */
  startInvocation() {
    this.invocationsStarted++;
    const invocation = this.invocationsStarted;

    return {
      /**
       * Throws an error if this invocation is no longer valid
       * @throws {Error} If the invocation is no longer valid
       */
      isStillValid: () => {
        if (invocation !== this.invocationsStarted) {
          return false;
        }
        return true;
      },

      /**
       * Marks this invocation as complete
       */
      endInvocation: () => {
        this.invocationsEnded = invocation;
      },
    };
  }

  /**
   * Checks if there are any active invocations
   * @returns True if there are active invocations, false otherwise
   */
  isActive() {
    return this.invocationsStarted !== this.invocationsEnded;
  }

  async invoke<T>(invocable: () => Promise<T>, localizedMessage: string) {
    const invocation = this.startInvocation();
    try {
      return await invocable();
    } catch (error: unknown) {
      handleError(error, localizedMessage);
    } finally {
      invocation.endInvocation();
    }
  }
}
