import { isOptgroupSymbol } from './SuperSelect.constants'
import {
  collectGroupsInOrderOfOccurrence,
  createOptgroup,
  getBatchOptionFormatter,
  getGroupNameIsValid,
  getIsAllOptionsUngrouped,
  getOptionsSortingAlgorithm,
  removeEmptyOptGroups,
  reorder,
  sortOptgroups,
} from './SuperSelect.utils'

describe('reorder', () => {
  test('should move from index 4 to 1, from group 2 to 1', () => {
    expect(
      reorder(
        [
          {
            id: 1,
            label: 'Group 1',
            group: 'Group 1',
            [isOptgroupSymbol]: true,
          },
          { id: 2, label: 'item 2', group: 'Group 1' },
          { id: 3, label: 'item 3', group: 'Group 1' },
          {
            id: 4,
            label: 'Group 2',
            group: 'Group 2',
            [isOptgroupSymbol]: true,
          },
          { id: 5, label: 'item 5', group: 'Group 2' },
        ],
        4,
        1
      )
    ).toEqual({
      nextOptions: [
        { id: 1, label: 'Group 1', group: 'Group 1', [isOptgroupSymbol]: true },
        { id: 5, label: 'item 5', group: 'Group 2' },
        { id: 2, label: 'item 2', group: 'Group 1' },
        { id: 3, label: 'item 3', group: 'Group 1' },
        { id: 4, label: 'Group 2', group: 'Group 2', [isOptgroupSymbol]: true },
      ],
      prevGroup: 4,
      nextGroup: 1,
    })
  })

  test('should move from index 2 to 1, from group 1 to 1', () => {
    expect(
      reorder(
        [
          {
            id: 1,
            label: 'Group 1',
            group: 'Group 1',
            [isOptgroupSymbol]: true,
          },
          { id: 2, label: 'item 2', group: 'Group 1' },
          { id: 3, label: 'item 3', group: 'Group 1' },
          {
            id: 4,
            label: 'Group 2',
            group: 'Group 2',
            [isOptgroupSymbol]: true,
          },
          { id: 5, label: 'item 5', group: 'Group 2' },
        ],
        2,
        1
      )
    ).toEqual({
      nextOptions: [
        { id: 1, label: 'Group 1', group: 'Group 1', [isOptgroupSymbol]: true },
        { id: 3, label: 'item 3', group: 'Group 1' },
        { id: 2, label: 'item 2', group: 'Group 1' },
        { id: 4, label: 'Group 2', group: 'Group 2', [isOptgroupSymbol]: true },
        { id: 5, label: 'item 5', group: 'Group 2' },
      ],
      prevGroup: 1,
      nextGroup: 1,
    })
  })

  test('should move from index 2 to 0, from group 1 to no group', () => {
    expect(
      reorder(
        [
          {
            id: 1,
            label: 'Group 1',
            group: 'Group 1',
            [isOptgroupSymbol]: true,
          },
          { id: 2, label: 'item 2', group: 'Group 1' },
          { id: 3, label: 'item 3', group: 'Group 1' },
          {
            id: 4,
            label: 'Group 2',
            group: 'Group 2',
            [isOptgroupSymbol]: true,
          },
          { id: 5, label: 'item 5', group: 'Group 2' },
        ],
        2,
        0
      )
    ).toEqual({
      nextOptions: [
        { id: 3, label: 'item 3', group: 'Group 1' },
        { id: 1, label: 'Group 1', group: 'Group 1', [isOptgroupSymbol]: true },
        { id: 2, label: 'item 2', group: 'Group 1' },
        { id: 4, label: 'Group 2', group: 'Group 2', [isOptgroupSymbol]: true },
        { id: 5, label: 'item 5', group: 'Group 2' },
      ],
      prevGroup: 1,
      nextGroup: 'RESERVED_UNGROUPED_OPTGROUP_NAME',
    })
  })

  test('should move from index 2 to 3, from group 1 to 2', () => {
    expect(
      reorder(
        [
          {
            id: 1,
            label: 'Group 1',
            group: 'Group 1',
            [isOptgroupSymbol]: true,
          },
          { id: 2, label: 'item 2', group: 'Group 1' },
          { id: 3, label: 'item 3', group: 'Group 1' },
          {
            id: 4,
            label: 'Group 2',
            group: 'Group 2',
            [isOptgroupSymbol]: true,
          },
          { id: 5, label: 'item 5', group: 'Group 2' },
        ],
        2,
        3
      )
    ).toEqual({
      nextOptions: [
        { id: 1, label: 'Group 1', group: 'Group 1', [isOptgroupSymbol]: true },
        { id: 2, label: 'item 2', group: 'Group 1' },
        { id: 4, label: 'Group 2', group: 'Group 2', [isOptgroupSymbol]: true },
        { id: 3, label: 'item 3', group: 'Group 1' },
        { id: 5, label: 'item 5', group: 'Group 2' },
      ],
      prevGroup: 1,
      nextGroup: 4,
    })
  })

  test('should move from index 0 to 2, from no group to group 1', () => {
    expect(
      reorder(
        [
          { id: 2, label: 'item 2', group: 'RESERVED_UNGROUPED_OPTGROUP_NAME' },
          {
            id: 1,
            label: 'Group 1',
            group: 'Group 1',
            [isOptgroupSymbol]: true,
          },
          { id: 3, label: 'item 3', group: 'Group 1' },
          {
            id: 4,
            label: 'Group 2',
            group: 'Group 2',
            [isOptgroupSymbol]: true,
          },
          { id: 5, label: 'item 5', group: 'Group 2' },
        ],
        0,
        2
      )
    ).toEqual({
      nextOptions: [
        { id: 1, label: 'Group 1', group: 'Group 1', [isOptgroupSymbol]: true },
        { id: 3, label: 'item 3', group: 'Group 1' },
        { id: 2, label: 'item 2', group: 'RESERVED_UNGROUPED_OPTGROUP_NAME' },
        { id: 4, label: 'Group 2', group: 'Group 2', [isOptgroupSymbol]: true },
        { id: 5, label: 'item 5', group: 'Group 2' },
      ],
      prevGroup: 'RESERVED_UNGROUPED_OPTGROUP_NAME',
      nextGroup: 1,
    })
  })

  test('should move from index 1 to 5, from group 1 to 3', () => {
    expect(
      reorder(
        [
          {
            id: 1,
            label: 'Group 1',
            group: 'Group 1',
            [isOptgroupSymbol]: true,
          },
          { id: 2, label: 'item 2', group: 'Group 1' },
          { id: 3, label: 'item 3', group: 'Group 1' },
          {
            id: 4,
            label: 'Group 2',
            group: 'Group 2',
            [isOptgroupSymbol]: true,
          },
          {
            id: 6,
            label: 'Group 3',
            group: 'Group 3',
            [isOptgroupSymbol]: true,
          },
          { id: 5, label: 'item 5', group: 'Group 3' },
        ],
        1,
        5
      )
    ).toEqual({
      nextOptions: [
        { id: 1, label: 'Group 1', group: 'Group 1', [isOptgroupSymbol]: true },
        { id: 3, label: 'item 3', group: 'Group 1' },
        { id: 4, label: 'Group 2', group: 'Group 2', [isOptgroupSymbol]: true },
        { id: 6, label: 'Group 3', group: 'Group 3', [isOptgroupSymbol]: true },
        { id: 5, label: 'item 5', group: 'Group 3' },
        { id: 2, label: 'item 2', group: 'Group 1' },
      ],
      prevGroup: 1,
      nextGroup: 6,
    })
  })

  test('should move from index 0 to 1, from no group to no group', () => {
    expect(
      reorder(
        [
          { id: 2, label: 'item 2', group: 'RESERVED_UNGROUPED_OPTGROUP_NAME' },
          { id: 6, label: 'item 6', group: 'RESERVED_UNGROUPED_OPTGROUP_NAME' },
          {
            id: 1,
            label: 'Group 1',
            group: 'Group 1',
            [isOptgroupSymbol]: true,
          },
          { id: 3, label: 'item 3', group: 'Group 1' },
          {
            id: 4,
            label: 'Group 2',
            group: 'Group 2',
            [isOptgroupSymbol]: true,
          },
          { id: 5, label: 'item 5', group: 'Group 2' },
        ],
        0,
        1
      )
    ).toEqual({
      nextOptions: [
        { id: 6, label: 'item 6', group: 'RESERVED_UNGROUPED_OPTGROUP_NAME' },
        { id: 2, label: 'item 2', group: 'RESERVED_UNGROUPED_OPTGROUP_NAME' },
        { id: 1, label: 'Group 1', group: 'Group 1', [isOptgroupSymbol]: true },
        { id: 3, label: 'item 3', group: 'Group 1' },
        { id: 4, label: 'Group 2', group: 'Group 2', [isOptgroupSymbol]: true },
        { id: 5, label: 'item 5', group: 'Group 2' },
      ],
      prevGroup: 'RESERVED_UNGROUPED_OPTGROUP_NAME',
      nextGroup: 'RESERVED_UNGROUPED_OPTGROUP_NAME',
    })
  })
})

