export function createCachedGetter(fetcher, ttlMs = 0) {
  let hasValue = false
  let cachedValue = null
  let expiresAt = 0
  let inflight = null

  async function run(options = {}) {
    const force = Boolean(options?.force)
    const now = Date.now()

    if (!force && hasValue && now < expiresAt) {
      return cachedValue
    }

    if (!force && inflight) {
      return inflight
    }

    inflight = Promise.resolve()
      .then(() => fetcher())
      .then((data) => {
        cachedValue = data
        hasValue = true
        const ttl = Math.max(0, Number(ttlMs) || 0)
        expiresAt = Date.now() + ttl
        return data
      })
      .finally(() => {
        inflight = null
      })

    return inflight
  }

  function clear() {
    hasValue = false
    cachedValue = null
    expiresAt = 0
    inflight = null
  }

  return {
    run,
    clear,
  }
}
