import { Observable } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

/**
 * Creates an Observable that emits when an AbortSignal is aborted.
 *
 * @param signal - The AbortSignal instance to listen to
 * @returns An Observable that emits and completes when the signal is aborted
 */
export const fromAbortSignal = (signal: AbortSignal): Observable<void> => {
  // If the signal is already aborted, return an Observable that immediately completes
  if (signal.aborted) {
    return new Observable<void>((subscriber) => {
      subscriber.complete();
    });
  }

  // Otherwise, create an Observable from the abort event
  return new Observable<void>((subscriber) => {
    const abortHandler = () => {
      subscriber.next();
      subscriber.complete();
    };

    // Listen for the 'abort' event
    signal.addEventListener('abort', abortHandler);

    // Clean up the event listener when the Observable is unsubscribed
    return () => {
      signal.removeEventListener('abort', abortHandler);
    };
  });
};

/**
 * Operator that completes the source Observable when the provided AbortSignal is aborted.
 * Similar to takeUntil, but works with AbortSignal instead of an Observable.
 *
 * @param signal - The AbortSignal instance that will trigger completion when aborted
 * @returns An Observable that completes when the source completes or when the signal is aborted
 */
export const takeUntilAborted =
  <T>(signal: AbortSignal) =>
  (source: Observable<T>): Observable<T> => {
    // Convert the AbortSignal to an Observable
    const aborted$ = fromAbortSignal(signal);

    // Use the standard takeUntil operator with our abort Observable
    return source.pipe(takeUntil(aborted$));
  };
