import type { Ref } from 'vue'
import { computed, shallowRef } from 'vue'

import type { RawValueType } from '../BaseSelect'
import type { DefaultOptionType, LabelInValueType } from '../Select'

/**
 * Cache `value` related LabeledValue & options.
 */
export default (
  labeledValues: Ref<LabelInValueType[]>,
  valueOptions: Ref<Map<RawValueType, DefaultOptionType>>,
): [Ref<LabelInValueType[]>, (val: RawValueType) => DefaultOptionType] => {
  const cacheRef = shallowRef({
    values: new Map<RawValueType, LabelInValueType>(),
    options: new Map<RawValueType, DefaultOptionType>(),
  })

  const filledLabeledValues = computed(() => {
    const { values: prevValueCache, options: prevOptionCache } = cacheRef.value

    // Fill label by cache
    const patchedValues = labeledValues.value.map((item) => {
      if (item.label === undefined) {
        return {
          ...item,
          label: prevValueCache.get(item.value)?.label,
        }
      }

      return item
    })

    // Refresh cache
    const valueCache = new Map<RawValueType, LabelInValueType>()
    const optionCache = new Map<RawValueType, DefaultOptionType>()

    patchedValues.forEach((item) => {
      valueCache.set(item.value, item)
      optionCache.set(
        item.value,
        valueOptions.value.get(item.value) || prevOptionCache.get(item.value),
      )
    })

    cacheRef.value.values = valueCache
    cacheRef.value.options = optionCache

    return patchedValues
  })

  const getOption = (val: RawValueType) =>
    valueOptions.value.get(val) || cacheRef.value.options.get(val)

  return [filledLabeledValues, getOption]
}
