import { renderHook, act, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import { useExpanderState, shouldShowWhenExpanded, ExpanderToggle } from './Expander';
import { mockMatchMedia, render } from '../../../test-utils';

mockMatchMedia();

describe('useExpanderState', () => {
  it('initializes with default expanded state (true)', () => {
    const { result } = renderHook(() => useExpanderState());

    expect(result.current.expanded).toBe(true);
  });

  it('initializes with custom expanded state', () => {
    const { result } = renderHook(() => useExpanderState(false));

    expect(result.current.expanded).toBe(false);
  });

  it('toggles expanded state', () => {
    const { result } = renderHook(() => useExpanderState(true));

    expect(result.current.expanded).toBe(true);

    act(() => {
      result.current.toggle();
    });

    expect(result.current.expanded).toBe(false);

    act(() => {
      result.current.toggle();
    });

    expect(result.current.expanded).toBe(true);
  });

  it('sets expanded state programmatically', () => {
    const { result } = renderHook(() => useExpanderState(true));

    act(() => {
      result.current.setExpanded(false);
    });

    expect(result.current.expanded).toBe(false);

    act(() => {
      result.current.setExpanded(true);
    });

    expect(result.current.expanded).toBe(true);
  });

  it('memoizes toggle callback', () => {
    const { result, rerender } = renderHook(() => useExpanderState(true));

    const firstToggle = result.current.toggle;

    rerender();

    expect(result.current.toggle).toBe(firstToggle);
  });
});

describe('shouldShowWhenExpanded', () => {
  it('returns false when hasContent is false', () => {
    expect(shouldShowWhenExpanded({ expanded: true, hasContent: false })).toBe(false);
    expect(shouldShowWhenExpanded({ expanded: false, hasContent: false })).toBe(false);
  });

  it('returns true when expanded and hasContent are true', () => {
    expect(shouldShowWhenExpanded({ expanded: true, hasContent: true })).toBe(true);
  });

  it('returns false when collapsed but hasContent is true', () => {
    expect(shouldShowWhenExpanded({ expanded: false, hasContent: true })).toBe(false);
  });

  it('returns true when alwaysShow is true and hasContent is true, regardless of expanded', () => {
    expect(shouldShowWhenExpanded({ expanded: true, hasContent: true, alwaysShow: true })).toBe(
      true,
    );
    expect(shouldShowWhenExpanded({ expanded: false, hasContent: true, alwaysShow: true })).toBe(
      true,
    );
  });

  it('returns false when alwaysShow is true but hasContent is false', () => {
    expect(shouldShowWhenExpanded({ expanded: true, hasContent: false, alwaysShow: true })).toBe(
      false,
    );
    expect(shouldShowWhenExpanded({ expanded: false, hasContent: false, alwaysShow: true })).toBe(
      false,
    );
  });
});

describe('ExpanderToggle', () => {
  it('renders with correct aria-label when expanded', () => {
    render(<ExpanderToggle expanded onToggle={jest.fn()} />);

    expect(screen.getByRole('button')).toHaveAttribute('aria-label', 'Collapse');
  });

  it('renders with correct aria-label when collapsed', () => {
    render(<ExpanderToggle expanded={false} onToggle={jest.fn()} />);

    expect(screen.getByRole('button')).toHaveAttribute('aria-label', 'Expand');
  });

  it('renders with custom labels', () => {
    const { rerender } = render(<ExpanderToggle expanded onToggle={jest.fn()} />);

    expect(screen.getByRole('button')).toHaveAttribute('aria-label', 'Collapse');

    rerender(<ExpanderToggle expanded={false} onToggle={jest.fn()} />);

    expect(screen.getByRole('button')).toHaveAttribute('aria-label', 'Expand');
  });

  it('sets aria-expanded attribute correctly', () => {
    const { rerender } = render(<ExpanderToggle expanded onToggle={jest.fn()} />);

    expect(screen.getByRole('button')).toHaveAttribute('aria-expanded', 'true');

    rerender(<ExpanderToggle expanded={false} onToggle={jest.fn()} />);

    expect(screen.getByRole('button')).toHaveAttribute('aria-expanded', 'false');
  });

  it('calls onToggle when clicked', async () => {
    const user = userEvent.setup();
    const onToggle = jest.fn();

    render(<ExpanderToggle expanded onToggle={onToggle} />);

    await user.click(screen.getByRole('button'));

    expect(onToggle).toHaveBeenCalledTimes(1);
  });

  it('applies custom className', () => {
    render(<ExpanderToggle expanded className="custom-class" onToggle={jest.fn()} />);

    expect(screen.getByRole('button')).toHaveClass('custom-class');
  });

  it('applies base class and collapsed class correctly', () => {
    const { rerender } = render(<ExpanderToggle expanded onToggle={jest.fn()} />);

    expect(screen.getByRole('button')).toHaveClass('wds-expander-toggle');
    expect(screen.getByRole('button')).not.toHaveClass('wds-expander-toggle--collapsed');

    rerender(<ExpanderToggle expanded={false} onToggle={jest.fn()} />);

    expect(screen.getByRole('button')).toHaveClass('wds-expander-toggle');
    expect(screen.getByRole('button')).toHaveClass('wds-expander-toggle--collapsed');
  });

  it('applies data-testid attribute', () => {
    render(<ExpanderToggle expanded data-testid="expander-toggle" onToggle={jest.fn()} />);

    expect(screen.getByTestId('expander-toggle')).toBeInTheDocument();
  });
});
