import { render, waitFor } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import { describe, expect, test, vi } from 'vitest';
import UiDataTablePagination from '../UiDataTablePagination.vue';

// Mock table object that mimics TanStack Table's Table interface
/**
 *
 * @param options
 * @param options.pageIndex
 * @param options.pageSize
 * @param options.pageCount
 * @param options.canPreviousPage
 * @param options.canNextPage
 * @param options.selectedRows
 * @param options.totalRows
 */
function createMockTable(
  options: {
    pageIndex?: number;
    pageSize?: number;
    pageCount?: number;
    canPreviousPage?: boolean;
    canNextPage?: boolean;
    selectedRows?: number;
    totalRows?: number;
  } = {},
) {
  const {
    pageIndex = 0,
    pageSize = 10,
    pageCount = 5,
    canPreviousPage = false,
    canNextPage = true,
    selectedRows = 0,
    totalRows = 50,
  } = options;

  return {
    getState: vi.fn(() => ({
      pagination: { pageIndex, pageSize },
    })),
    getPageCount: vi.fn(() => pageCount),
    getCanPreviousPage: vi.fn(() => canPreviousPage),
    getCanNextPage: vi.fn(() => canNextPage),
    setPageIndex: vi.fn(),
    setPageSize: vi.fn(),
    previousPage: vi.fn(),
    nextPage: vi.fn(),
    getFilteredSelectedRowModel: vi.fn(() => ({
      rows: Array(selectedRows).fill({}),
    })),
    getFilteredRowModel: vi.fn(() => ({
      rows: Array(totalRows).fill({}),
    })),
  };
}

