"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const Equal = require("./dist-esm-Equal.cjs");
const Function = require("./dist-esm-Function.cjs");
const GlobalValue = require("./dist-esm-GlobalValue.cjs");
const Hash = require("./dist-esm-Hash.cjs");
const Inspectable = require("./dist-esm-Inspectable.cjs");
const Pipeable = require("./dist-esm-Pipeable.cjs");
const Predicate = require("./dist-esm-Predicate.cjs");
const effectable = require("./dist-esm-internal-effectable.cjs");
const TagTypeId = /* @__PURE__ */ Symbol.for("effect/Context/Tag");
const ReferenceTypeId = /* @__PURE__ */ Symbol.for("effect/Context/Reference");
const STMSymbolKey = "effect/STM";
const STMTypeId = /* @__PURE__ */ Symbol.for(STMSymbolKey);
const TagProto = {
  ...effectable.EffectPrototype,
  _op: "Tag",
  [STMTypeId]: effectable.effectVariance,
  [TagTypeId]: {
    _Service: (_) => _,
    _Identifier: (_) => _
  },
  toString() {
    return Inspectable.format(this.toJSON());
  },
  toJSON() {
    return {
      _id: "Tag",
      key: this.key,
      stack: this.stack
    };
  },
  [Inspectable.NodeInspectSymbol]() {
    return this.toJSON();
  },
  of(self) {
    return self;
  },
  context(self) {
    return make(this, self);
  }
};
const ReferenceProto = {
  ...TagProto,
  [ReferenceTypeId]: ReferenceTypeId
};
const Tag = (id) => () => {
  const limit = Error.stackTraceLimit;
  Error.stackTraceLimit = 2;
  const creationError = new Error();
  Error.stackTraceLimit = limit;
  function TagClass() {
  }
  Object.setPrototypeOf(TagClass, TagProto);
  TagClass.key = id;
  Object.defineProperty(TagClass, "stack", {
    get() {
      return creationError.stack;
    }
  });
  return TagClass;
};
const Reference = () => (id, options) => {
  const limit = Error.stackTraceLimit;
  Error.stackTraceLimit = 2;
  const creationError = new Error();
  Error.stackTraceLimit = limit;
  function ReferenceClass() {
  }
  Object.setPrototypeOf(ReferenceClass, ReferenceProto);
  ReferenceClass.key = id;
  ReferenceClass.defaultValue = options.defaultValue;
  Object.defineProperty(ReferenceClass, "stack", {
    get() {
      return creationError.stack;
    }
  });
  return ReferenceClass;
};
const TypeId = /* @__PURE__ */ Symbol.for("effect/Context");
const ContextProto = {
  [TypeId]: {
    _Services: (_) => _
  },
  [Equal.symbol](that) {
    if (isContext(that)) {
      if (this.unsafeMap.size === that.unsafeMap.size) {
        for (const k of this.unsafeMap.keys()) {
          if (!that.unsafeMap.has(k) || !Equal.equals(this.unsafeMap.get(k), that.unsafeMap.get(k))) {
            return false;
          }
        }
        return true;
      }
    }
    return false;
  },
  [Hash.symbol]() {
    return Hash.cached(this, Hash.number(this.unsafeMap.size));
  },
  pipe() {
    return Pipeable.pipeArguments(this, arguments);
  },
  toString() {
    return Inspectable.format(this.toJSON());
  },
  toJSON() {
    return {
      _id: "Context",
      services: Array.from(this.unsafeMap).map(Inspectable.toJSON)
    };
  },
  [Inspectable.NodeInspectSymbol]() {
    return this.toJSON();
  }
};
const makeContext = (unsafeMap) => {
  const context = Object.create(ContextProto);
  context.unsafeMap = unsafeMap;
  return context;
};
const serviceNotFoundError = (tag) => {
  const error = new Error(`Service not found${tag.key ? `: ${String(tag.key)}` : ""}`);
  if (tag.stack) {
    const lines = tag.stack.split("\n");
    if (lines.length > 2) {
      const afterAt = lines[2].match(/at (.*)/);
      if (afterAt) {
        error.message = error.message + ` (defined at ${afterAt[1]})`;
      }
    }
  }
  if (error.stack) {
    const lines = error.stack.split("\n");
    lines.splice(1, 3);
    error.stack = lines.join("\n");
  }
  return error;
};
const isContext = (u) => Predicate.hasProperty(u, TypeId);
const make = (tag, service) => makeContext(/* @__PURE__ */ new Map([[tag.key, service]]));
const add = /* @__PURE__ */ Function.dual(3, (self, tag, service) => {
  const map = new Map(self.unsafeMap);
  map.set(tag.key, service);
  return makeContext(map);
});
const defaultValueCache = /* @__PURE__ */ GlobalValue.globalValue("effect/Context/defaultValueCache", () => /* @__PURE__ */ new Map());
const getDefaultValue = (tag) => {
  if (defaultValueCache.has(tag.key)) {
    return defaultValueCache.get(tag.key);
  }
  const value = tag.defaultValue();
  defaultValueCache.set(tag.key, value);
  return value;
};
const unsafeGetReference = (self, tag) => {
  return self.unsafeMap.has(tag.key) ? self.unsafeMap.get(tag.key) : getDefaultValue(tag);
};
const unsafeGet = /* @__PURE__ */ Function.dual(2, (self, tag) => {
  if (!self.unsafeMap.has(tag.key)) {
    if (ReferenceTypeId in tag) return getDefaultValue(tag);
    throw serviceNotFoundError(tag);
  }
  return self.unsafeMap.get(tag.key);
});
exports.ContextProto = ContextProto;
exports.Reference = Reference;
exports.ReferenceProto = ReferenceProto;
exports.ReferenceTypeId = ReferenceTypeId;
exports.STMTypeId = STMTypeId;
exports.Tag = Tag;
exports.TagProto = TagProto;
exports.TagTypeId = TagTypeId;
exports.TypeId = TypeId;
exports.add = add;
exports.isContext = isContext;
exports.make = make;
exports.makeContext = makeContext;
exports.unsafeGet = unsafeGet;
exports.unsafeGetReference = unsafeGetReference;
//# sourceMappingURL=dist-esm-internal-context.cjs.map
