import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { act, fireEvent, render, screen } from '@testing-library/react'
import { Provider } from '../provider/widget-provider'
import { clearAllWidgetStores, getWidgetStore } from '../stores'
import { Table } from './table'
import type { TableColumn, TableWidgetState } from './types'

beforeEach(() => clearAllWidgetStores())
afterEach(() => clearAllWidgetStores())

const COLUMNS: TableColumn[] = [
  { id: 'name', label: 'Name', sortable: true },
  { id: 'score', label: 'Score', sortable: true },
]

const DATA = Array.from({ length: 23 }, (_, i) => ({
  id: i + 1,
  name: `Row ${String(i + 1).padStart(2, '0')}`,
  score: ((i * 7) % 11) + 1,
}))

describe('<Table>', () => {
  it('paginates rows from the store, defaulting to page 0', () => {
    render(
      <Provider id='t1' data={DATA}>
        <Table columns={COLUMNS} initialPageSize={5} />
      </Provider>,
    )
    expect(screen.getByText('Row 01')).toBeTruthy()
    expect(screen.getByText('Row 05')).toBeTruthy()
    expect(screen.queryByText('Row 06')).toBeNull()
  })

  it('writes sort to the store and resets page=0', () => {
    render(
      <Provider id='t2' data={DATA}>
        <Table columns={COLUMNS} initialPageSize={5} />
      </Provider>,
    )
    // Move to page 2 first.
    getWidgetStore('t2').setState({ page: 2 } as Partial<TableWidgetState>)
    fireEvent.click(screen.getByText('Score'))
    const state = getWidgetStore('t2').getState() as TableWidgetState
    expect(state.sort).toEqual({ columnId: 'score', direction: 'asc' })
    expect(state.page).toBe(0)
  })

  it('honors columnOrder from the store', () => {
    render(
      <Provider id='t3' data={DATA.slice(0, 2)}>
        <Table columns={COLUMNS} initialPageSize={5} />
      </Provider>,
    )
    act(() => {
      getWidgetStore('t3').setState({
        columnOrder: ['score', 'name'],
      } as Partial<TableWidgetState>)
    })
    const headers = screen.getAllByRole('columnheader')
    expect(headers[0]?.textContent).toContain('Score')
    expect(headers[1]?.textContent).toContain('Name')
  })

  it('in remote mode renders data as-is and uses external `total` for the footer', () => {
    // Only 3 rows worth of "current page" data, but the server says
    // there are 87 total. The bridge must not re-sort / re-slice.
    const PAGE = DATA.slice(0, 3)
    render(
      <Provider id='t-remote' data={PAGE}>
        <Table columns={COLUMNS} remote total={87} initialPageSize={3} />
      </Provider>,
    )
    expect(screen.getByText('Row 01')).toBeTruthy()
    expect(screen.getByText('Row 03')).toBeTruthy()
    expect(screen.queryByText('Row 04')).toBeNull()
    // Pagination footer surfaces the remote total, not data.length.
    expect(screen.getByText(/of 87/)).toBeTruthy()
  })

  it('fires onSortChange / onPageChange / onPageSizeChange after store writes', () => {
    const onSortChange = vi.fn()
    const onPageChange = vi.fn()
    const onPageSizeChange = vi.fn()
    render(
      <Provider id='t-cb' data={DATA}>
        <Table
          columns={COLUMNS}
          initialPageSize={5}
          onSortChange={onSortChange}
          onPageChange={onPageChange}
          onPageSizeChange={onPageSizeChange}
        />
      </Provider>,
    )
    fireEvent.click(screen.getByText('Score'))
    expect(onSortChange).toHaveBeenCalledWith({
      columnId: 'score',
      direction: 'asc',
    })
  })

  it('selection prop drives the row checkbox state and onSelectionChange fires', () => {
    const onSelectionChange = vi.fn()
    render(
      <Provider id='t4' data={DATA.slice(0, 2)}>
        <Table
          columns={COLUMNS}
          selectable
          selection={[2]}
          onSelectionChange={onSelectionChange}
        />
      </Provider>,
    )
    const cb = screen.getByLabelText('Select row 2')
    expect((cb as HTMLInputElement).checked).toBe(true)
    fireEvent.click(screen.getByLabelText('Select row 1'))
    expect(onSelectionChange).toHaveBeenCalledWith([2, 1])
  })
})
