export type LruCache<K, V> = Readonly<{
  get: (key: K) => V | undefined
  set: (key: K, value: V) => void
  clear: () => void
  size: () => number
}>

export function createLruCache<K, V>(maxEntries: number): LruCache<K, V> {
  const limit =
    Number.isFinite(maxEntries) && maxEntries > 0 ? Math.floor(maxEntries) : 0
  const map = new Map<K, V>()

  const get = (key: K) => {
    if (!limit) return undefined
    const value = map.get(key)
    if (value === undefined) return undefined
    // Refresh recency.
    map.delete(key)
    map.set(key, value)
    return value
  }

  const set = (key: K, value: V) => {
    if (!limit) return
    if (map.has(key)) map.delete(key)
    map.set(key, value)
    if (map.size > limit) {
      const first = map.keys().next().value
      if (first !== undefined) map.delete(first)
    }
  }

  const clear = () => {
    map.clear()
  }

  const size = () => map.size

  return { get, set, clear, size }
}
