import { cleanup, fireEvent, render } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { useState } from 'react';

import { useRevealOnExpand } from './useRevealOnExpand';

function GroupList() {
  const { setContainer, registerGroup, onToggle } = useRevealOnExpand();
  const [expanded, setExpanded] = useState(false);
  return (
    <div ref={setContainer} data-testid="container">
      <div ref={registerGroup('g1')} data-testid="group">
        <button
          data-testid="toggle"
          onClick={() => {
            const willExpand = !expanded;
            setExpanded(willExpand);
            onToggle('g1', willExpand);
          }}
        >
          toggle
        </button>
        {expanded && <div style={{ height: 1000 }} />}
      </div>
      <div style={{ height: 2000 }} />
    </div>
  );
}

describe('useRevealOnExpand', () => {
  const original = {
    resizeObserver: globalThis.ResizeObserver,
    scrollTo: HTMLElement.prototype.scrollTo,
    getBoundingClientRect: HTMLElement.prototype.getBoundingClientRect,
  };

  beforeEach(() => {
    globalThis.ResizeObserver = class {
      observe() {}
      unobserve() {}
      disconnect() {}
    } as unknown as typeof ResizeObserver;
    HTMLElement.prototype.scrollTo = vi.fn();
    // The opened group's bottom (800) sits below the container's bottom (500),
    // so it needs a 300px downward scroll to come fully into view.
    HTMLElement.prototype.getBoundingClientRect = function (this: HTMLElement) {
      const id = this.dataset?.testid;
      const bottom = id === 'group' ? 800 : id === 'container' ? 500 : 0;
      return { bottom } as DOMRect;
    };
  });

  afterEach(() => {
    cleanup();
    globalThis.ResizeObserver = original.resizeObserver;
    HTMLElement.prototype.scrollTo = original.scrollTo;
    HTMLElement.prototype.getBoundingClientRect = original.getBoundingClientRect;
    vi.restoreAllMocks();
  });

  it('scrolls a freshly expanded group into view', () => {
    const view = render(<GroupList />);
    const scrollTo = view.getByTestId('container').scrollTo as ReturnType<typeof vi.fn>;

    fireEvent.click(view.getByTestId('toggle'));

    expect(scrollTo).toHaveBeenCalledWith({ top: 300 });
  });

  it('does not scroll when a group is collapsed', () => {
    const view = render(<GroupList />);
    const scrollTo = view.getByTestId('container').scrollTo as ReturnType<typeof vi.fn>;

    fireEvent.click(view.getByTestId('toggle')); // expand
    scrollTo.mockClear();
    fireEvent.click(view.getByTestId('toggle')); // collapse

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