import { describe, expect, it, vi } from 'vitest'

import { fireEvent, renderWithProviders, screen } from '../../../test/utils'

import MediaStackGrid from './MediaStackGrid'

const tiles = (count: number) =>
  Array.from({ length: count }, (_, index) => ({
    content: <span>tile {index + 1}</span>,
    ariaLabel: index === 1 ? 'Second tile' : undefined,
  }))

describe('MediaStackGrid', () => {
  it('renders nothing for empty tiles', () => {
    const { container } = renderWithProviders(<MediaStackGrid tiles={[]} />)
    expect(container.firstChild).toBeNull()
  })

  it.each([
    [1, 'aspect-square'],
    [2, 'aspect-[16/9]'],
    [3, 'aspect-[4/3]'],
    [4, 'aspect-[379/286]'],
  ])('renders the %s-tile layout', (count, layoutClass) => {
    const { container } = renderWithProviders(
      <MediaStackGrid tiles={tiles(count)} className="custom-grid" />
    )
    expect(container.firstElementChild).toHaveClass(layoutClass, 'custom-grid')
    expect(screen.getAllByText(/tile/)).toHaveLength(count)
  })

  it('clamps visible tiles and places overflow on the last tile', () => {
    const { container, rerender } = renderWithProviders(
      <MediaStackGrid tiles={tiles(6)} maxVisible={4} />
    )
    expect(container.querySelectorAll('span')).toHaveLength(4)
    expect(screen.getByText('+2')).toBeInTheDocument()
    expect(screen.getByText('+2').parentElement).toHaveTextContent('tile 4')
    rerender(<MediaStackGrid tiles={tiles(2)} maxVisible={4} />)
    expect(container.querySelectorAll('span')).toHaveLength(2)
    expect(screen.queryByText(/^\+/)).toBeNull()
    rerender(<MediaStackGrid tiles={tiles(1)} singleAspectRatio={0.5} />)
    expect(
      screen.getByText('tile 1').closest('div')?.parentElement
    ).toHaveStyle({
      aspectRatio: '0.5',
    })
  })

  it.each([
    [2, '+3', 'tile 2'],
    [3, '+2', 'tile 3'],
  ])(
    'shows overflow on the last visible tile when maxVisible is %s',
    (maxVisible, badge, lastTile) => {
      const { container } = renderWithProviders(
        <MediaStackGrid tiles={tiles(5)} maxVisible={maxVisible} />
      )
      expect(container.querySelectorAll('span')).toHaveLength(maxVisible)
      expect(screen.getByText(badge)).toBeInTheDocument()
      expect(screen.getByText(badge).parentElement).toHaveTextContent(lastTile)
    }
  )

  it('shows overflow on the single visible tile', () => {
    const { container } = renderWithProviders(
      <MediaStackGrid tiles={tiles(3)} maxVisible={1} />
    )
    expect(container.querySelectorAll('span')).toHaveLength(1)
    expect(screen.getByText('+2').parentElement).toHaveTextContent('tile 1')
  })

  it('does not show overflow when all tiles are visible', () => {
    renderWithProviders(<MediaStackGrid tiles={tiles(3)} maxVisible={3} />)
    expect(screen.queryByText(/^\+/)).toBeNull()
  })

  it('renders activatable buttons with labels and indexes', () => {
    const onActivate = vi.fn()
    renderWithProviders(
      <MediaStackGrid tiles={tiles(3)} onTileActivate={onActivate} />
    )
    const buttons = screen.getAllByRole('button')
    expect(buttons).toHaveLength(3)
    expect(buttons[0]).toHaveAttribute('aria-label', 'Open media 1')
    expect(buttons[1]).toHaveAttribute('aria-label', 'Second tile')
    fireEvent.click(buttons[0])
    fireEvent.click(buttons[2])
    expect(onActivate).toHaveBeenNthCalledWith(1, 0)
    expect(onActivate).toHaveBeenNthCalledWith(2, 2)
  })

  it('uses non-interactive shells when no activation handler is supplied', () => {
    renderWithProviders(<MediaStackGrid tiles={tiles(2)} />)
    expect(screen.queryAllByRole('button')).toHaveLength(0)
  })
})