describe('getGroupNameIsValid', () => {
  test('should group name be valid', () => {
    expect(getGroupNameIsValid('Group 1')).toEqual(true)

    expect(getGroupNameIsValid(' ')).toEqual(true)

    expect(getGroupNameIsValid(null)).toEqual(false)

    expect(getGroupNameIsValid('')).toEqual(false)

    expect(getGroupNameIsValid(undefined)).toEqual(false)

    expect(getGroupNameIsValid('null')).toEqual(true)
  })
})

describe('collectGroupsInOrderOfOccurrence', () => {
  test('should get groups in the order they come and options', () => {
    expect(
      collectGroupsInOrderOfOccurrence(
        [
          { id: 1, label: 'item 1', customGroup: 'Group 2' },
          { id: 2, label: 'item 2', customGroup: 'Group 1' },
          { id: 3, label: 'item 3', customGroup: 'Group 1' },
          { id: 4, label: 'item 4', customGroup: 'Group 1' },
          { id: 5, label: 'item 5', customGroup: 'Group 3' },
          { id: 6, label: 'item 6', customGroup: 'Group 3' },
          { id: 7, label: 'item 7', customGroup: 'Group 2' },
          { id: 8, label: 'item 8', customGroup: null },
          { id: 9, label: 'item 9', customGroup: 'Group 1' },
          { id: 10, label: 'item 1', customGroup: 'Group 4' },
          { id: 11, label: 'item 2', customGroup: 'Group 4' },
          { id: 12, label: 'item 3', customGroup: 'Group 2' },
          { id: 13, label: 'item 4', customGroup: 'Group 4' },
          { id: 14, label: 'item 5', customGroup: undefined },
          { id: 15, label: 'item 6' },
          { id: 16, label: 'item 7', customGroup: 'Group 3' },
          { id: 17, label: 'item 8', customGroup: 'Group 1' },
          { id: 18, label: 'item 9', customGroup: 'Group 4' },
        ],
        (option) => {
          return option.customGroup
        }
      )
    ).toEqual({
      groups: [
        'RESERVED_UNGROUPED_OPTGROUP_NAME',
        'Group 2',
        'Group 1',
        'Group 3',
        'Group 4',
      ],
      groupedOptions: {
        RESERVED_UNGROUPED_OPTGROUP_NAME: [
          { id: 8, label: 'item 8', customGroup: null },
          { id: 14, label: 'item 5', customGroup: undefined },
          { id: 15, label: 'item 6' },
        ],
        'Group 2': [
          { id: 1, label: 'item 1', customGroup: 'Group 2' },
          { id: 7, label: 'item 7', customGroup: 'Group 2' },
          { id: 12, label: 'item 3', customGroup: 'Group 2' },
        ],
        'Group 1': [
          { id: 2, label: 'item 2', customGroup: 'Group 1' },
          { id: 3, label: 'item 3', customGroup: 'Group 1' },
          { id: 4, label: 'item 4', customGroup: 'Group 1' },
          { id: 9, label: 'item 9', customGroup: 'Group 1' },
          { id: 17, label: 'item 8', customGroup: 'Group 1' },
        ],
        'Group 3': [
          { id: 5, label: 'item 5', customGroup: 'Group 3' },
          { id: 6, label: 'item 6', customGroup: 'Group 3' },
          { id: 16, label: 'item 7', customGroup: 'Group 3' },
        ],
        'Group 4': [
          { id: 10, label: 'item 1', customGroup: 'Group 4' },
          { id: 11, label: 'item 2', customGroup: 'Group 4' },
          { id: 13, label: 'item 4', customGroup: 'Group 4' },
          { id: 18, label: 'item 9', customGroup: 'Group 4' },
        ],
      },
    })
  })
})

