import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
import { render } from '@testing-library/svelte';
import InfiniteScrollSentinel from './InfiniteScrollSentinel.svelte';

// jsdom doesn't ship IntersectionObserver. We install a minimal mock that
// captures the registered callback so tests can drive intersection events
// synchronously.
type IOInstance = {
  observe: ReturnType<typeof vi.fn>;
  disconnect: ReturnType<typeof vi.fn>;
  fire: (isIntersecting: boolean) => void;
};
const created: IOInstance[] = [];

beforeEach(() => {
  created.length = 0;
  (globalThis as unknown as { IntersectionObserver: unknown }).IntersectionObserver =
    class MockIO {
      private cb: IntersectionObserverCallback;
      observe = vi.fn();
      disconnect = vi.fn();
      constructor(cb: IntersectionObserverCallback) {
        this.cb = cb;
        created.push({
          observe: this.observe,
          disconnect: this.disconnect,
          fire: (isIntersecting: boolean) => {
            this.cb(
              [{ isIntersecting } as unknown as IntersectionObserverEntry],
              this as unknown as IntersectionObserver
            );
          }
        });
      }
    };
});

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

describe('InfiniteScrollSentinel', () => {
  test('observes the sentinel when enabled', () => {
    const onIntersect = vi.fn();
    render(InfiniteScrollSentinel, { props: { onIntersect, enabled: true } });
    expect(created).toHaveLength(1);
    expect(created[0].observe).toHaveBeenCalledTimes(1);
  });

  test('does not create an observer when enabled is false', () => {
    const onIntersect = vi.fn();
    render(InfiniteScrollSentinel, { props: { onIntersect, enabled: false } });
    expect(created).toHaveLength(0);
  });

  test('fires onIntersect when the sentinel enters view', () => {
    const onIntersect = vi.fn();
    render(InfiniteScrollSentinel, { props: { onIntersect, enabled: true } });
    created[0].fire(true);
    expect(onIntersect).toHaveBeenCalledTimes(1);
  });

  test('does not fire onIntersect when the entry is not intersecting', () => {
    const onIntersect = vi.fn();
    render(InfiniteScrollSentinel, { props: { onIntersect, enabled: true } });
    created[0].fire(false);
    expect(onIntersect).not.toHaveBeenCalled();
  });

  test('disconnects the observer when the component unmounts', () => {
    const onIntersect = vi.fn();
    const { unmount } = render(InfiniteScrollSentinel, {
      props: { onIntersect, enabled: true }
    });
    unmount();
    expect(created[0].disconnect).toHaveBeenCalledTimes(1);
  });
});
