"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
var __typeError = (msg) => {
  throw TypeError(msg);
};
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 __using = (stack, value, async) => {
  if (value != null) {
    if (typeof value !== "object" && typeof value !== "function") __typeError("Object expected");
    var dispose, inner;
    if (async) dispose = value[__knownSymbol("asyncDispose")];
    if (dispose === void 0) {
      dispose = value[__knownSymbol("dispose")];
      if (async) inner = dispose;
    }
    if (typeof dispose !== "function") __typeError("Object not disposable");
    if (inner) dispose = function() {
      try {
        inner.call(this);
      } catch (e) {
        return Promise.reject(e);
      }
    };
    stack.push([async, dispose, value]);
  } else if (async) {
    stack.push([async]);
  }
  return value;
};
var __callDispose = (stack, error, hasError) => {
  var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
    return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
  };
  var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e);
  var next = (it) => {
    while (it = stack.pop()) {
      try {
        var result = it[1] && it[1].call(it[2]);
        if (it[0]) return Promise.resolve(result).then(next, (e) => (fail(e), next()));
      } catch (e) {
        fail(e);
      }
    }
    if (hasError) throw error;
  };
  return next();
};
var TransactionalInteractionServer_exports = {};
__export(TransactionalInteractionServer_exports, {
  TransactionalInteractionServer: () => TransactionalInteractionServer
});
module.exports = __toCommonJS(TransactionalInteractionServer_exports);
var import_ActionTracer = require("#behavior/context/ActionTracer.js");
var import_NodeActivity = require("#behavior/context/NodeActivity.js");
var import_OfflineContext = require("#behavior/context/server/OfflineContext.js");
var import_OnlineContext = require("#behavior/context/server/OnlineContext.js");
var import_access_control = require("#clusters/access-control");
var import_EndpointServer = require("#endpoint/EndpointServer.js");
var import_EndpointLifecycle = require("#endpoint/properties/EndpointLifecycle.js");
var import_general = require("#general");
var import_protocol = require("#protocol");
var import_AccessControlServer = require("../../behaviors/access-control/AccessControlServer.js");
/**
 * @license
 * Copyright 2022-2024 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const activityKey = Symbol("activity");
const AclClusterId = import_access_control.AccessControlCluster.id;
const AclAttributeId = import_access_control.AccessControlCluster.attributes.acl.id;
class TransactionalInteractionServer extends import_protocol.InteractionServer {
  #endpointStructure;
  #changeListener;
  #endpoint;
  #activity;
  #newActivityBlocked = false;
  #aclServer;
  #aclUpdateIsDelayed = false;
  static async create(endpoint, sessions) {
    const structure = new import_protocol.InteractionEndpointStructure();
    return new TransactionalInteractionServer(endpoint, {
      sessions,
      structure,
      subscriptionOptions: endpoint.state.network.subscriptionOptions,
      maxPathsPerInvoke: endpoint.state.basicInformation.maxPathsPerInvoke,
      initiateExchange: (address, protocolId) => endpoint.env.get(import_protocol.ExchangeManager).initiateExchange(address, protocolId)
    });
  }
  constructor(endpoint, context) {
    super(context);
    const { structure } = context;
    this.#activity = endpoint.env.get(import_NodeActivity.NodeActivity);
    this.#endpoint = endpoint;
    this.#endpointStructure = structure;
    this.#updateStructure();
    this.#changeListener = (type) => {
      switch (type) {
        case import_EndpointLifecycle.EndpointLifecycle.Change.PartsReady:
        case import_EndpointLifecycle.EndpointLifecycle.Change.ClientsChanged:
        case import_EndpointLifecycle.EndpointLifecycle.Change.ServersChanged:
        case import_EndpointLifecycle.EndpointLifecycle.Change.Destroyed:
          this.#updateStructure();
          break;
      }
    };
    endpoint.lifecycle.changed.on(this.#changeListener);
  }
  async [Symbol.asyncDispose]() {
    this.#endpoint.lifecycle.changed.off(this.#changeListener);
    await this.close();
    this.#endpointStructure.close();
  }
  blockNewActivity() {
    this.#newActivityBlocked = true;
  }
  async onNewExchange(exchange) {
    var _stack = [];
    try {
      if (this.#newActivityBlocked || this.isClosing) {
        return;
      }
      const activity = __using(_stack, this.#activity.begin(`session#${exchange.session.id.toString(16)}`));
      exchange[activityKey] = activity;
      return new import_protocol.InteractionServerMessenger(exchange).handleRequest(this).finally(() => delete exchange[activityKey]);
    } catch (_) {
      var _error = _, _hasError = true;
    } finally {
      __callDispose(_stack, _error, _hasError);
    }
  }
  get aclServer() {
    if (this.#aclServer !== void 0) {
      return this.#aclServer;
    }
    const aclServer = this.#endpoint.act((agent) => agent.get(import_AccessControlServer.AccessControlServer));
    if (import_general.MaybePromise.is(aclServer)) {
      throw new import_general.InternalError("AccessControlServer should already be initialized.");
    }
    return this.#aclServer = aclServer;
  }
  async readAttribute(path, attribute, exchange, fabricFiltered, message, endpoint, offline = false) {
    const readAttribute = () => super.readAttribute(path, attribute, exchange, fabricFiltered, message, endpoint, offline);
    if (offline) {
      return import_OfflineContext.OfflineContext.act("offline-read", this.#activity, readAttribute);
    }
    return (0, import_OnlineContext.OnlineContext)({
      activity: exchange[activityKey],
      fabricFiltered,
      message,
      exchange,
      tracer: this.#tracer,
      actionType: import_ActionTracer.ActionTracer.ActionType.Read,
      endpoint,
      root: this.#endpoint
    }).act(readAttribute);
  }
  async readEvent(path, eventFilters, event, exchange, fabricFiltered, message, endpoint) {
    const readEvent = (context) => {
      if (!context.authorizedFor(event.readAcl, { cluster: path.clusterId })) {
        throw new import_protocol.AccessDeniedError(
          `Access to ${endpoint.number}/${import_general.Diagnostic.hex(path.clusterId)} denied on ${exchange.session.name}.`
        );
      }
      return super.readEvent(path, eventFilters, event, exchange, fabricFiltered, message, endpoint);
    };
    return (0, import_OnlineContext.OnlineContext)({
      activity: exchange[activityKey],
      fabricFiltered,
      message,
      exchange,
      tracer: this.#tracer,
      actionType: import_ActionTracer.ActionTracer.ActionType.Read,
      endpoint,
      root: this.#endpoint
    }).act(readEvent);
  }
  async handleWriteRequest(exchange, writeRequest, message) {
    const result = await super.handleWriteRequest(exchange, writeRequest, message);
    if (this.#aclUpdateIsDelayed) {
      this.aclServer.aclUpdateDelayed = false;
      this.#aclUpdateIsDelayed = false;
    }
    return result;
  }
  async writeAttribute(path, attribute, value, exchange, message, endpoint, timed = false, isListWrite) {
    const writeAttribute = () => super.writeAttribute(path, attribute, value, exchange, message, endpoint, timed, isListWrite);
    if (path.endpointId === 0 && path.clusterId === AclClusterId && path.attributeId === AclAttributeId) {
      this.aclServer.aclUpdateDelayed = true;
      this.#aclUpdateIsDelayed = true;
    } else if (this.#aclUpdateIsDelayed) {
      this.aclServer.aclUpdateDelayed = false;
      this.#aclUpdateIsDelayed = false;
    }
    return (0, import_OnlineContext.OnlineContext)({
      activity: exchange[activityKey],
      timed,
      message,
      exchange,
      fabricFiltered: true,
      tracer: this.#tracer,
      actionType: import_ActionTracer.ActionTracer.ActionType.Write,
      endpoint,
      root: this.#endpoint
    }).act(writeAttribute);
  }
  async invokeCommand(path, command, exchange, commandFields, message, endpoint, timed = false) {
    const invokeCommand = (context) => {
      if (!context.authorizedFor(command.invokeAcl, { cluster: path.clusterId })) {
        throw new import_protocol.AccessDeniedError(
          `Access to ${endpoint.number}/${import_general.Diagnostic.hex(path.clusterId)} denied on ${exchange.session.name}.`
        );
      }
      return super.invokeCommand(path, command, exchange, commandFields, message, endpoint, timed);
    };
    return (0, import_OnlineContext.OnlineContext)({
      activity: exchange[activityKey],
      command: true,
      timed,
      message,
      exchange,
      tracer: this.#tracer,
      actionType: import_ActionTracer.ActionTracer.ActionType.Invoke,
      endpoint,
      root: this.#endpoint
    }).act(invokeCommand);
  }
  get #tracer() {
    if (this.#endpoint.env.has(import_ActionTracer.ActionTracer)) {
      return this.#endpoint.env.get(import_ActionTracer.ActionTracer);
    }
  }
  #updateStructure() {
    if (this.#endpoint.lifecycle.isPartsReady) {
      this.#endpointStructure.initializeFromEndpoint(import_EndpointServer.EndpointServer.forEndpoint(this.#endpoint));
    }
  }
}
//# sourceMappingURL=TransactionalInteractionServer.js.map