describe('getOptgroups', () => {
  test('should return sorted array with "RESERVED_UNGROUPED_OPTGROUP_NAME"', () => {
    const groupsArray = [
      'ksacowenv',
      'RESERVED_UNGROUPED_OPTGROUP_NAME',
      'asweoknvewovn',
    ]
    expect(groupsArray.sort(sortOptgroups)).toEqual([
      'RESERVED_UNGROUPED_OPTGROUP_NAME',
      'asweoknvewovn',
      'ksacowenv',
    ])
  })

  test('should return sorted array of group names', () => {
    const groupsArray = ['ksacowenv', 'baiisfbiw', 'csewryhds', 'asweoknvewovn']
    expect(groupsArray.sort(sortOptgroups)).toEqual([
      'asweoknvewovn',
      'baiisfbiw',
      'csewryhds',
      'ksacowenv',
    ])
  })
  test('should return -1 if groupA is ungrouped', () => {
    expect(
      sortOptgroups('RESERVED_UNGROUPED_OPTGROUP_NAME', 'usaihfjbsiewjb')
    ).toEqual(-1)
  })
  test('should return -1 if groupA higher than groupB', () => {
    expect(sortOptgroups('akdidnwdoke', 'hjbhvjhv')).toEqual(-1)
  })
  test('should return 1 if groupB higher than groupA', () => {
    expect(sortOptgroups('dhredveve', 'asoireovein')).toEqual(1)
  })
  test('should return -1 if groupA higher than groupB', () => {
    expect(sortOptgroups('aaaaaaaaab', 'aaaaaaaaac')).toEqual(-1)
  })
})

