// eslint-disable-next-line @typescript-eslint/triple-slash-reference
/// <reference path="../../../../test/src/custom_typings/chai.d.ts" />
/* eslint-disable no-undef */
import {
  ZuiTableElement,
  ZuiTableRowElement,
  ZuiTableCellElement,
  ZuiTableTopbarElement,
  ZuiTableFooterElement,
} from '@zywave/zui-table';
import { assert } from '@esm-bundle/chai';
import { setViewport } from '@web/test-runner-commands';
import { isMobile } from '../../../../test/src/util/isMobile.js';
import { screenBreakpoints } from '@zywave/zui-base/dist/utils/breakpoints.js';
import { sleep } from '@zywave/../../test/src/util/helpers.js';
import type { ZuiTableSortDirection } from '@zywave/zui-table';

suite('zui-table', () => {
  let table: ZuiTableElement;
  let row: ZuiTableRowElement;
  let cell: ZuiTableCellElement;
  let topbar: ZuiTableTopbarElement;
  let footer: ZuiTableFooterElement;

  setup(() => {
    table = document.createElement('zui-table') as ZuiTableElement;
    row = document.createElement('zui-table-row') as ZuiTableRowElement;
    cell = document.createElement('zui-table-cell') as ZuiTableCellElement;
    topbar = document.createElement('zui-table-topbar') as ZuiTableTopbarElement;
    footer = document.createElement('zui-table-footer') as ZuiTableFooterElement;

    row.appendChild(cell);
    table.appendChild(topbar);
    table.appendChild(row);
    table.appendChild(footer);

    document.body.appendChild(table);
  });

  teardown(() => {
    document.body.removeChild(table);
  });

  test('initializes as a ZuiTableElement', () => {
    assert.instanceOf(table, ZuiTableElement);
  });

  test('initializes as a ZuiTableRowElement', () => {
    assert.instanceOf(row, ZuiTableRowElement);
  });

  test('initializes as a ZuiTableCellElement', () => {
    assert.instanceOf(cell, ZuiTableCellElement);
  });

  test('initializes as a ZuiTableTopbarElement', () => {
    assert.instanceOf(topbar, ZuiTableTopbarElement);
  });

  test('initializes as a ZuiTableFooterElement', () => {
    assert.instanceOf(footer, ZuiTableFooterElement);
  });

  test('roles are assigned properly to table elements', async () => {
    const rowHeader = document.createElement('zui-table-row');
    const rowHeaderCell = document.createElement('zui-table-cell');
    rowHeader.setAttribute('header', '');
    rowHeader.appendChild(rowHeaderCell);
    table.prepend(rowHeader);

    await table.updateComplete;

    const tableRole = table.getAttribute('role');
    const rowRole = row.getAttribute('role');
    const cellRole = cell.getAttribute('role');
    const rowHeaderCellRole = rowHeaderCell.getAttribute('role');

    assert.equal(tableRole, 'table', 'Expected table to have a role attribute');
    assert.equal(rowRole, 'row', 'Expected table row to have a role attribute');
    assert.equal(cellRole, 'cell', 'Expected table cell to have a role attribute');
    assert.equal(rowHeaderCellRole, 'columnheader', 'Expected table header cell to have a role attribute');
  });

  test('banded attribute alternates background color for rows', async () => {
    table.setAttribute('banded', '');
    table.appendChild(row.cloneNode(true));

    await table.updateComplete;

    const tableRows = table.querySelectorAll('zui-table-row');
    const tableRowBgColor = window.getComputedStyle(tableRows[1]);

    assert.property(
      tableRowBgColor,
      'background-color',
      'Expected alternate rows to have background color for banded tables'
    );
  });

  test('table with no-results attribute results in a no results view', async () => {
    table.setAttribute('no-results', '');
    await table.updateComplete;

    const shadowEl = table.shadowRoot;
    const noResultsDiv = shadowEl.querySelector('.no-results');

    assert.exists(noResultsDiv, 'Expected no-results view to show when zui-table as attribute no-results applied');
  });

  test('table with a footer will assign it to the slot "footer"', async () => {
    await table.updateComplete;

    const bb = table.querySelector('zui-table-footer');
    const footerSlot = bb.hasAttribute('slot');

    assert.isTrue(footerSlot, 'Expected zui-table-footer to be assigned to a slot');
  });

  test('hide counter and action divs in ZuiTableTopbar if empty', async () => {
    const zuiSearch = document.createElement('zui-search');
    topbar.appendChild(zuiSearch);

    await table.updateComplete;

    const counter = topbar.shadowRoot!.querySelector('.counter');
    const counterSlot: HTMLSlotElement = counter.querySelector('slot[name="counter"]');
    const counterSlotLength = counterSlot.assignedElements().length;
    const counterComputedDisplayStyle = window.getComputedStyle(counter).getPropertyValue('display');

    const action = topbar.shadowRoot!.querySelector('.action');
    const actionSlot: HTMLSlotElement = action.querySelector('slot[name="action"]');
    const actionSlotLength = actionSlot.assignedElements().length;
    const actionComputedDisplayStyle = window.getComputedStyle(action).getPropertyValue('display');

    assert.equal(counterSlotLength, 0, 'Expected counter slot length to be 0 when empty');
    assert.equal(counterComputedDisplayStyle, 'none', 'Expected counter div to be hidden on mobile if empty');
    assert.equal(actionSlotLength, 0, 'Expected action slot length to be 0 when empty');
    assert.equal(actionComputedDisplayStyle, 'none', 'Expected counter div to be hidden on mobile if empty');
  });

  suite('mobile', () => {
    let initialWidth, initialHeight: number;
    setup(async () => {
      initialHeight = window.innerHeight;
      initialWidth = window.innerWidth;
      await setViewport({ width: 400, height: 640 });

      await table.updateComplete;
      await sleep(100);
    });

    teardown(async () => {
      await setViewport({ width: initialWidth, height: initialHeight });
    });

    test('sets mobile state on small viewports', async () => {
      assert.isTrue(table.matches(':state(mobile)'), 'Expected zui-table to match :state(mobile)');
    });

    test('mobile table with headers in cell', async () => {
      const rowHeader = document.createElement('zui-table-row');
      const rowHeaderCell = document.createElement('zui-table-cell');
      rowHeader.setAttribute('header', '');
      rowHeaderCell.innerText = 'Dank';
      rowHeader.appendChild(rowHeaderCell);
      topbar.insertAdjacentElement('afterend', rowHeader);
      cell.innerText = 'Farrik!';

      await cell.updateComplete;
      await sleep(100);

      const rowHeaderComputedDisplayStyle = window.getComputedStyle(rowHeader).getPropertyValue('display');
      const rowHeaderText = rowHeaderCell.textContent;
      const bodyCellHeaderText = cell.shadowRoot.children[0].children[0].textContent;

      assert.isTrue(isMobile(), `Expected viewport to be <${screenBreakpoints.xsmall}`);
      assert.equal(rowHeaderComputedDisplayStyle, 'none', 'Expected table row header to be hidden on mobile');
      assert.exists(
        cell.shadowRoot.querySelector('.mobile-header'),
        'Expected cell to have an element with class .mobile-header in its shadow DOM'
      );
      assert.equal(
        rowHeaderText,
        bodyCellHeaderText,
        `Expected table body cell text (${bodyCellHeaderText}) to have header text (${rowHeaderText}) in it`
      );
    });

    test('ZuiButtons in the ZuiTableTopbar action slot switches to block style on mobile', async () => {
      const actionButton = document.createElement('zui-button');
      actionButton.innerText = 'Add a new entry';
      actionButton.setAttribute('slot', 'action');
      topbar.appendChild(actionButton);

      await table.updateComplete;
      await sleep(100);

      assert.isTrue(isMobile(), `Expected viewport to be <${screenBreakpoints.xsmall}`);
      assert.isTrue(actionButton.hasAttribute('block'));
    });
  });

  suite('sort', () => {
    test('cell can only be sortable when in header row', async () => {
      row.remove();

      const newRow = document.createElement('zui-table-row');
      const sortableCell = document.createElement('zui-table-cell');
      sortableCell.setAttribute('sortable', '');

      newRow.appendChild(sortableCell);
      table.append(newRow);

      await sortableCell.updateComplete;

      assert.isFalse(sortableCell.sortable);

      newRow.setAttribute('header', '');

      await sortableCell.updateComplete;
      assert.isTrue(sortableCell.sortable);
    });

    test('clicking sortable cell dispatches sort event', async () => {
      let sortEvent: CustomEvent<{ sort: ZuiTableSortDirection; cell: ZuiTableCellElement }> | undefined;
      let dispatchedSortEventsCounter = 0;
      cell.setAttribute('sortable', '');
      row.setAttribute('header', '');
      await cell.updateComplete;

      table.addEventListener(
        'sort',
        (event: CustomEvent<{ sort: ZuiTableSortDirection; cell: ZuiTableCellElement }>) => {
          sortEvent = event;
          dispatchedSortEventsCounter++;
        }
      );

      cell.click();

      assert.exists(sortEvent);
      assert.equal(sortEvent.detail.sort, 'ascending');
      assert.equal(sortEvent.detail.cell, cell);

      await sleep(100);

      assert.equal(dispatchedSortEventsCounter, 1);
    });

    test('clicking sortable cell changes direction', async () => {
      cell.setAttribute('sortable', '');
      row.setAttribute('header', '');
      await cell.updateComplete;

      assert.equal(cell.sort, null);

      cell.click();
      assert.equal(cell.sort, 'ascending');

      cell.click();
      assert.equal(cell.sort, 'descending');

      cell.click();
      assert.equal(cell.sort, null);
    });

    test('clicking sibling sortable cell resets direction', async () => {
      cell.setAttribute('sortable', '');
      cell.setAttribute('sort', 'ascending');

      const otherCell = document.createElement('zui-table-cell');
      otherCell.setAttribute('sortable', '');

      row.setAttribute('header', '');
      row.appendChild(otherCell);
      await cell.updateComplete;

      assert.equal(cell.sort, 'ascending');
      assert.equal(otherCell.sort, null);

      otherCell.click();
      assert.equal(cell.sort, null);
      assert.equal(otherCell.sort, 'ascending');
    });

    test('multiple header cells with directions set are reset except first', async () => {
      cell.setAttribute('sortable', '');
      cell.setAttribute('sort', 'ascending');
      row.setAttribute('header', '');

      const otherCell = document.createElement('zui-table-cell');
      otherCell.setAttribute('sortable', '');
      otherCell.setAttribute('sort', 'ascending');
      row.appendChild(otherCell);

      await cell.updateComplete;

      assert.equal(cell.sort, 'ascending');
      assert.equal(otherCell.sort, null);
      assert.isFalse(otherCell.hasAttribute('sort'));
    });
  });
});
