import { act, fireEvent, render } from '@testing-library/react';
import React, { useRef, useState } from 'react';
import { describe, expect, it } from 'vitest';
import { useClickOutside } from './useClickOutside';

const TestWrapper: React.FC<{ enabled?: boolean }> = ({ enabled = true }) => {
  const [clickedOutside, setClickedOutside] = useState(false);
  const ref1 = useRef<HTMLDivElement>(null);
  const ref2 = useRef<HTMLDivElement>(null);

  useClickOutside([ref1, ref2], () => setClickedOutside(true), enabled);

  return (
    <div>
      <div ref={ref1} className="inside-1">
        Inside 1
      </div>
      <div ref={ref2} className="inside-2">
        Inside 2
      </div>
      <div className="outside">Outside</div>
      <p className="status">
        {clickedOutside ? 'clicked-outside' : 'not-clicked'}
      </p>
    </div>
  );
};

describe('useClickOutside', () => {
  it('should call handler when clicking outside the referenced elements', () => {
    const { getByText } = render(<TestWrapper />);
    expect(getByText('not-clicked')).toBeInTheDocument();

    act(() => {
      fireEvent.mouseDown(getByText('Outside'));
    });

    expect(getByText('clicked-outside')).toBeInTheDocument();
  });

  it('should not call handler when clicking inside the first referenced element', () => {
    const { getByText } = render(<TestWrapper />);

    act(() => {
      fireEvent.mouseDown(getByText('Inside 1'));
    });

    expect(getByText('not-clicked')).toBeInTheDocument();
  });

  it('should not call handler when clicking inside the second referenced element', () => {
    const { getByText } = render(<TestWrapper />);

    act(() => {
      fireEvent.mouseDown(getByText('Inside 2'));
    });

    expect(getByText('not-clicked')).toBeInTheDocument();
  });

  it('should not call handler when disabled', () => {
    const { getByText } = render(<TestWrapper enabled={false} />);

    act(() => {
      fireEvent.mouseDown(getByText('Outside'));
    });

    expect(getByText('not-clicked')).toBeInTheDocument();
  });

  it('should handle touch events', () => {
    const { getByText } = render(<TestWrapper />);
    expect(getByText('not-clicked')).toBeInTheDocument();

    const outside = getByText('Outside');

    act(() => {
      const event = new TouchEvent('touchstart', {
        bubbles: true,
        cancelable: true,
      });
      Object.defineProperty(event, 'target', {
        writable: false,
        value: outside,
      });
      document.dispatchEvent(event);
    });

    expect(getByText('clicked-outside')).toBeInTheDocument();
  });
});