describe('getOptgroup', () => {
  test('should return options without group options', () => {
    expect(
      createOptgroup('RESERVED_UNGROUPED_OPTGROUP_NAME', [
        { id: 2, label: 'item 2' },
        { id: 3, label: 'item 3' },
      ])
    ).toEqual([
      { id: 2, label: 'item 2' },
      { id: 3, label: 'item 3' },
    ])
  })

  test('should return options with group options', () => {
    expect(
      createOptgroup('GroupA', [
        { id: 2, label: 'item 2' },
        { id: 3, label: 'item 3' },
      ])
    ).toEqual([
      {
        [isOptgroupSymbol]: true,
        label: 'GroupA',
        id: 'GroupA',
      },
      { id: 2, label: 'item 2' },
      { id: 3, label: 'item 3' },
    ])
  })
})

describe('getIsAllOptionsUngrouped', () => {
  test('should return false if not all options are ungrouped and true if they are', () => {
    expect(
      getIsAllOptionsUngrouped(['RESERVED_UNGROUPED_OPTGROUP_NAME'])
    ).toEqual(true)

    expect(
      getIsAllOptionsUngrouped(['RESERVED_UNGROUPED_OPTGROUP_NAME', 'Group 1'])
    ).toEqual(false)

    expect(getIsAllOptionsUngrouped([''])).toEqual(false)
  })
})

describe('getOptionsSortingAlgorithm', () => {
  const getOptionIsBatch = (option) => {
    return Array.isArray(option.value)
  }

  const getOptionLabel = (option) => {
    return option.label
  }

  test('should return sorted array first batch than options', () => {
    const options = [
      { label: 'Item 2', value: 2, id: '3' },

      {
        id: '1',
        value: [2, 6, 11],
        label: 'Group B',
      },
      {
        id: '2',
        value: [1, 5, 10],
        label: 'Group A',
      },
      { label: 'Item 3', value: 3, id: '4' },
    ]

    const sortOptions = getOptionsSortingAlgorithm({
      getOptionIsBatch,
      getOptionLabel,
    })

    expect(options.sort(sortOptions)).toEqual([
      {
        id: '2',
        value: [1, 5, 10],
        label: 'Group A',
      },
      {
        id: '1',
        value: [2, 6, 11],
        label: 'Group B',
      },
      { label: 'Item 2', value: 2, id: '3' },
      { label: 'Item 3', value: 3, id: '4' },
    ])
  })

  test('should return sorted array options only', () => {
    const optionsArray = [
      { label: 'Item 2', value: 2, id: '2' },
      { label: 'Item 4', value: 4, id: '4' },

      { label: 'Item 3', value: 3, id: '3' },
      { label: 'Item 1', value: 1, id: '1' },
    ]

    const sortOptions = getOptionsSortingAlgorithm({
      getOptionIsBatch,
      getOptionLabel,
    })

    expect(optionsArray.sort(sortOptions)).toEqual([
      { label: 'Item 1', value: 1, id: '1' },
      { label: 'Item 2', value: 2, id: '2' },
      { label: 'Item 3', value: 3, id: '3' },
      { label: 'Item 4', value: 4, id: '4' },
    ])
  })
})

