import { type InternalAxiosRequestConfig } from 'axios';

type PendingRequest = {
  controller: AbortController;
  cleanup?: () => void;
};

class RequestManager {
  private static instance: RequestManager;

  private pendingRequests: Map<AbortSignal, PendingRequest>;

  private constructor() {
    this.pendingRequests = new Map();
  }

  public static getInstance(): RequestManager {
    if (!RequestManager.instance) {
      RequestManager.instance = new RequestManager();
    }
    return RequestManager.instance;
  }

  private createLinkedSignal = (
    externalSignal?: AbortSignal,
  ): PendingRequest & {
    signal: AbortSignal;
  } => {
    const controller = new AbortController();

    if (!externalSignal) {
      return {
        controller,
        signal: controller.signal,
      };
    }

    if (externalSignal.aborted) {
      controller.abort();
      return {
        controller,
        signal: controller.signal,
      };
    }

    const abortRequest = () => {
      controller.abort();
    };

    externalSignal.addEventListener('abort', abortRequest, { once: true });

    return {
      controller,
      signal: controller.signal,
      cleanup: () => {
        externalSignal.removeEventListener('abort', abortRequest);
      },
    };
  };

  public addRequest(config: InternalAxiosRequestConfig): void {
    const externalSignal = config.signal as AbortSignal | undefined;
    const pendingRequest = this.createLinkedSignal(externalSignal);

    config.signal = pendingRequest.signal;
    this.pendingRequests.set(pendingRequest.signal, pendingRequest);
  }

  public removeRequest(config: InternalAxiosRequestConfig): void {
    if (config.signal) {
      const signal = config.signal as AbortSignal;
      this.pendingRequests.get(signal)?.cleanup?.();
      this.pendingRequests.delete(signal);
    }
  }

  public abortAllRequests(): void {
    this.pendingRequests.forEach(({ controller, cleanup }) => {
      cleanup?.();
      controller.abort();
    });
    this.pendingRequests.clear();
  }
}

export const requestManager = RequestManager.getInstance();