describe('UiDataTablePagination', () => {
  describe('Rendering', () => {
    // Navigation buttons are asserted with accessible names in the Navigation Buttons suite

    test('renders page info with current page and total pages', () => {
      const table = createMockTable({ pageIndex: 2, pageCount: 10 });
      const { getByText } = render(UiDataTablePagination, {
        props: { table },
      });

      expect(getByText('Page 3 of 10')).toBeInTheDocument();
    });

    test('renders rows per page selector by default', () => {
      const table = createMockTable({ pageSize: 20 });
      const { getByText } = render(UiDataTablePagination, {
        props: { table },
      });

      expect(getByText('Rows per page')).toBeInTheDocument();
      expect(getByText('20')).toBeInTheDocument();
    });

    test('renders selection count by default', () => {
      const table = createMockTable({ selectedRows: 5, totalRows: 100 });
      const { getByText } = render(UiDataTablePagination, {
        props: { table },
      });

      expect(getByText('5 of 100 row(s) selected.')).toBeInTheDocument();
    });

    test('hides page size selector when showPageSize is false', () => {
      const table = createMockTable();
      const { queryByText } = render(UiDataTablePagination, {
        props: { table, showPageSize: false },
      });

      expect(queryByText('Rows per page')).not.toBeInTheDocument();
    });

    test('hides selection count when showSelection is false', () => {
      const table = createMockTable({ selectedRows: 5, totalRows: 100 });
      const { queryByText } = render(UiDataTablePagination, {
        props: { table, showSelection: false },
      });

      expect(queryByText(/selected/i)).not.toBeInTheDocument();
    });
  });

  describe('Page Size Selector', () => {
    test('shows default page size options', async () => {
      const user = userEvent.setup();
      const table = createMockTable();
      const { getByText, getByRole } = render(UiDataTablePagination, {
        props: { table },
      });

      // Click the page size button (displays current page size)
      await user.click(getByText('10'));

      await waitFor(() => {
        expect(getByRole('menuitem', { name: '10' })).toBeInTheDocument();
        expect(getByRole('menuitem', { name: '20' })).toBeInTheDocument();
        expect(getByRole('menuitem', { name: '30' })).toBeInTheDocument();
        expect(getByRole('menuitem', { name: '50' })).toBeInTheDocument();
      });
    });

    test('shows custom page size options when provided', async () => {
      const user = userEvent.setup();
      const table = createMockTable({ pageSize: 5 });
      const { getByRole, getByText } = render(UiDataTablePagination, {
        props: { table, pageSizeOptions: [5, 15, 25] },
      });

      await user.click(getByText('5'));

      await waitFor(() => {
        expect(getByRole('menuitem', { name: '5' })).toBeInTheDocument();
        expect(getByRole('menuitem', { name: '15' })).toBeInTheDocument();
        expect(getByRole('menuitem', { name: '25' })).toBeInTheDocument();
      });
    });

    test('changes page size when option is selected', async () => {
      const user = userEvent.setup();
      const table = createMockTable();
      const { getByRole, getByText } = render(UiDataTablePagination, {
        props: { table },
      });

      await user.click(getByText('10'));
      await waitFor(() => expect(getByRole('menu')).toBeInTheDocument());

      await user.click(getByRole('menuitem', { name: '20' }));

      expect(table.setPageSize).toHaveBeenCalledWith(20);
    });
  });

  describe('Navigation Buttons', () => {
    test('first and previous buttons are disabled on first page', () => {
      const table = createMockTable({ canPreviousPage: false });
      const { getByRole } = render(UiDataTablePagination, {
        props: { table },
      });

      expect(getByRole('button', { name: /first page/i })).toBeDisabled();
      expect(getByRole('button', { name: /previous page/i })).toBeDisabled();
    });

    test('next and last buttons are disabled on last page', () => {
      const table = createMockTable({ canNextPage: false });
      const { getByRole } = render(UiDataTablePagination, {
        props: { table },
      });

      expect(getByRole('button', { name: /next page/i })).toBeDisabled();
      expect(getByRole('button', { name: /last page/i })).toBeDisabled();
    });

    test('navigates to first page when first button is clicked', async () => {
      const user = userEvent.setup();
      const table = createMockTable({ canPreviousPage: true, pageIndex: 3 });
      const { getByRole } = render(UiDataTablePagination, {
        props: { table },
      });

      await user.click(getByRole('button', { name: /first page/i }));

      expect(table.setPageIndex).toHaveBeenCalledWith(0);
    });

    test('navigates to previous page when previous button is clicked', async () => {
      const user = userEvent.setup();
      const table = createMockTable({ canPreviousPage: true });
      const { getByRole } = render(UiDataTablePagination, {
        props: { table },
      });

      await user.click(getByRole('button', { name: /previous page/i }));

      expect(table.previousPage).toHaveBeenCalled();
    });

    test('navigates to next page when next button is clicked', async () => {
      const user = userEvent.setup();
      const table = createMockTable({ canNextPage: true });
      const { getByRole } = render(UiDataTablePagination, {
        props: { table },
      });

      await user.click(getByRole('button', { name: /next page/i }));

      expect(table.nextPage).toHaveBeenCalled();
    });

    test('navigates to last page when last button is clicked', async () => {
      const user = userEvent.setup();
      const table = createMockTable({ canNextPage: true, pageCount: 10 });
      const { getByRole } = render(UiDataTablePagination, {
        props: { table },
      });

      await user.click(getByRole('button', { name: /last page/i }));

      expect(table.setPageIndex).toHaveBeenCalledWith(9);
    });

    test('all buttons are enabled when on middle page', () => {
      const table = createMockTable({
        canPreviousPage: true,
        canNextPage: true,
      });
      const { getByRole } = render(UiDataTablePagination, {
        props: { table },
      });

      expect(getByRole('button', { name: /first page/i })).toBeEnabled();
      expect(getByRole('button', { name: /previous page/i })).toBeEnabled();
      expect(getByRole('button', { name: /next page/i })).toBeEnabled();
      expect(getByRole('button', { name: /last page/i })).toBeEnabled();
    });
  });

  describe('Selection Count', () => {
    test('shows zero selected when no rows are selected', () => {
      const table = createMockTable({ selectedRows: 0, totalRows: 50 });
      const { getByText } = render(UiDataTablePagination, {
        props: { table },
      });

      expect(getByText('0 of 50 row(s) selected.')).toBeInTheDocument();
    });

    test('shows correct count when some rows are selected', () => {
      const table = createMockTable({ selectedRows: 12, totalRows: 100 });
      const { getByText } = render(UiDataTablePagination, {
        props: { table },
      });

      expect(getByText('12 of 100 row(s) selected.')).toBeInTheDocument();
    });
  });

  describe('i18n Support', () => {
    test('applies custom labels when provided', () => {
      const table = createMockTable({
        selectedRows: 3,
        totalRows: 20,
        pageIndex: 1,
        pageCount: 4,
      });
      const { getByText, getByRole } = render(UiDataTablePagination, {
        props: {
          table,
          labels: {
            rowsPerPage: 'Zeilen pro Seite',
            goToFirstPage: 'Zur ersten Seite',
            pageOf: (page: number, total: number) =>
              `Seite ${page} von ${total}`,
            rowsSelected: (selected: number, total: number) =>
              `${selected} von ${total} ausgewählt`,
          },
        },
      });

      expect(getByText('Zeilen pro Seite')).toBeInTheDocument();
      expect(getByText('Seite 2 von 4')).toBeInTheDocument();
      expect(getByText('3 von 20 ausgewählt')).toBeInTheDocument();
      expect(
        getByRole('button', { name: 'Zur ersten Seite' }),
      ).toBeInTheDocument();
    });
  });
});