describe('getBatchOptionFormatter', () => {
  const getOptionIsBatch = (option) => {
    return Array.isArray(option.value)
  }
  const getOptionValue = (option) => {
    return option.value
  }

  const value = [2, 4, 6, 9]

  test('should return disabled true', () => {
    expect(
      getBatchOptionFormatter({
        value,
        multiple: true,
        getOptionIsBatch,
        getOptionValue,
      })({
        id: '1',
        group: 'Groups 1',
        value: [2, 4, 6],
        label: 'Group B',
        disabled: false,
      })
    ).toEqual({
      id: '1',
      group: 'Groups 1',
      value: [2, 4, 6],
      label: 'Group B',
      disabled: true,
    })
  })

  test('should return disabled false', () => {
    expect(
      getBatchOptionFormatter({
        value,
        multiple: true,
        getOptionIsBatch,
        getOptionValue,
      })({
        id: '1',
        group: 'Groups 1',
        value: [2, 3, 6],
        label: 'Group B',
        disabled: false,
      })
    ).toEqual({
      id: '1',
      group: 'Groups 1',
      value: [2, 3, 6],
      label: 'Group B',
      disabled: false,
    })
  })

  test('should return option', () => {
    expect(
      getBatchOptionFormatter({
        value,
        multiple: false,
        getOptionIsBatch,
        getOptionValue,
      })({
        group: 'Group 1',
        label: 'Item 0',
        value: 0,
        disabled: true,
        id: '0',
      })
    ).toEqual({
      group: 'Group 1',
      label: 'Item 0',
      value: 0,
      disabled: true,
      id: '0',
    })
  })
})

describe('removeEmptyOptGroups', () => {
  test('should delete empty groups', () => {
    expect(
      removeEmptyOptGroups([
        {
          label: 'value 1',
          value: 1,
        },
        {
          value: [9, 8, 10],
          label: 'Batch Ungrouped',
          disabled: false,
        },
        {
          id: 'Group 0',
          label: 'Group 0',
          [isOptgroupSymbol]: true,
        },
        {
          id: 'Group 1',
          label: 'Group 1',
          [isOptgroupSymbol]: true,
        },
        { label: 'value 0', value: 0, group: 'Group 1' },
        {
          label: 'Batch Group 1.1',
          value: [4, 6],
          group: 'Group 1',
          disabled: false,
        },
        {
          label: 'Batch Group 1.2',
          value: [1, 7],
          group: 'Group 1',
          disabled: false,
        },
        {
          id: 'Batch Group',
          label: 'Batch Group',
          [isOptgroupSymbol]: true,
        },
        {
          value: [2, 3, 5],
          label: 'Batch Group',
          group: 'Batch Group',
          disabled: true,
        },
        {
          id: 'Group 2',
          label: 'Group 2',
          [isOptgroupSymbol]: true,
        },
        { label: 'value 2', value: 2, group: 'Group 2' },
        { label: 'value 3', value: 3, group: 'Group 2' },
        {
          label: 'Batch Group 2',
          value: [11, 12, 13],
          group: 'Group 2',
          disabled: false,
        },
        {
          id: 'Group B',
          label: 'Group B',
          [isOptgroupSymbol]: true,
        },
      ])
    ).toEqual([
      {
        label: 'value 1',
        value: 1,
      },
      {
        value: [9, 8, 10],
        label: 'Batch Ungrouped',
        disabled: false,
      },
      {
        id: 'Group 1',
        label: 'Group 1',
        [isOptgroupSymbol]: true,
      },
      { label: 'value 0', value: 0, group: 'Group 1' },
      {
        label: 'Batch Group 1.1',
        value: [4, 6],
        group: 'Group 1',
        disabled: false,
      },
      {
        label: 'Batch Group 1.2',
        value: [1, 7],
        group: 'Group 1',
        disabled: false,
      },
      {
        id: 'Batch Group',
        label: 'Batch Group',
        [isOptgroupSymbol]: true,
      },
      {
        value: [2, 3, 5],
        label: 'Batch Group',
        group: 'Batch Group',
        disabled: true,
      },
      {
        id: 'Group 2',
        label: 'Group 2',
        [isOptgroupSymbol]: true,
      },
      { label: 'value 2', value: 2, group: 'Group 2' },
      { label: 'value 3', value: 3, group: 'Group 2' },
      {
        label: 'Batch Group 2',
        value: [11, 12, 13],
        group: 'Group 2',
        disabled: false,
      },
    ])
  })
})
