import type { Option, OptionsGroup, Search } from './types'

export const filterOptions = (
  options: (Option | OptionsGroup)[],
  search: string
): (Option | OptionsGroup)[] => {
  const result: (Option | OptionsGroup)[] = []

  options.forEach((item: Option | OptionsGroup) => {
    if (typeof item !== 'string' && 'options' in item && Array.isArray(item.options)) {
      const filteredOptions = filterOptions(item.options, search)
      if (filteredOptions.length > 0) {
        result.push({ ...item, options: filteredOptions })
      }
    } else if (getOptionLabel(item).toLowerCase().includes(search.toLowerCase())) {
      result.push(item)
    }
  })

  return result
}

export const flattenOptionsArray = (
  options: (Option | OptionsGroup)[]
): (Option | OptionsGroup)[] => {
  const optionsList: (Option | OptionsGroup)[] = []

  for (const option of options) {
    if (typeof option !== 'string' && 'options' in option && Array.isArray(option.options)) {
      optionsList.push(...option.options)
    } else {
      optionsList.push(option)
    }
  }

  return optionsList
}

export const isExternalSearch = (search: Search | undefined): boolean => {
  return (
    (typeof search === 'string' && search === 'external') ||
    (typeof search === 'object' && search.external === true)
  )
}

export const isGlobalSearch = (search: Search | undefined): boolean => {
  return (
    (typeof search === 'string' && search === 'global') ||
    (typeof search === 'object' && search.global === true)
  )
}

export const getOptionLabel = (option: Option) =>
  typeof option === 'string' ? option : option.label

export const highlightSubstring = (string: string, query?: string) => {
  if (query) {
    const regex = new RegExp(query, 'gi')
    return string.replace(regex, (string) => `<strong>${string}</strong>`)
  }

  return string
}

export const isOptionDisabled = (option: Option | OptionsGroup | string) =>
  typeof option === 'string' ? false : option.disabled

export const isOptionSelected = (
  option: Option | OptionsGroup | string,
  selected: Option | null
) => {
  if (!selected) {
    return false
  }

  if (typeof option === 'string' && typeof selected === 'string') {
    return selected === option
  }

  if (typeof option !== 'string' && typeof selected !== 'string') {
    return selected.label === option.label
  }

  return false
}

export const getFirstOptionByLabel = (
  value: string,
  options: (Option | OptionsGroup)[]
): Option | null => {
  for (const option of options) {
    if (typeof option !== 'string' && 'options' in option && Array.isArray(option.options)) {
      const found = getFirstOptionByLabel(value, option.options)
      if (found) {
        return found
      }
    }

    if (getOptionLabel(option).toLowerCase() === value.toLowerCase()) {
      return option
    }
  }

  return null
}

export const getFirstOptionByValue = (
  value: number,
  options: (Option | OptionsGroup)[]
): Option | null => {
  for (const option of options) {
    if (typeof option !== 'string' && 'options' in option && Array.isArray(option.options)) {
      const found = getFirstOptionByValue(value, option.options)
      if (found) {
        return found
      }
    }

    if (typeof option !== 'string' && 'value' in option && option.value === value) {
      return option
    }
  }

  return null
}
