import { DataTableActionHandler } from '@/manager/modules/ui/data-table/components/DataTableActionHandler';
import { DataTableRowSelector } from '@/manager/modules/ui/data-table/components/DataTableRowSelector';
import { DataTableSortOrder } from '@/manager/modules/ui/data-table/data-table-types';
import { SingleSelect } from '@/manager/shared/components/SingleSelect';
import { BaseComponent } from '@/shared/base/BaseComponent';
import { getAttribute } from '@/shared/utils/locator-utils';
import { Locator } from '@playwright/test';

export class DataTable extends BaseComponent {
  readonly main = this.host.locator('.ui-data-grid');
  readonly table = this.main.locator('[data-test-id=ui-data-grid-table]:not(.disabled):not(.loading):not(.fetching)');
  readonly tbody = this.table.getByTestId('ui-data-grid-table-with-data');
  readonly rowSelector = new DataTableRowSelector(this.table);
  readonly actionHandler = new DataTableActionHandler(this.table);
  readonly paginationSelect = new SingleSelect(this.main.getByTestId('items-per-page-select'));

  waitForLoading(): Promise<void> {
    return this.table.waitFor();
  }

  async sortBy(columnheaderId: string, sortOrder: DataTableSortOrder): Promise<void> {
    const columnheaderButton = this.getColumnheaderButton(columnheaderId);

    while ((await this.getColumnheaderButtonSortOrder(columnheaderButton)) !== sortOrder) {
      await columnheaderButton.click();
    }
  }

  selectRowsPerPage(rowsPerPage: number): Promise<void> {
    return this.paginationSelect.select(rowsPerPage.toString());
  }

  getRows(): Locator {
    return this.tbody.locator('tr');
  }

  getRow(index: number): Locator {
    const rows = this.getRows();
    return rows.nth(index);
  }

  clickRow(index: number): Promise<void> {
    const row = this.getRow(index);
    return row.click();
  }

  async removeRowByName(name: string): Promise<void> {
    await this.getRowColumnByName(name, 'remove').locator('button').click();
  }

  getColumn(columnId: string): Locator {
    const rows = this.getRows();
    return rows.getByTestId(this.getColumnTestId(columnId));
  }

  getColumnheader(columnheaderId: string): Locator {
    return this.main.getByTestId(this.getColumnheaderTestId(columnheaderId));
  }

  getRowColumnByIndex(index: number, columnId: string): Locator {
    const row = this.getRow(index);
    return row.getByTestId(this.getColumnTestId(columnId));
  }

  // Deliberately matches the name cell by substring (hasText), unlike getRowColumnBy's exact getByText:
  // name cells can render more than the bare name, and callers pass unique-enough names.
  getRowColumnByName(name: string, columnId: string): Locator {
    return this.getRows()
      .filter({ has: this.tbody.page().getByTestId(this.getColumnTestId('name')).filter({ hasText: name }) })
      .getByTestId(this.getColumnTestId(columnId));
  }

  getRowColumnBy(filters: { columnId: string; text: string }[], columnId: string): Locator {
    let rows = this.getRows();

    for (const filter of filters) {
      rows = rows.filter({
        has: this.tbody
          .page()
          .getByTestId(this.getColumnTestId(filter.columnId))
          .getByText(filter.text, { exact: true }),
      });
    }

    return rows.getByTestId(this.getColumnTestId(columnId));
  }

  private getColumnheaderButton(columnheaderId: string): Locator {
    return this.getColumnheader(columnheaderId).locator('button');
  }

  private async getColumnheaderButtonSortOrder(columnheaderButton: Locator): Promise<DataTableSortOrder> {
    const current = await getAttribute(columnheaderButton, 'aria-sort');

    switch (current) {
      case 'ascending':
        return 'asc';
      case 'descending':
        return 'desc';
      default:
        return 'none';
    }
  }

  private getColumnheaderTestId(columnheaderId: string): string {
    return `ui-data-grid-header-${columnheaderId}`;
  }

  private getColumnTestId(columnId: string): string {
    return `ui-data-grid-cell-${columnId}`;
  }
}
