/**
 * Tests for the useClickOutside hook
 * @jest-environment jsdom
 */

import { renderHook } from '@testing-library/react';
import { useClickOutside } from '@/hooks/useClickOutside';
import { createRef } from 'react';

describe('useClickOutside', () => {
  let callback: jest.Mock;
  let ref: React.RefObject<HTMLDivElement>;
  let element: HTMLDivElement;

  beforeEach(() => {
    callback = jest.fn();
    element = document.createElement('div');
    document.body.appendChild(element);
    ref = { current: element };
  });

  afterEach(() => {
    document.body.removeChild(element);
    jest.clearAllMocks();
  });

  it('should call callback when clicking outside the element', () => {
    renderHook(() => useClickOutside(ref, callback, true));

    const outsideElement = document.createElement('div');
    document.body.appendChild(outsideElement);

    const event = new MouseEvent('mousedown', { bubbles: true });
    outsideElement.dispatchEvent(event);

    expect(callback).toHaveBeenCalledTimes(1);

    document.body.removeChild(outsideElement);
  });

  it('should not call callback when clicking inside the element', () => {
    renderHook(() => useClickOutside(ref, callback, true));

    const event = new MouseEvent('mousedown', { bubbles: true });
    element.dispatchEvent(event);

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

  it('should not call callback when disabled', () => {
    renderHook(() => useClickOutside(ref, callback, false));

    const outsideElement = document.createElement('div');
    document.body.appendChild(outsideElement);

    const event = new MouseEvent('mousedown', { bubbles: true });
    outsideElement.dispatchEvent(event);

    expect(callback).not.toHaveBeenCalled();

    document.body.removeChild(outsideElement);
  });

  it('should handle null ref gracefully', () => {
    const nullRef = createRef<HTMLDivElement>();

    // Should not throw when ref is null
    expect(() => {
      renderHook(() => useClickOutside(nullRef, callback, true));

      const event = new MouseEvent('mousedown', { bubbles: true });
      document.body.dispatchEvent(event);
    }).not.toThrow();

    // Callback should NOT be called when ref is null (ref.current is falsy)
    expect(callback).not.toHaveBeenCalled();
  });

  it('should remove event listener on unmount', () => {
    const removeEventListenerSpy = jest.spyOn(document, 'removeEventListener');

    const { unmount } = renderHook(() => useClickOutside(ref, callback, true));
    unmount();

    expect(removeEventListenerSpy).toHaveBeenCalledWith('mousedown', expect.any(Function));

    removeEventListenerSpy.mockRestore();
  });

  it('should use default enabled value of true', () => {
    renderHook(() => useClickOutside(ref, callback));

    const outsideElement = document.createElement('div');
    document.body.appendChild(outsideElement);

    const event = new MouseEvent('mousedown', { bubbles: true });
    outsideElement.dispatchEvent(event);

    expect(callback).toHaveBeenCalledTimes(1);

    document.body.removeChild(outsideElement);
  });
});
