import { afterEach, describe, expect, it, vi } from 'vitest';

import { playMedia } from '@/utils/audioPlayback';
import { logger } from '@/utils/logger';

function mediaElement(playResult: Promise<void> | undefined): HTMLMediaElement {
  return { play: vi.fn(() => playResult) } as unknown as HTMLMediaElement;
}

function abortError(): DOMException {
  // What Chrome rejects with when pause() lands before play() has resolved.
  return new DOMException('The play() request was interrupted by a call to pause().', 'AbortError');
}

afterEach(() => {
  vi.restoreAllMocks();
});

describe('playMedia', () => {
  it('swallows the AbortError raised when playback is interrupted', async () => {
    const errorSpy = vi.spyOn(logger, 'error').mockImplementation(() => {});
    const element = mediaElement(Promise.reject(abortError()));

    expect(() => playMedia(element)).not.toThrow();
    await vi.waitFor(() => expect(element.play).toHaveBeenCalled());

    expect(errorSpy).not.toHaveBeenCalled();
  });

  it('reports a blocked autoplay instead of hiding it', async () => {
    const errorSpy = vi.spyOn(logger, 'error').mockImplementation(() => {});
    const blocked = new DOMException('play() failed because the user agent...', 'NotAllowedError');
    playMedia(mediaElement(Promise.reject(blocked)));

    await vi.waitFor(() =>
      expect(errorSpy).toHaveBeenCalledWith('Media playback failed:', blocked)
    );
  });

  it('tolerates a play() that returns no promise', () => {
    expect(() => playMedia(mediaElement(undefined))).not.toThrow();
  });

  it('does nothing without an element', () => {
    expect(() => playMedia(null)).not.toThrow();
  });
});
