import {
  defer,
  fromEvent,
  map,
  Observable,
  share,
  startWith
} from 'rxjs';

/**
 * `document.visibilityState === 'visible'` as an observable.
 *
 * Emits on every `visibilitychange` event, starting with the current
 * value. `share()` is applied so multiple subscribers reuse the single
 * underlying DOM listener.
 */
export const documentVisible$ = ((): Observable<boolean> => {
  if (typeof document === 'undefined') {
    return defer(() => new Observable<boolean>((subscriber) => subscriber.next(true)));
  }

  return fromEvent(document, 'visibilitychange').pipe(
    startWith(null),
    map(() => document.visibilityState === 'visible'),
    share()
  );
})();

/**
 * Builds an interval-like observable that only ticks while the document
 * is visible. Useful for replacing `setInterval` callbacks that should
 * pause when the window is hidden (heartbeat, polling).
 */
export function visibilityAwareInterval$(periodMs: number): Observable<number> {
  return new Observable<number>((subscriber) => {
    let counter = 0;
    let handle: ReturnType<typeof setInterval> | null = null;

    const start = (): void => {
      if (handle !== null) {
        return;
      }

      handle = setInterval(() => subscriber.next(counter++), periodMs);
    };
    const stop = (): void => {
      if (handle === null) {
        return;
      }

      clearInterval(handle);
      handle = null;
    };
    const handleVisibility = (): void => {
      if (typeof document === 'undefined' || document.visibilityState === 'visible') {
        start();
      } else {
        stop();
      }
    };

    handleVisibility();
    document.addEventListener('visibilitychange', handleVisibility);

    return () => {
      stop();
      document.removeEventListener('visibilitychange', handleVisibility);
    };
  });
}
