import { act, render, screen } from '@testing-library/react'
import { Provider } from 'react-redux'
import { ParticipantsGridView } from './ParticipantsGridView'
import userEvent from '@testing-library/user-event'
import { initStore } from '../../../testUtils'
import { activeSpeakersChanged } from '../../../store/session/sessionSlice'

const renderWithParticipants = (numberOfRemoteParticipants: number, pageSize?: number, activeSpeakers?: string[]) => {
  const store = initStore(numberOfRemoteParticipants, activeSpeakers)
  const { container } = render(
    <Provider store={store}>
      <ParticipantsGridView pageSize={pageSize} />
    </Provider>
  )

  return { container, store }
}

test('displays all participants without buttons when all participants fit on the screen', () => {
  renderWithParticipants(10)

  for (const i of Array(10).keys()) {
    expect(screen.getByText(`Participant name: ${i.toString(10)}`)).toBeInTheDocument()
  }
  expect(screen.getByText('Participant name: local')).toBeInTheDocument()
  expect(screen.queryByRole('button', { name: 'Next' })).not.toBeInTheDocument()
  expect(screen.queryByRole('button', { name: 'Previous' })).not.toBeInTheDocument()
})

test('clicking the Next button displays the next page of participants', () => {
  renderWithParticipants(3, 2)

  const nextButton = screen.getByRole('button', { name: 'Next' })
  expect(nextButton).not.toBeDisabled()

  userEvent.click(nextButton)

  expect(screen.getAllByText(/Participant name:/)).toHaveLength(2)
  expect(screen.getByText('Participant name: 1')).toBeInTheDocument()
  expect(screen.getByText('Participant name: 2')).toBeInTheDocument()
  expect(screen.queryByRole('button', { name: 'Next' })).not.toBeInTheDocument()
})

test('clicking the Previous button displays the previous page of participants', () => {
  renderWithParticipants(3, 2)
  userEvent.click(screen.getByRole('button', { name: 'Next' }))
  const previousButton = screen.getByRole('button', { name: 'Previous' })
  expect(previousButton).not.toBeDisabled()

  userEvent.click(previousButton)

  expect(screen.getAllByText(/Participant name:/)).toHaveLength(2)
  expect(screen.getByText('Participant name: local')).toBeInTheDocument()
  expect(screen.getByText('Participant name: 0')).toBeInTheDocument()
  expect(screen.queryByRole('button', { name: 'Previous' })).not.toBeInTheDocument()
})

test.each([
  [1, '.zocks__participants-grid-view__grid--1-column'],
  [2, '.zocks__participants-grid-view__grid--2-columns'],
  [4, '.zocks__participants-grid-view__grid--2-columns'],
  [6, '.zocks__participants-grid-view__grid--3-columns'],
  [9, '.zocks__participants-grid-view__grid--3-columns'],
  [16, '.zocks__participants-grid-view__grid--4-columns'],
  [25, '.zocks__participants-grid-view__grid--5-columns'],
])(
  'with %i participant(s), the grid should be displayed with %s grid style',
  (numberOfParticipants: number, gridClass: string) => {
    const { container } = renderWithParticipants(numberOfParticipants - 1)

    expect(container.querySelector(gridClass)).toBeInTheDocument()
  }
)

test('when the page size is less than the number of participants, grid class is adjusted', () => {
  const { container } = renderWithParticipants(9, 3)

  expect(container.querySelector('.zocks__participants-grid-view__grid--2-columns')).toBeInTheDocument()
})

test('order of active speakers is not considered if all of the participants can be displayed on a single page', () => {
  const { container } = renderWithParticipants(4, 5, ['Participant id: 3', 'Participant id: 0'])

  const participants = container.querySelector('.zocks__participants-grid-view__grid--3-columns')
  expect(participants!.children[0]).toHaveTextContent('Participant name: local')
  expect(participants!.children[1]).toHaveTextContent('Participant name: 0')
  expect(participants!.children[2]).toHaveTextContent('Participant name: 1')
  expect(participants!.children[3]).toHaveTextContent('Participant name: 2')
  expect(participants!.children[4]).toHaveTextContent('Participant name: 3')
})

