import { fireEvent, render, screen } from '@testing-library/react';
import { userEvent } from '../test-utils';

import Display from '../display';
import Title from '../title';

import Carousel, { type CarouselCard } from './Carousel';

jest.mock('@wise/components-theming', () => ({
  ...jest.requireActual<Record<string, unknown>>('@wise/components-theming'),
  useTheme: jest.fn().mockReturnValue({ theme: 'personal' }),
}));

const yourInvoiceOnClick = jest.fn();

const carouselCards: CarouselCard[] = [
  {
    id: 'YOUR_INVOICE',
    type: 'anchor',
    href: 'https://wise.com',
    onClick: yourInvoiceOnClick,
    content: <div>Add a link in accounting software like Xero or QuickBooks.</div>,
  },
  {
    id: 'YOUR_WEBSITE',
    type: 'button',
    onClick: () => {},
    content: <Title type="title-screen">Wise.</Title>,
  },
  {
    id: 'SHOP_OR_STALL',
    type: 'anchor',
    href: 'https://wise.com',
    onClick: () => {},
    content: <Display type="display-small">Get Quick Pay</Display>,
  },
];

const onClick = jest.fn();

const renderCarousel = () => {
  render(<Carousel header="Cool stuff" cards={carouselCards} onClick={onClick} />);
};

describe('Carousel', () => {
  const focusSpy = jest.fn();
  const scrollBy = jest.fn();

  beforeEach(() => {
    // eslint-disable-next-line functional/immutable-data
    window.HTMLElement.prototype.scrollIntoView = focusSpy;
    // eslint-disable-next-line functional/immutable-data
    window.HTMLElement.prototype.scrollBy = scrollBy;
  });

  afterEach(() => {
    focusSpy.mockClear();
  });

  it('shows expected content on page', async () => {
    renderCarousel();

    expect(
      screen.getByText('Add a link in accounting software like Xero or QuickBooks.'),
    ).toBeInTheDocument();

    expect(screen.getByText('Wise.')).toBeInTheDocument();

    expect(screen.getByText('Get Quick Pay')).toBeInTheDocument();
  });

  it('allows user to scroll carousel to the right when at start', async () => {
    mockPositions(948, 903);

    renderCarousel();

    const scrollLeftButton = screen.getByTestId('scroll-carousel-left');
    const scrollRightButton = screen.getByTestId('scroll-carousel-right');

    expect(scrollLeftButton).toBeDisabled();
    expect(scrollRightButton).toBeEnabled();

    await userEvent.click(scrollRightButton);

    expect(scrollBy).toHaveBeenCalledWith({ left: 300, behavior: 'smooth' });
  });

  it('allows user to scroll carousel to the left or right when in middle', async () => {
    mockPositions(948, 903);

    renderCarousel();

    const carousel = screen.getByRole('list');
    fireEvent.scroll(carousel, { target: { scrollLeft: 10 } });

    const scrollLeftButton = screen.getByTestId('scroll-carousel-left');
    const scrollRightButton = screen.getByTestId('scroll-carousel-right');

    expect(scrollLeftButton).toBeEnabled();
    expect(scrollRightButton).toBeEnabled();

    await userEvent.click(scrollLeftButton);

    expect(scrollBy).toHaveBeenCalledWith({ left: -300, behavior: 'smooth' });

    await userEvent.click(scrollRightButton);

    expect(scrollBy).toHaveBeenCalledWith({ left: 300, behavior: 'smooth' });
  });

  it('allows user to scroll carousel to the left when at end', async () => {
    mockPositions(948, 903);

    renderCarousel();

    const carousel = screen.getByRole('list');
    fireEvent.scroll(carousel, { target: { scrollLeft: 50 } });

    const scrollLeftButton = screen.getByTestId('scroll-carousel-left');
    const scrollRightButton = screen.getByTestId('scroll-carousel-right');

    expect(scrollLeftButton).toBeEnabled();
    expect(scrollRightButton).toBeDisabled();

    await userEvent.click(scrollLeftButton);

    expect(scrollBy).toHaveBeenCalledWith({ left: -300, behavior: 'smooth' });
  });

  it('allows user to go through carousel by indicator', async () => {
    mockPositions(948, 903);

    renderCarousel();

    const yourInvoiceIndicator = screen.getByTestId('YOUR_INVOICE-indicator');
    const yourWebsiteIndicator = screen.getByTestId('YOUR_WEBSITE-indicator');
    const shopOrStallIndicator = screen.getByTestId('SHOP_OR_STALL-indicator');
    const firstCard = getFirstCard();
    const secondCard = getSecondCard();
    const thirdCard = getThirdCard();

    expect(focusSpy).toHaveBeenCalledTimes(0);
    expect(firstCard).toHaveClass('carousel__card--focused');

    await userEvent.click(yourWebsiteIndicator);

    expect(focusSpy).toHaveBeenCalledTimes(1);
    expect(secondCard).toHaveClass('carousel__card--focused');

    await userEvent.click(shopOrStallIndicator);

    expect(focusSpy).toHaveBeenCalledTimes(2);
    expect(thirdCard).toHaveClass('carousel__card--focused');

    await userEvent.click(yourInvoiceIndicator);

    expect(focusSpy).toHaveBeenCalledTimes(3);
    expect(firstCard).toHaveClass('carousel__card--focused');
  });

  it('is focused on the first element and lets user navigate through them', async () => {
    renderCarousel();

    const firstCard = getFirstCard();
    const secondCard = getSecondCard();
    const thirdCard = getThirdCard();

    expect(firstCard).toBeInTheDocument();

    firstCard.focus();
    expect(firstCard).toHaveFocus();

    await userEvent.keyboard('{arrowright}');

    expect(secondCard).toHaveFocus();

    await userEvent.keyboard('{arrowright}');

    expect(thirdCard).toHaveFocus();

    await userEvent.keyboard('{arrowleft}');

    expect(secondCard).toHaveFocus();

    await userEvent.keyboard('{arrowleft}');

    expect(firstCard).toHaveFocus();
  });

  it('calls onClick when a card is clicked', async () => {
    renderCarousel();

    await userEvent.click(getFirstCard());

    expect(onClick).toHaveBeenCalledTimes(1);
    expect(yourInvoiceOnClick).toHaveBeenCalledTimes(1);
  });
});

const mockPositions = (scrollWidth: number, offsetWidth: number) => {
  // eslint-disable-next-line functional/immutable-data
  Object.defineProperty(HTMLElement.prototype, 'scrollWidth', {
    configurable: true,
    value: scrollWidth,
  });

  // eslint-disable-next-line functional/immutable-data
  Object.defineProperty(HTMLElement.prototype, 'offsetWidth', {
    configurable: true,
    value: offsetWidth,
  });
};

const getFirstCard = () =>
  screen.getByRole('link', {
    name: /Add a link in accounting software like Xero or QuickBooks\./,
  });

const getSecondCard = () => screen.getByRole('button', { name: /Wise\./ });

const getThirdCard = () => screen.getByRole('link', { name: /Get Quick Pay/ });
