import {
  ClockRef,
  PipeInspectableProto,
  _await,
  asSome,
  catchCause,
  clear,
  doneUnsafe,
  dual,
  exitIsFailure,
  exitIsSuccess,
  exitSucceed,
  failVoid,
  filterMap,
  fromInputUnsafe,
  get1 as get,
  infinity,
  isExit,
  isFinite,
  isNone,
  isSome,
  isZero,
  make7 as make,
  makeUnsafe1 as makeUnsafe,
  map1 as map,
  map5 as map2,
  merge,
  onExit,
  remove,
  servicesWith,
  set1 as set,
  size,
  succeed,
  succeed1 as succeed2,
  succeedNone,
  succeedSome,
  sync,
  toMillis,
  updateServices,
  void_,
  withFiber
} from "./index-w9j14fjm.js";
import {
  __export
} from "./index-sdksp5px.js";

// ../../node_modules/.bun/effect@https+++pkg.pr.new+Effect-TS+effect-smol+effect@8881a9b/node_modules/effect/dist/Cache.js
var exports_Cache = {};
__export(exports_Cache, {
  values: () => values,
  size: () => size2,
  set: () => set2,
  refresh: () => refresh,
  makeWith: () => makeWith,
  make: () => make2,
  keys: () => keys,
  invalidateWhen: () => invalidateWhen,
  invalidateAll: () => invalidateAll,
  invalidate: () => invalidate,
  has: () => has,
  getSuccess: () => getSuccess,
  getOption: () => getOption,
  get: () => get2,
  entries: () => entries
});
var TypeId = "~effect/Cache";
var makeWith = (options) => servicesWith((services) => {
  const self = Object.create(Proto);
  self.lookup = (key) => updateServices(options.lookup(key), (input) => merge(services, input));
  self.map = make();
  self.capacity = options.capacity;
  self.timeToLive = options.timeToLive ? (exit, key) => fromInputUnsafe(options.timeToLive(exit, key)) : defaultTimeToLive;
  return succeed2(self);
});
var make2 = (options) => makeWith({
  ...options,
  timeToLive: options.timeToLive ? () => options.timeToLive : defaultTimeToLive
});
var Proto = {
  ...PipeInspectableProto,
  [TypeId]: TypeId,
  toJSON() {
    return {
      _id: "Cache",
      capacity: this.capacity,
      map: this.map
    };
  }
};
var defaultTimeToLive = (_, _key) => infinity;
var get2 = /* @__PURE__ */ dual(2, (self, key) => withFiber((fiber) => {
  const oentry = get(self.map, key);
  if (isSome(oentry) && !hasExpired(oentry.value, fiber)) {
    remove(self.map, key);
    set(self.map, key, oentry.value);
    return _await(oentry.value.deferred);
  }
  const deferred = makeUnsafe();
  const entry = {
    expiresAt: undefined,
    deferred
  };
  set(self.map, key, entry);
  if (Number.isFinite(self.capacity)) {
    checkCapacity(self);
  }
  return onExit(self.lookup(key), (exit) => {
    doneUnsafe(deferred, exit);
    const ttl = self.timeToLive(exit, key);
    if (isFinite(ttl)) {
      entry.expiresAt = fiber.getRef(ClockRef).currentTimeMillisUnsafe() + toMillis(ttl);
    } else if (isZero(ttl)) {
      remove(self.map, key);
    }
    return void_;
  });
}));
var hasExpired = (entry, fiber) => {
  if (entry.expiresAt === undefined) {
    return false;
  }
  return fiber.getRef(ClockRef).currentTimeMillisUnsafe() >= entry.expiresAt;
};
var checkCapacity = (self) => {
  let diff = size(self.map) - self.capacity;
  if (diff <= 0)
    return;
  for (const [key] of self.map) {
    remove(self.map, key);
    diff--;
    if (diff === 0)
      return;
  }
};
var getOption = /* @__PURE__ */ dual(2, (self, key) => withFiber((fiber) => {
  const entry = getImpl(self, key, fiber);
  return entry ? asSome(_await(entry.deferred)) : succeedNone;
}));
var getImpl = (self, key, fiber, isRead = true) => {
  const oentry = get(self.map, key);
  if (isNone(oentry)) {
    return;
  } else if (hasExpired(oentry.value, fiber)) {
    remove(self.map, key);
    return;
  } else if (isRead) {
    remove(self.map, key);
    set(self.map, key, oentry.value);
  }
  return oentry.value;
};
var getSuccess = /* @__PURE__ */ dual(2, (self, key) => withFiber((fiber) => {
  const exit = getImpl(self, key, fiber)?.deferred.effect;
  if (exit && exitIsSuccess(exit)) {
    return succeedSome(exit.value);
  }
  return succeedNone;
}));
var set2 = /* @__PURE__ */ dual(3, (self, key, value) => withFiber((fiber) => {
  const exit = exitSucceed(value);
  const deferred = makeUnsafe();
  doneUnsafe(deferred, exit);
  const ttl = self.timeToLive(exit, key);
  if (isZero(ttl)) {
    remove(self.map, key);
    return void_;
  }
  set(self.map, key, {
    deferred,
    expiresAt: isFinite(ttl) ? fiber.getRef(ClockRef).currentTimeMillisUnsafe() + toMillis(ttl) : undefined
  });
  checkCapacity(self);
  return void_;
}));
var has = /* @__PURE__ */ dual(2, (self, key) => withFiber((fiber) => {
  const oentry = getImpl(self, key, fiber, false);
  return succeed2(oentry !== undefined);
}));
var invalidate = /* @__PURE__ */ dual(2, (self, key) => sync(() => {
  remove(self.map, key);
}));
var invalidateWhen = /* @__PURE__ */ dual(3, (self, key, f) => withFiber((fiber) => {
  const oentry = getImpl(self, key, fiber, false);
  if (oentry === undefined) {
    return succeed2(false);
  }
  return _await(oentry.deferred).pipe(map2((value) => {
    if (f(value)) {
      remove(self.map, key);
      return true;
    }
    return false;
  }), catchCause(() => succeed2(false)));
}));
var refresh = /* @__PURE__ */ dual(2, (self, key) => withFiber((fiber) => {
  const deferred = makeUnsafe();
  const entry = {
    expiresAt: undefined,
    deferred
  };
  const existing = getImpl(self, key, fiber, false) !== undefined;
  if (!existing) {
    set(self.map, key, entry);
    checkCapacity(self);
  }
  return onExit(self.lookup(key), (exit) => {
    doneUnsafe(deferred, exit);
    const ttl = self.timeToLive(exit, key);
    if (isZero(ttl)) {
      remove(self.map, key);
      return void_;
    }
    entry.expiresAt = isFinite(ttl) ? fiber.getRef(ClockRef).currentTimeMillisUnsafe() + toMillis(ttl) : undefined;
    if (existing) {
      set(self.map, key, entry);
    }
    return void_;
  });
}));
var invalidateAll = (self) => sync(() => {
  clear(self.map);
});
var size2 = (self) => sync(() => size(self.map));
var keys = (self) => withFiber((fiber) => {
  const now = fiber.getRef(ClockRef).currentTimeMillisUnsafe();
  return succeed2(filterMap(self.map, ([key, entry]) => {
    if (entry.expiresAt === undefined || entry.expiresAt > now) {
      return succeed(key);
    }
    remove(self.map, key);
    return failVoid;
  }));
});
var values = (self) => map2(entries(self), map(([, value]) => value));
var entries = (self) => withFiber((fiber) => {
  const now = fiber.getRef(ClockRef).currentTimeMillisUnsafe();
  return succeed2(filterMap(self.map, ([key, entry]) => {
    if (entry.expiresAt === undefined || entry.expiresAt > now) {
      const exit = entry.deferred.effect;
      return !isExit(exit) || exitIsFailure(exit) ? failVoid : succeed([key, exit.value]);
    }
    remove(self.map, key);
    return failVoid;
  }));
});

export { make2 as make, get2 as get, exports_Cache };
