import { InputGroup, InputType, OrderBy, QuerySettings } from '../../types';
import { filterMapping } from './filter.mapping';
import * as filterGroupMappingModule from './filter-group.mapping';
import * as filterInputMappingModule from './filter-input.mapping';
import * as orderByOptionMappingModule from './order-by-option.mapping';

describe('filterMapping', () => {
  // Mock implementations for each specific mapping function
  const mockFilterGroupMapping = jest
    .fn()
    .mockImplementation((group: InputGroup) => ({
      group: group.group,
      label: group.label,
      mapped: 'group',
    }));

  const mockFilterInputMapping = jest
    .fn()
    .mockImplementation((group, input) => ({
      group,
      key: input.key,
      mapped: 'input',
    }));

  const mockOrderByOptionMapping = jest
    .fn()
    .mockImplementation((orderBy: OrderBy) => ({
      by: orderBy.by,
      mapped: 'orderBy',
    }));

  beforeEach(() => {
    jest.clearAllMocks();

    // Mock all the mapping functions that filterMapping delegates to
    jest
      .spyOn(filterGroupMappingModule, 'filterGroupMapping')
      .mockImplementation(mockFilterGroupMapping);
    jest
      .spyOn(filterInputMappingModule, 'filterInputMapping')
      .mockImplementation(mockFilterInputMapping);
    jest
      .spyOn(orderByOptionMappingModule, 'orderByOptionMapping')
      .mockImplementation(mockOrderByOptionMapping);
  });

  it('should map empty query settings correctly', () => {
    // Arrange
    const settings: QuerySettings = {
      filter: [],
      input: [],
      orderBy: [], // Add required property
    };

    // Act
    const result = filterMapping(settings);

    // Assert
    expect(result).toEqual({
      groups: [],
      inputs: [],
      orderBy: [],
    });
    expect(mockFilterGroupMapping).not.toHaveBeenCalled();
    expect(mockFilterInputMapping).not.toHaveBeenCalled();
    expect(mockOrderByOptionMapping).not.toHaveBeenCalled();
  });

  it('should map filter groups correctly', () => {
    // Arrange
    const settings: QuerySettings = {
      filter: [
        {
          group: 'group1',
          label: 'Group 1',
          input: [
            { key: 'input1', label: 'Input 1', type: InputType.Text },
            { key: 'input2', label: 'Input 2', type: InputType.Checkbox },
          ],
        },
      ],
      input: [],
      orderBy: [], // Add required property
    };

    // Act
    const result = filterMapping(settings);

    // Assert
    expect(mockFilterGroupMapping).toHaveBeenCalledTimes(1);
    expect(mockFilterGroupMapping).toHaveBeenCalledWith(settings.filter[0]);
    expect(mockFilterInputMapping).toHaveBeenCalledTimes(2);
    expect(mockFilterInputMapping).toHaveBeenCalledWith(
      'group1',
      settings.filter[0].input[0],
    );
    expect(mockFilterInputMapping).toHaveBeenCalledWith(
      'group1',
      settings.filter[0].input[1],
    );
    expect(mockOrderByOptionMapping).not.toHaveBeenCalled();

    expect(result).toEqual({
      groups: [
        {
          group: 'group1',
          label: 'Group 1',
          mapped: 'group',
        },
      ],
      inputs: [
        {
          group: 'group1',
          key: 'input1',
          mapped: 'input',
        },
        {
          group: 'group1',
          key: 'input2',
          mapped: 'input',
        },
      ],
      orderBy: [],
    });
  });

  it('should map input groups correctly', () => {
    // Arrange
    const settings: QuerySettings = {
      filter: [],
      input: [
        {
          group: 'group2',
          label: 'Group 2',
          input: [{ key: 'input3', label: 'Input 3', type: InputType.Text }],
        },
      ],
      orderBy: [], // Add required property
    };

    // Act
    const result = filterMapping(settings);

    // Assert
    expect(mockFilterGroupMapping).toHaveBeenCalledTimes(1);
    expect(mockFilterGroupMapping).toHaveBeenCalledWith(settings.input[0]);
    expect(mockFilterInputMapping).toHaveBeenCalledTimes(1);
    expect(mockFilterInputMapping).toHaveBeenCalledWith(
      'group2',
      settings.input[0].input[0],
    );
    expect(mockOrderByOptionMapping).not.toHaveBeenCalled();

    expect(result).toEqual({
      groups: [
        {
          group: 'group2',
          label: 'Group 2',
          mapped: 'group',
        },
      ],
      inputs: [
        {
          group: 'group2',
          key: 'input3',
          mapped: 'input',
        },
      ],
      orderBy: [],
    });
  });

  it('should map orderBy options correctly', () => {
    // Arrange
    const settings: QuerySettings = {
      filter: [],
      input: [],
      orderBy: [
        { label: 'Sort by Name', by: 'name', desc: false },
        { label: 'Sort by Date', by: 'date', desc: true },
      ],
    };

    // Act
    const result = filterMapping(settings);

    // Assert
    expect(mockFilterGroupMapping).not.toHaveBeenCalled();
    expect(mockFilterInputMapping).not.toHaveBeenCalled();
    expect(mockOrderByOptionMapping).toHaveBeenCalledTimes(2);
    expect(mockOrderByOptionMapping).toHaveBeenNthCalledWith(
      1,
      settings.orderBy[0],
    );
    expect(mockOrderByOptionMapping).toHaveBeenNthCalledWith(
      2,
      settings.orderBy[1],
    );

    expect(result).toEqual({
      groups: [],
      inputs: [],
      orderBy: [
        { by: 'name', mapped: 'orderBy' },
        { by: 'date', mapped: 'orderBy' },
      ],
    });
  });

  it('should map a complete query settings object', () => {
    // Arrange
    const settings: QuerySettings = {
      filter: [
        {
          group: 'filter1',
          label: 'Filter 1',
          input: [{ key: 'input1', label: 'Input 1', type: InputType.Text }],
        },
      ],
      input: [
        {
          group: 'input1',
          label: 'Input 1',
          input: [
            { key: 'input2', label: 'Input 2', type: InputType.Checkbox },
          ],
        },
      ],
      orderBy: [{ label: 'Sort by Name', by: 'name', desc: false }],
    };

    // Act
    const result = filterMapping(settings);

    // Assert
    expect(mockFilterGroupMapping).toHaveBeenCalledTimes(2);
    expect(mockFilterInputMapping).toHaveBeenCalledTimes(2);
    expect(mockOrderByOptionMapping).toHaveBeenCalledTimes(1);

    expect(result).toEqual({
      groups: [
        {
          group: 'filter1',
          label: 'Filter 1',
          mapped: 'group',
        },
        {
          group: 'input1',
          label: 'Input 1',
          mapped: 'group',
        },
      ],
      inputs: [
        {
          group: 'filter1',
          key: 'input1',
          mapped: 'input',
        },
        {
          group: 'input1',
          key: 'input2',
          mapped: 'input',
        },
      ],
      orderBy: [{ by: 'name', mapped: 'orderBy' }],
    });
  });
});
