"use strict"
var __createBinding =
  (this && this.__createBinding) ||
  (Object.create
    ? function (o, m, k, k2) {
        if (k2 === undefined) k2 = k
        Object.defineProperty(o, k2, {
          enumerable: true,
          get: function () {
            return m[k]
          }
        })
      }
    : function (o, m, k, k2) {
        if (k2 === undefined) k2 = k
        o[k2] = m[k]
      })
var __setModuleDefault =
  (this && this.__setModuleDefault) ||
  (Object.create
    ? function (o, v) {
        Object.defineProperty(o, "default", { enumerable: true, value: v })
      }
    : function (o, v) {
        o["default"] = v
      })
var __importStar =
  (this && this.__importStar) ||
  function (mod) {
    if (mod && mod.__esModule) return mod
    var result = {}
    if (mod != null)
      for (var k in mod)
        if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k))
          __createBinding(result, mod, k)
    __setModuleDefault(result, mod)
    return result
  }
Object.defineProperty(exports, "__esModule", { value: true })
exports.cachedInvalidate_ = exports.cachedInvalidate = void 0
const Clock_1 = require("../Clock")
const Tp = __importStar(require("../Collections/Immutable/Tuple"))
const Function_1 = require("../Function")
const O = __importStar(require("../Option"))
const P = __importStar(require("../Promise"))
const Ref = __importStar(require("../RefM"))
const core = __importStar(require("./core"))
const die = __importStar(require("./die"))
const Do = __importStar(require("./do"))
const environment_1 = require("./environment")
const uninterruptibleMask = __importStar(require("./interruption"))
const map = __importStar(require("./map"))
const tap = __importStar(require("./tap"))
const to = __importStar(require("./to"))
/**
 * Returns an effect that, if evaluated, will return the cached result of
 * this effect. Cached results will expire after `timeToLive` duration. In
 * addition, returns an effect that can be used to invalidate the current
 * cached value before the `timeToLive` duration expires.
 *
 * @dataFirst cachedInvalidate_
 */
function cachedInvalidate(ttl, __trace) {
  return (fa) => cachedInvalidate_(fa, ttl)
}
exports.cachedInvalidate = cachedInvalidate
/**
 * Returns an effect that, if evaluated, will return the cached result of
 * this effect. Cached results will expire after `timeToLive` duration. In
 * addition, returns an effect that can be used to invalidate the current
 * cached value before the `timeToLive` duration expires.
 */
function cachedInvalidate_(fa, ttl, __trace) {
  return map.map_(
    Do.bind_(
      Do.bind_(Do.do, "r", () => (0, environment_1.environment)()),
      "cache",
      () => Ref.makeRefM(O.none)
    ),
    ({ cache, r }) =>
      Tp.tuple(core.provideAll_(get(fa, ttl, cache), r), invalidate(cache)),
    __trace
  )
}
exports.cachedInvalidate_ = cachedInvalidate_
function invalidate(cache) {
  return cache.set(O.none)
}
function compute(fa, ttl, start) {
  return map.map_(
    tap.tap_(
      Do.bind_(Do.do, "p", () => P.make()),
      ({ p }) => to.to_(fa, p)
    ),
    ({ p }) => O.some(Tp.tuple(start + ttl, p))
  )
}
function get(fa, ttl, cache) {
  return uninterruptibleMask.uninterruptibleMask(({ restore }) =>
    core.chain_(Clock_1.currentTime, (time) =>
      core.chain_(
        Ref.updateSomeAndGet((o) =>
          O.fold(
            () => O.some(compute(fa, ttl, time)),
            ({ tuple: [end] }) =>
              end - time <= 0 ? O.some(compute(fa, ttl, time)) : O.none
          )(o)
        )(cache),
        (a) => (a._tag === "None" ? die.die("bug") : restore(P.await(a.value.get(1))))
      )
    )
  )
}
//# sourceMappingURL=cachedInvalidate.js.map
