"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var RemoteActorContext_exports = {};
__export(RemoteActorContext_exports, {
  RemoteActorContext: () => RemoteActorContext
});
module.exports = __toCommonJS(RemoteActorContext_exports);
var import_general = require("#general");
var import_protocol = require("#protocol");
var import_types = require("#types");
var import_Contextual = require("../Contextual.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const exchangeCompleteEvents = /* @__PURE__ */ new WeakMap();
function RemoteActorContext(options) {
  let nodeProtocol;
  let accessLevelCache;
  const { exchange, message } = options;
  const session = exchange.session;
  import_protocol.SecureSession.assert(session);
  const fabric = session.fabric;
  const subject = session.subjectFor(message);
  const accessControl = fabric?.accessControl ?? new import_protocol.FabricAccessControl();
  const via = message ? import_protocol.Message.via(exchange, message) : exchange.via;
  return {
    /**
     * Operate on behalf of a remote actor.
     *
     * If the actor changes state, this may return a promise even if {@link actor} does not return a promise.
     */
    act(actor) {
      const context = this.open();
      let result;
      try {
        result = actor(context);
      } catch (e) {
        return context.reject(e);
      }
      return context.resolve(result);
    },
    /**
     * Create an online context.
     *
     * This context operates with a {@link Transaction} created via {@link Transaction.open} and the same rules
     * apply for lifecycle management using {@link Transaction.Finalization}.
     */
    open() {
      const tx = createTransaction("rw");
      return createContext(tx, {
        resolve: tx.resolve.bind(tx),
        reject: tx.reject.bind(tx)
      });
    },
    /**
     * Begin an operation with a read-only context.
     *
     * A read-only context offers simpler lifecycle semantics than a r/w OnlineContext but you must still close the
     * context after use to properly deregister activity.
     */
    beginReadOnly() {
      const tx = createTransaction("snapshot");
      return createContext(tx, {
        [Symbol.dispose]: tx[Symbol.dispose].bind(tx)
      });
    },
    [Symbol.toStringTag]: "RemoteActorContext"
  };
  function createTransaction(isolation) {
    const activity = options.activity?.frame(via);
    let tx;
    try {
      tx = import_general.Transaction.open(via, exchange, isolation);
      tx.onClose(close);
    } catch (e) {
      close?.();
      tx?.[Symbol.dispose]();
      throw e;
    }
    return tx;
    function close() {
      if (message) {
        import_Contextual.Contextual.setContextOf(message, void 0);
      }
      if (activity) {
        activity[Symbol.dispose]();
      }
    }
  }
  function createContext(transaction, methods) {
    if (session) {
      import_protocol.SecureSession.assert(session);
    }
    let interactionComplete;
    if (exchange !== void 0) {
      interactionComplete = exchangeCompleteEvents.get(exchange);
      if (interactionComplete === void 0) {
        interactionComplete = new import_general.AsyncObservable();
        exchangeCompleteEvents.set(exchange, interactionComplete);
      }
      const notifyInteractionComplete = () => {
        exchange.closing.off(notifyInteractionComplete);
        exchangeCompleteEvents.delete(exchange);
        if (context.interactionComplete?.isObserved) {
          context.interactionComplete.emit(context);
        }
      };
      exchange.closing.on(notifyInteractionComplete);
    }
    const context = {
      ...options,
      session,
      exchange,
      subject,
      largeMessage: exchange?.session.supportsLargeMessages,
      offline: false,
      fabric: fabric?.fabricIndex ?? import_types.FabricIndex.NO_FABRIC,
      transaction,
      interactionComplete,
      ...methods,
      // TODO - Matter 1.4 - add support for ARLs
      authorityAt(desiredAccessLevel, location) {
        if (location === void 0) {
          throw new import_general.InternalError("AccessControl.Location is required");
        }
        const cachedAccessLevels = accessLevelCache?.get(location);
        if (cachedAccessLevels !== void 0) {
          return cachedAccessLevels.includes(desiredAccessLevel) ? import_protocol.AccessControl.Authority.Granted : import_protocol.AccessControl.Authority.Unauthorized;
        }
        if (options.node === void 0) {
          throw new import_general.InternalError("OnlineContext initialized without node");
        }
        const accessLevels = accessControl.accessLevelsFor(context, location, aclEndpointContextFor(location));
        if (accessLevelCache === void 0) {
          accessLevelCache = /* @__PURE__ */ new Map();
        }
        accessLevelCache.set(location, accessLevels);
        return accessLevels.includes(desiredAccessLevel) ? import_protocol.AccessControl.Authority.Granted : import_protocol.AccessControl.Authority.Unauthorized;
      },
      get [import_Contextual.Contextual.context]() {
        return this;
      }
    };
    if (message) {
      import_Contextual.Contextual.setContextOf(message, context);
    }
    return context;
  }
  function aclEndpointContextFor({ endpoint: number }) {
    if (number === void 0) {
      throw new import_general.InternalError("Online location missing required endpoint number");
    }
    if (options.node === void 0) {
      throw new import_general.InternalError("Online context has no node defined");
    }
    if (nodeProtocol === void 0) {
      nodeProtocol = options.node.protocol;
    }
    const endpoint = nodeProtocol[number];
    if (endpoint !== void 0) {
      return endpoint;
    }
    return {
      id: number,
      deviceTypes: []
    };
  }
}
//# sourceMappingURL=RemoteActorContext.js.map
