/* eslint-disable  @typescript-eslint/no-explicit-any */
import startsWith from 'lodash/startsWith'
import orderBy from 'lodash/orderBy'
import toLower from 'lodash/toLower'
import filter from 'lodash/filter'
import split from 'lodash/split'
import size from 'lodash/size'
import some from 'lodash/some'

type Item = { [key: string]: any }

type Func = <T extends Item, K extends keyof T>(
  items: Array<T>,
  key: K,
  query: string,
) => Array<T>

const startsWithIgnore = (word: string, target: string) => (
  startsWith(toLower(word), toLower(target))
)

const hasManyWords = (word: string) => size(split(word, ' ')) > 1

const atLeastOneWordStartsWith = (word: string, target: string) => {
  const words = split(word, ' ')
  return some(words, (w) => startsWithIgnore(w, target))
}

const filterMatching: Func = (
  items,
  key,
  query,
) => (
  filter(items, (item) => {
    const value = item[key]
    if (startsWithIgnore(value, query)) return true
    if (hasManyWords(value)) return atLeastOneWordStartsWith(value, query)
    return false
  })
)

const sortMatching: Func = (
  items,
  key,
  query,
) => (
  orderBy(
    items,
    (item) => {
      if (item[key] === query) return 10
      if (startsWithIgnore(item[key], query)) {
        return 5
      }
      return 0
    },
    'desc',
  )
)

export const matchSort: Func = (
  items,
  key,
  query,
) => {
  if (!query) return items

  const filteredItems = filterMatching(
    items,
    key,
    query,
  )

  return sortMatching(
    filteredItems,
    key,
    query,
  )
}