test('order of active speakers is not considered if only the first page has active speakers', () => {
  const { container } = renderWithParticipants(5, 4, ['Participant id: 1', 'Participant id: 0'])

  const participants = container.querySelector('.zocks__participants-grid-view__grid--2-columns')
  expect(participants!.children[0]).toHaveTextContent('Participant name: local')
  expect(participants!.children[1]).toHaveTextContent('Participant name: 0')
  expect(participants!.children[2]).toHaveTextContent('Participant name: 1')
  expect(participants!.children[3]).toHaveTextContent('Participant name: 2')
})

test('an active speaker is moved to the first page if they are on another page', () => {
  const { container } = renderWithParticipants(5, 4, ['Participant id: 3'])

  const participants = container.querySelector('.zocks__participants-grid-view__grid--2-columns')
  expect(participants!.children).toHaveLength(4)
  expect(participants!.children[0]).toHaveTextContent('Participant name: local')
  expect(participants!.children[1]).toHaveTextContent('Participant name: 3')
  expect(participants!.children[2]).toHaveTextContent('Participant name: 0')
  expect(participants!.children[3]).toHaveTextContent('Participant name: 1')
})

test('an active speaker from another page stays on the first page if a first page participant starts to speak', () => {
  const { container, store } = renderWithParticipants(5, 4, ['Participant id: 3'])

  act(() => {
    store.dispatch(activeSpeakersChanged(['Participant id: 0']))
  })

  const participants = container.querySelector('.zocks__participants-grid-view__grid--2-columns')
  expect(participants!.children).toHaveLength(4)
  expect(participants!.children[0]).toHaveTextContent('Participant name: local')
  expect(participants!.children[1]).toHaveTextContent('Participant name: 3')
  expect(participants!.children[2]).toHaveTextContent('Participant name: 0')
  expect(participants!.children[3]).toHaveTextContent('Participant name: 1')
})

test('a participant from the first page is moved back to the first page if they were pushed out by other participants but they start speaking', () => {
  const { container, store } = renderWithParticipants(5, 4, ['Participant id: 3'])

  act(() => {
    store.dispatch(activeSpeakersChanged(['Participant id: 2']))
  })

  const participants = container.querySelector('.zocks__participants-grid-view__grid--2-columns')
  expect(participants!.children).toHaveLength(4)
  expect(participants!.children[0]).toHaveTextContent('Participant name: local')
  expect(participants!.children[1]).toHaveTextContent('Participant name: 2')
  expect(participants!.children[2]).toHaveTextContent('Participant name: 3')
  expect(participants!.children[3]).toHaveTextContent('Participant name: 0')
})

test('an active speaker from another page stays on the first page if an another page participant starts to speak', () => {
  const { container, store } = renderWithParticipants(5, 4, ['Participant id: 3'])

  act(() => {
    store.dispatch(activeSpeakersChanged(['Participant id: 4']))
  })

  const participants = container.querySelector('.zocks__participants-grid-view__grid--2-columns')
  expect(participants!.children).toHaveLength(4)
  expect(participants!.children[0]).toHaveTextContent('Participant name: local')
  expect(participants!.children[1]).toHaveTextContent('Participant name: 4')
  expect(participants!.children[2]).toHaveTextContent('Participant name: 3')
  expect(participants!.children[3]).toHaveTextContent('Participant name: 0')
})

test('disconnected participants do not affect active speaker ordering', () => {
  const { container } = renderWithParticipants(5, 4, ['Participant id: 5'])

  const participants = container.querySelector('.zocks__participants-grid-view__grid--2-columns')
  expect(participants!.children[0]).toHaveTextContent('Participant name: local')
  expect(participants!.children[1]).toHaveTextContent('Participant name: 0')
  expect(participants!.children[2]).toHaveTextContent('Participant name: 1')
  expect(participants!.children[3]).toHaveTextContent('Participant name: 2')
})
