/* eslint-disable @typescript-eslint/ban-ts-comment */
import { fireEvent, render, waitFor } from '@testing-library/react';
import React from 'react';
import { act } from 'react-dom/test-utils';
import { describe, expect, it, vi } from 'vitest';
import { Checkbox, CheckboxProps } from './Checkbox';

describe('Checkbox', () => {
  it('renders the component without crashing', () => {
    const { container } = render(<Checkbox name="test-name" />);

    expect(container).toBeTruthy();
  });

  it('displays a label', () => {
    const mockLabel = 'mockLabel';
    const { container } = render(
      <Checkbox name="test-name" label={mockLabel} />,
    );

    const label = container.querySelector('label');

    expect(label?.textContent).toBe(mockLabel);
  });

  it('uses optional props when passed in', () => {
    const mockProps: Partial<CheckboxProps> = {
      autoFocus: true,
      disabled: true,
      id: 'test-id',
      name: 'test-name',
      onBlur: () => null,
      onFocus: () => null,
    };

    const { container } = render(<Checkbox name="test-name" {...mockProps} />);

    const checkbox = container.querySelector('input') as HTMLInputElement;

    // React's autoFocus focuses the element programmatically
    // expect(document.activeElement).toBe(checkbox);
    expect(checkbox.disabled).toBe(mockProps.disabled);
    expect(checkbox.id).toBe(mockProps.id);
    expect(checkbox.name).toBe(mockProps.name);
  });

  it('sets checkbox field using the value prop and emits updated values', () => {
    const spy = vi.fn();
    const mockValue = true;
    const mockValueUpdated = false;
    const { container } = render(
      <Checkbox name="test-name" value={mockValue} onChange={spy} />,
    );

    const checkbox = container.querySelector('input') as HTMLInputElement;

    expect(checkbox.checked).toEqual(mockValue);

    fireEvent.click(checkbox);

    expect(spy).toHaveBeenCalledTimes(1);
    expect(spy).toHaveBeenCalledWith(mockValueUpdated);
  });

  it('raises change, blur, and focus events', () => {
    const changeSpy = vi.fn();
    const blurSpy = vi.fn();
    const focusSpy = vi.fn();
    const { container } = render(
      <Checkbox
        name={'test-name'}
        onChange={changeSpy}
        onBlur={blurSpy}
        onFocus={focusSpy}
      />,
    );

    const checkbox = container.querySelector('input') as HTMLInputElement;

    fireEvent.click(checkbox);
    expect(changeSpy).toHaveBeenCalledTimes(1);

    fireEvent.blur(checkbox);
    expect(blurSpy).toHaveBeenCalledTimes(1);

    fireEvent.focus(checkbox);
    expect(focusSpy).toHaveBeenCalledTimes(1);
  });

  it('applies error styling and renders error message when an error is passed', () => {
    const mockErrorMessage = 'test-error-message';
    const { container } = render(
      <Checkbox name="test-name" error={mockErrorMessage} />,
    );

    const errorMsg = container.querySelector('small');
    const errorStyling = container.querySelector('input');

    expect(errorMsg?.textContent).toBe(mockErrorMessage);
    expect(errorStyling?.classList.contains('hasError')).toBe(true);
  });

  describe('confirmation', () => {
    it(`'Advanced' mode changes applies confirmation styles`, () => {
      const { container } = render(
        <Checkbox name="test-name" confirmationMode="Advanced" />,
      );

      let checkbox = container.querySelector('input') as HTMLInputElement;

      // Initially no confirmation styling
      expect(checkbox.className).not.toContain('hasConfirm');

      act(() => {
        fireEvent.click(checkbox);
      });

      checkbox = container.querySelector('input') as HTMLInputElement;

      // After clicking, confirmation styling should be applied
      expect(checkbox.className).toContain('hasConfirm');
    });

    it(`When input is not checked, 'Advanced' mode renders a confirmation pop up, raises the 'onChange' with 'true' after the 'Confirm' button is clicked, and finally dismisses the confirmation pop up`, async () => {
      const spy = vi.fn();

      const { container } = render(
        <Checkbox
          name="test-name"
          confirmationMode="Advanced"
          onChange={spy}
        />,
      );

      let checkbox = container.querySelector('input') as HTMLInputElement;
      let confirmation = document.querySelector('.confirm-dialog-container');

      expect(confirmation).not.toBeInTheDocument();

      act(() => {
        fireEvent.click(checkbox);
      });

      await waitFor(() => {
        const conf = document.querySelector('.confirm-dialog-container');
        expect(conf).toBeInTheDocument();
      });

      confirmation = document.querySelector('.confirm-dialog-container');

      const confirmButton = document.querySelectorAll('button')[1];

      act(() => {
        fireEvent.click(confirmButton);
      });

      await waitFor(() => {
        expect(spy).toHaveBeenCalledTimes(1);
      });

      checkbox = container.querySelector('input') as HTMLInputElement;
      confirmation = document.querySelector('.confirm-dialog-container');

      expect(checkbox.className).not.toContain('hasConfirm');
      expect(confirmation).not.toBeInTheDocument();
      expect(spy).toHaveBeenCalledWith(true);
    });

    it(`When input is checked, 'Advanced' mode renders a confirmation pop up, raises the 'onChange' with 'false' after the 'Confirm' button is clicked, and finally dismisses the confirmation pop up`, async () => {
      const spy = vi.fn();

      const { container } = render(
        <Checkbox
          name="test-name"
          confirmationMode="Advanced"
          onChange={spy}
          value={true}
        />,
      );

      let checkbox = container.querySelector('input') as HTMLInputElement;
      let confirmation = document.querySelector('.confirm-dialog-container');

      expect(confirmation).not.toBeInTheDocument();

      act(() => {
        fireEvent.click(checkbox);
      });

      await waitFor(() => {
        const conf = document.querySelector('.confirm-dialog-container');
        expect(conf).toBeInTheDocument();
      });

      confirmation = document.querySelector('.confirm-dialog-container');

      const confirmButton = document.querySelectorAll('button')[1];

      act(() => {
        fireEvent.click(confirmButton);
      });

      await waitFor(() => {
        expect(spy).toHaveBeenCalledTimes(1);
      });

      checkbox = container.querySelector('input') as HTMLInputElement;
      confirmation = document.querySelector('.confirm-dialog-container');

      expect(checkbox.className).not.toContain('hasConfirm');
      expect(confirmation).not.toBeInTheDocument();
      expect(spy).toHaveBeenCalledWith(false);
    });

    it(`'Advanced' mode renders a confirmation pop up, does not raise the 'onChange' event after the 'Cancel' button is clicked, and finally dismisses the confirmation pop up`, async () => {
      const spy = vi.fn();

      const { container } = render(
        <Checkbox
          name="test-name"
          confirmationMode="Advanced"
          onChange={spy}
        />,
      );

      let checkbox = container.querySelector('input') as HTMLInputElement;
      let confirmation = document.querySelector('.confirm-dialog-container');

      expect(confirmation).not.toBeInTheDocument();

      act(() => {
        fireEvent.click(checkbox);
      });

      await waitFor(() => {
        const conf = document.querySelector('.confirm-dialog-container');
        expect(conf).toBeInTheDocument();
      });

      confirmation = document.querySelector('.confirm-dialog-container');

      const cancelButton = document.querySelectorAll('button')[0];

      act(() => {
        fireEvent.click(cancelButton);
      });

      await waitFor(() => {
        const conf = document.querySelector('.confirm-dialog-container');
        expect(conf).not.toBeInTheDocument();
      });

      checkbox = container.querySelector('input') as HTMLInputElement;
      confirmation = document.querySelector('.confirm-dialog-container');

      expect(checkbox.className).not.toContain('hasConfirm');
      expect(confirmation).not.toBeInTheDocument();
      expect(spy).not.toHaveBeenCalled();
    });

    it(`emits 'onConfirmOpen' if set`, async () => {
      const spy = vi.fn();

      const { container } = render(
        <Checkbox
          name="test-name"
          confirmationMode="Advanced"
          confirmationConfig={{ onConfirmOpen: spy }}
        />,
      );

      const checkbox = container.querySelector('input') as HTMLInputElement;

      act(() => {
        fireEvent.click(checkbox);
      });

      await waitFor(() => {
        const confirmation = document.querySelector(
          '.confirm-dialog-container',
        );
        expect(confirmation).toBeInTheDocument();
      });

      const cancelButton = document.querySelectorAll('button')[0];

      act(() => {
        fireEvent.click(cancelButton);
      });

      await waitFor(() => {
        expect(spy).toHaveBeenCalledTimes(2);
        expect(spy).toHaveBeenNthCalledWith(1, true);
        expect(spy).toHaveBeenNthCalledWith(2, false);
      });
    });
  });
});
