import { mapFilterInputToRecord } from './filter-input-to-record.mapping';
import { InputType } from '../../types';
import {
  CheckboxFilterInput,
  DateRangeFilterInput,
  FilterInput,
  TextFilterInput,
} from '../schemas';

describe('mapFilterInputToRecord', () => {
  it('should map text input', () => {
    const input: FilterInput[] = [
      { key: 'name', type: InputType.Text, value: 'test' } as TextFilterInput,
    ];
    expect(mapFilterInputToRecord(input)).toEqual({ name: 'test' });
  });

  it('should map checkbox input', () => {
    const input: FilterInput[] = [
      {
        key: 'tags',
        type: InputType.Checkbox,
        selected: ['Key A', 'Key B'],
        group: 'g',
        options: [
          {
            key: 'Key A',
            label: 'Option A',
            value: 'a',
            path: ['g', 'tags', 'Key A'],
          },
          {
            key: 'Key B',
            label: 'Option B',
            value: 'b',
            path: ['g', 'tags', 'Key B'],
          },
        ],
      } as CheckboxFilterInput,
    ];
    expect(mapFilterInputToRecord(input)).toEqual({ tags: 'a;b' });
  });

  it('should map date range input', () => {
    const start = '2024-06-01T00:00:00.000Z';
    const stop = '2024-06-05T00:00:00.000Z';
    const input: FilterInput[] = [
      {
        key: 'range',
        type: InputType.DateRange,
        start,
        stop,
      } as DateRangeFilterInput,
    ];
    const result = mapFilterInputToRecord(input);
    // The stop value is incremented by 1 day and wrapped in quotes
    expect(result['range']).toMatch(
      /^"2024-06-01T00:00:00.000Z"-"2024-06-06T00:00:00.000Z"$/,
    );
  });

  it('should skip empty values', () => {
    const input: FilterInput[] = [
      { key: 'empty', type: InputType.Text, value: '', target: 'input' } as TextFilterInput,
      {
        key: 'none',
        type: InputType.Checkbox,
        selected: [],
        group: 'g',
        options: [],
        target: 'input',
      } as CheckboxFilterInput,
    ];
    expect(mapFilterInputToRecord(input)).toEqual({ empty: '', none: '' });
  });

  describe('nested checkbox selections', () => {
    it('should handle 2-level path resolution', () => {
      const input: FilterInput[] = [
        {
          key: 'categories',
          type: InputType.Checkbox,
          selected: ['electronics.phones'],
          group: 'g',
          target: 'input',
          options: [
            {
              key: 'electronics',
              label: 'Electronics',
              path: ['g', 'categories', 'electronics'],
              values: [
                {
                  key: 'phones',
                  label: 'Phones',
                  value: 'phone-value',
                  path: ['g', 'categories', 'electronics', 'phones'],
                },
              ],
            },
          ],
        } as CheckboxFilterInput,
      ];
      expect(mapFilterInputToRecord(input)).toEqual({ categories: 'phone-value' });
    });

    it('should handle parent selection collecting all child values', () => {
      const input: FilterInput[] = [
        {
          key: 'categories',
          type: InputType.Checkbox,
          selected: ['parent'],
          group: 'g',
          target: 'input',
          options: [
            {
              key: 'parent',
              label: 'Parent',
              path: ['g', 'categories', 'parent'],
              // Parent has no direct value
              values: [
                {
                  key: 'child1',
                  label: 'Child 1',
                  value: 'child1-value',
                  path: ['g', 'categories', 'parent', 'child1'],
                },
                {
                  key: 'child2',
                  label: 'Child 2',
                  value: 'child2-value',
                  path: ['g', 'categories', 'parent', 'child2'],
                },
              ],
            },
          ],
        } as CheckboxFilterInput,
      ];
      expect(mapFilterInputToRecord(input)).toEqual({
        categories: 'child1-value;child2-value'
      });
    });

    it('should handle mixed selections of parents and children', () => {
      const input: FilterInput[] = [
        {
          key: 'filters',
          type: InputType.Checkbox,
          selected: ['parent1', 'parent2.child'],
          group: 'g',
          target: 'input',
          options: [
            {
              key: 'parent1',
              label: 'Parent 1',
              value: 'parent1-value',
              path: ['g', 'filters', 'parent1'],
            },
            {
              key: 'parent2',
              label: 'Parent 2',
              path: ['g', 'filters', 'parent2'],
              values: [
                {
                  key: 'child',
                  label: 'Child',
                  value: 'child-value',
                  path: ['g', 'filters', 'parent2', 'child'],
                },
              ],
            },
          ],
        } as CheckboxFilterInput,
      ];
      expect(mapFilterInputToRecord(input)).toEqual({
        filters: 'parent1-value;child-value'
      });
    });

    it('should handle selections with non-existent paths', () => {
      const input: FilterInput[] = [
        {
          key: 'categories',
          type: InputType.Checkbox,
          selected: ['nonexistent.path', 'valid'],
          group: 'g',
          target: 'input',
          options: [
            {
              key: 'valid',
              label: 'Valid',
              value: 'valid-value',
              path: ['g', 'categories', 'valid'],
            },
          ],
        } as CheckboxFilterInput,
      ];
      expect(mapFilterInputToRecord(input)).toEqual({ categories: 'valid-value' });
    });

    it('should handle empty or null selected array', () => {
      const input: FilterInput[] = [
        {
          key: 'categories',
          type: InputType.Checkbox,
          selected: null as any,
          group: 'g',
          target: 'input',
          options: [],
        } as CheckboxFilterInput,
      ];
      expect(mapFilterInputToRecord(input)).toEqual({ categories: '' });
    });

    it('should handle options with both value and child values', () => {
      const input: FilterInput[] = [
        {
          key: 'mixed',
          type: InputType.Checkbox,
          selected: ['parent'],
          group: 'g',
          target: 'input',
          options: [
            {
              key: 'parent',
              label: 'Parent',
              value: 'parent-value', // Has its own value
              path: ['g', 'mixed', 'parent'],
              values: [
                {
                  key: 'child',
                  label: 'Child',
                  value: 'child-value',
                  path: ['g', 'mixed', 'parent', 'child'],
                },
              ],
            },
          ],
        } as CheckboxFilterInput,
      ];
      expect(mapFilterInputToRecord(input)).toEqual({
        mixed: 'parent-value' // Should use the parent's direct value, not collect children
      });
    });
  });

  describe('date range edge cases', () => {
    it('should handle only start date', () => {
      const input: FilterInput[] = [
        {
          key: 'range',
          type: InputType.DateRange,
          start: '2024-06-01T00:00:00.000Z',
          stop: undefined,
          target: 'input',
        } as DateRangeFilterInput,
      ];
      expect(mapFilterInputToRecord(input)).toEqual({
        range: '"2024-06-01T00:00:00.000Z"-'
      });
    });

    it('should handle only stop date', () => {
      const input: FilterInput[] = [
        {
          key: 'range',
          type: InputType.DateRange,
          start: undefined,
          stop: '2024-06-05T00:00:00.000Z',
          target: 'input',
        } as DateRangeFilterInput,
      ];
      const result = mapFilterInputToRecord(input);
      expect(result['range']).toMatch(/^-"2024-06-06T00:00:00.000Z"$/);
    });

    it('should skip date range with no dates', () => {
      const input: FilterInput[] = [
        {
          key: 'range',
          type: InputType.DateRange,
          start: undefined,
          stop: undefined,
          target: 'input',
        } as DateRangeFilterInput,
      ];
      expect(mapFilterInputToRecord(input)).toEqual({});
    });
  });

  it('should handle multiple filter types together', () => {
    const input: FilterInput[] = [
      { key: 'name', type: InputType.Text, value: 'test', target: 'input' } as TextFilterInput,
      {
        key: 'tags',
        type: InputType.Checkbox,
        selected: ['tag1'],
        group: 'g',
        target: 'input',
        options: [
          {
            key: 'tag1',
            label: 'Tag 1',
            value: 'tag1-value',
            path: ['g', 'tags', 'tag1'],
          },
        ],
      } as CheckboxFilterInput,
      {
        key: 'date',
        type: InputType.DateRange,
        start: '2024-01-01T00:00:00.000Z',
        stop: '2024-01-31T23:59:59.999Z',
        target: 'input',
      } as DateRangeFilterInput,
    ];
    const result = mapFilterInputToRecord(input);
    expect(result).toMatchObject({
      name: 'test',
      tags: 'tag1-value',
    });
    expect(result['date']).toMatch(/^"2024-01-01T00:00:00.000Z"-"2024-02-01T/);
  });
});
