import * as React from 'react';
import {
  cleanup,
  render,
  screen,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import {
  vi,
  expect,
  describe,
  it,
  afterEach,
} from 'vitest';
import matchers from '@testing-library/jest-dom/matchers';
import {
  ToggleSwitch,
  ToggleSwitchDerivedElement,
} from '.';

expect.extend(matchers);

describe('ToggleSwitch', () => {
  afterEach(() => {
    cleanup();
  });

  it('renders a checkbox', () => {
    render(
      <ToggleSwitch />,
    );

    const checkbox = screen.getByRole('checkbox');
    expect(checkbox).toBeInTheDocument();
  });

  it('renders a block switch', () => {
    render(
      <ToggleSwitch
        block
      />,
    );

    const base = screen.getByTestId('base');
    expect(base).toHaveClass('flex');
  });

  it('renders a label when the component is unchecked', () => {
    render(
      <ToggleSwitch
        uncheckedLabel="label"
      />,
    );

    const uncheckedLabel = screen.getByTestId('uncheckedLabel');
    expect(uncheckedLabel).toBeInTheDocument();
  });

  describe('on subtext', () => {
    it('renders without an unchecked label', () => {
      render(
        <ToggleSwitch
          subtext="subtext"
        />,
      );

      const subtext = screen.getByTestId('subtext');
      expect(subtext).toBeInTheDocument();
      expect(subtext).toHaveClass('pl-16');
    });

    it('renders with an unchecked label', () => {
      render(
        <ToggleSwitch
          subtext="subtext"
          uncheckedLabel="label"
        />,
      );

      const subtext = screen.getByTestId('subtext');
      expect(subtext).toBeInTheDocument();
      expect(subtext).toHaveClass('pt-2');
    });
  });

  describe('on indeterminate', () => {
    it('renders an indeterminate checkbox', () => {
      render(
        <ToggleSwitch
          indeterminate
        />,
      );
      const checkbox = screen.getByRole('checkbox');
      expect(checkbox).toHaveProperty('indeterminate', true);
    });

    it('acknowledges passed ref object', () => {
      const ref = React.createRef<ToggleSwitchDerivedElement>();
      render(
        <ToggleSwitch
          indeterminate
          ref={ref}
        />,
      );
      expect(ref.current).toHaveProperty('indeterminate', true);
    });

    it('acknowledges passed legacy ref', () => {
      let refElement = null as null | ToggleSwitchDerivedElement;
      const ref = (element: ToggleSwitchDerivedElement) => {
        refElement = element;
      };

      render(
        <ToggleSwitch
          indeterminate
          ref={ref}
        />,
      );

      const checkbox = screen.getByRole('checkbox');
      expect(checkbox).toHaveProperty('indeterminate', true);
      expect(refElement).toBe(checkbox);
    });
  });

  it('handles click events', async () => {
    const onClick = vi.fn().mockImplementationOnce(
      (e: React.MouseEvent<ToggleSwitchDerivedElement>) => {
        e.preventDefault();
      },
    );
    render(
      <ToggleSwitch
        onClick={onClick}
      />,
    );
    const checkbox: HTMLInputElement = screen.getByRole('checkbox');
    await userEvent.click(checkbox);
    expect(onClick).toBeCalled();
  });

  it('handles change events', async () => {
    const onChange = vi.fn().mockImplementationOnce(
      (e: React.ChangeEvent<ToggleSwitchDerivedElement>) => {
        e.preventDefault();
      },
    );
    render(
      <ToggleSwitch
        onChange={onChange}
      />,
    );
    const checkbox: HTMLInputElement = screen.getByRole('checkbox');
    await userEvent.click(checkbox);
    expect(onChange).toBeCalled();
  });
});
