import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { DynamicListColumn } from '../DynamicDataList.model';
import { DynamicListHeader, DynamicListHeaderProps } from './DynamicListHeader';

interface DynamicListHeaderTestData {
  id: number;
  desc: string;
  title: string;
  position?: number;
}

const defaultColumns: DynamicListColumn<DynamicListHeaderTestData>[] = [
  {
    propertyName: 'id',
    label: 'Id',
  },
  {
    propertyName: 'title',
    label: 'Title',
  },
  {
    propertyName: 'desc',
    label: 'Description',
  },
];

const defaultProps: DynamicListHeaderProps<DynamicListHeaderTestData> = {
  columns: defaultColumns,
  columnSizes: 'column-size',
  columnGap: 'column-gap',
  rowHeight: 'row-height',
  horizontalTextAlign: 'left',
  verticalTextAlign: 'center',
  positionKey: 'position',
  allowReordering: undefined,
  onColumnSizesChanged: vi.fn(),
  onResetColumnSizes: vi.fn(),
};

describe('DynamicListHeader', () => {
  it('renders the component without crashing', () => {
    const { container } = render(<DynamicListHeader {...defaultProps} />);

    expect(container).toBeTruthy();
  });

  it('header should not be draggable', () => {
    const { container } = render(
      <DynamicListHeader
        {...defaultProps}
        columns={defaultColumns}
        columnSizes={defaultProps.columnSizes}
      />,
    );

    const draggableAttribute = (
      container.querySelector('.container') as HTMLElement
    )?.getAttribute('draggable');

    expect(draggableAttribute).toBeNull();
  });

  it('most styling props, except gridTemplateColumns, should be undefined by default', () => {
    const { container } = render(
      <DynamicListHeader
        columns={defaultColumns}
        columnSizes={defaultProps.columnSizes}
        onColumnSizesChanged={vi.fn()}
        onResetColumnSizes={vi.fn()}
      />,
    );

    const header = container.querySelector('.container') as HTMLElement;

    expect(header.style.gridColumnGap).toBe('');
    expect(header.style.alignItems).toBe('');
    expect(header.style.gridAutoRows).toBe('');
    expect(header.style.gridTemplateColumns).toBe(defaultProps.columnSizes);
    expect(header.style.justifyItems).toBe('');
  });

  it('sets styling props when passed in', () => {
    const { container } = render(<DynamicListHeader {...defaultProps} />);

    const header = container.querySelector('.container') as HTMLElement;

    expect(header.style.gridColumnGap).toBe(defaultProps.columnGap);
    expect(header.style.alignItems).toBe(defaultProps.verticalTextAlign);
    expect(header.style.gridAutoRows).toBe(defaultProps.rowHeight);
    expect(header.style.gridTemplateColumns).toBe(defaultProps.columnSizes);
    expect(header.style.justifyItems).toBe(defaultProps.horizontalTextAlign);
  });

  it('renders a label for every column if set', () => {
    const { container } = render(<DynamicListHeader {...defaultProps} />);

    const columns = Array.from(container.querySelectorAll('.column')).slice(
      0,
      -1,
    ); // remove the last column, which is the action column

    columns.forEach((column, idx) => {
      expect(column.textContent).toEqual(defaultColumns[idx].label);
    });
  });

  it(`renders the position column if 'showPositionColumn' is set to true`, () => {
    const { container } = render(
      <DynamicListHeader
        {...defaultProps}
        columnSizes={''}
        columns={defaultColumns}
        positionKey={'position'}
        allowReordering={true}
        showPositionColumn={true}
        allowDragging={true}
      />,
    );

    const position = container.querySelector('.position');

    expect(position).toBeInTheDocument();
    expect(position?.textContent).toBe('Position');
  });

  it(`sets the position label if set`, () => {
    const mockLabel = 'test-label';

    const { container } = render(
      <DynamicListHeader
        {...defaultProps}
        columnSizes={''}
        columns={defaultColumns}
        positionKey={'position'}
        allowReordering={true}
        positionLabel={mockLabel}
        showPositionColumn={true}
      />,
    );

    const position = container.querySelector('.position');

    expect(position).toBeInTheDocument();
    expect(position?.textContent).toBe(mockLabel);
  });

  it(`doesn't render the position column if a 'positionKey' isn't set`, () => {
    const { container } = render(
      <DynamicListHeader
        {...defaultProps}
        columnSizes={''}
        columns={defaultColumns}
        allowReordering={true}
      />,
    );

    const position = container.querySelector('.position');

    expect(position).not.toBeInTheDocument();
  });

  it(`doesn't render the position column if 'allowReordering' is false`, () => {
    const { container } = render(
      <DynamicListHeader
        {...defaultProps}
        columnSizes={''}
        columns={defaultColumns}
        positionKey={'position'}
        allowReordering={false}
      />,
    );

    const position = container.querySelector('.position');

    expect(position).not.toBeInTheDocument();
  });

  describe('Column Resizing', () => {
    it('calls onResetColumnSizes when the reset button is clicked', () => {
      const spy = vi.fn();
      const { container } = render(
        <DynamicListHeader {...defaultProps} onResetColumnSizes={spy} />,
      );

      const resetButton = container.querySelector(
        '.resizeHandle',
      ) as HTMLElement;
      fireEvent.doubleClick(resetButton);

      expect(spy).toHaveBeenCalledTimes(1);
    });

    it('calls onResetColumnSizes when the reset button is clicked also on non-resizable column', () => {
      const spy = vi.fn();
      const { container } = render(
        <DynamicListHeader
          {...defaultProps}
          columns={[
            { ...defaultColumns[0], disableResizing: true },
            ...defaultColumns.slice(1),
          ]}
          onResetColumnSizes={spy}
        />,
      );

      const resetButton = container.querySelector(
        '.resizeHandle',
      ) as HTMLElement;
      fireEvent.doubleClick(resetButton);

      expect(spy).toHaveBeenCalledTimes(1);
    });

    it('calls onColumnSizesChanged when the column is resized', () => {
      const spy = vi.fn();
      const { container } = render(
        <DynamicListHeader {...defaultProps} onColumnSizesChanged={spy} />,
      );

      const resizeHandle = container.querySelector(
        '.resizeHandle',
      ) as HTMLElement;

      fireEvent.mouseDown(resizeHandle, {
        preventDefault: vi.fn(),
      });

      fireEvent(
        window,
        new MouseEvent('mousemove', {
          clientX: 100,
        }),
      );

      window.dispatchEvent(new Event('mouseup', {} as any));

      expect(spy).toHaveBeenCalledTimes(1);
    });
  });
});
