"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 : /* @__PURE__ */ 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 InteractionServer_exports = {};
__export(InteractionServer_exports, {
  InteractionServer: () => InteractionServer
});
module.exports = __toCommonJS(InteractionServer_exports);
var import_NodeActivity = require("#behavior/context/NodeActivity.js");
var import_access_control = require("#behaviors/access-control");
var import_general = require("@matter/general");
var import_model = require("@matter/model");
var import_protocol = require("@matter/protocol");
var import_types = require("@matter/types");
var import_OnlineServerInteraction = require("./OnlineServerInteraction.js");
var import_ServerSubscription = require("./ServerSubscription.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_general.Logger.get("InteractionServer");
function validateReadAttributesPath(path, isGroupSession = false) {
  if (isGroupSession) {
    throw new import_types.StatusResponseError("Illegal read request with group session", import_types.Status.InvalidAction);
  }
  const { clusterId, attributeId } = path;
  if (clusterId === void 0 && attributeId !== void 0) {
    if (!import_model.GLOBAL_IDS.has(attributeId)) {
      throw new import_types.StatusResponseError(
        `Illegal read request for wildcard cluster and non global attribute ${attributeId}`,
        import_types.Status.InvalidAction
      );
    }
  }
}
function validateReadEventPath(path, isGroupSession = false) {
  const { clusterId, eventId } = path;
  if (clusterId === void 0 && eventId !== void 0) {
    throw new import_types.StatusResponseError("Illegal read request with wildcard cluster ID", import_types.Status.InvalidAction);
  }
  if (isGroupSession) {
    throw new import_types.StatusResponseError("Illegal read request with group session", import_types.Status.InvalidAction);
  }
}
function clusterPathToId({ nodeId, endpointId, clusterId }) {
  return `${nodeId}/${endpointId}/${clusterId}`;
}
class InteractionServer {
  #lifetime;
  id = import_types.INTERACTION_PROTOCOL_ID;
  requiresSecureSession = true;
  #context;
  #nextSubscriptionId;
  #isClosing = false;
  #clientHandler;
  #subscriptionConfig;
  #maxPathsPerInvoke;
  #subscriptionEstablishmentStarted = (0, import_general.Observable)();
  #node;
  #activity;
  #newActivityBlocked = false;
  #aclServer;
  #serverInteraction;
  constructor(node, sessions) {
    this.#lifetime = node.construction.join("interaction server");
    this.#nextSubscriptionId = node.env.get(import_general.Crypto).randomUint32;
    this.#context = {
      sessions,
      exchangeManager: node.env.get(import_protocol.ExchangeManager)
    };
    this.#subscriptionConfig = import_ServerSubscription.ServerSubscriptionConfig.of(node.state.network.subscriptionOptions);
    this.#maxPathsPerInvoke = node.state.basicInformation.maxPathsPerInvoke ?? import_types.DEFAULT_MAX_PATHS_PER_INVOKE;
    this.#activity = node.env.get(import_NodeActivity.NodeActivity);
    this.#node = node;
    this.#serverInteraction = new import_OnlineServerInteraction.OnlineServerInteraction(node.protocol);
  }
  async [Symbol.asyncDispose]() {
    await this.close();
  }
  blockNewActivity() {
    this.#newActivityBlocked = true;
  }
  get isClosing() {
    return this.#isClosing;
  }
  get maxPathsPerInvoke() {
    return this.#maxPathsPerInvoke;
  }
  get subscriptionEstablishmentStarted() {
    return this.#subscriptionEstablishmentStarted;
  }
  async onNewExchange(exchange, message) {
    var _stack = [];
    try {
      if (this.#newActivityBlocked || this.isClosing) {
        return;
      }
      if (message.payloadHeader.messageType === import_protocol.MessageType.ReportData && this.clientHandler) {
        return await this.clientHandler.onNewExchange(exchange, message);
      }
      const activity = __using(_stack, this.#activity.begin(`session#${exchange.session.id.toString(16)}`));
      exchange[import_NodeActivity.NodeActivity.activityKey] = activity;
      try {
        return await new import_protocol.InteractionServerMessenger(exchange).handleRequest(this);
      } finally {
        delete exchange[import_NodeActivity.NodeActivity.activityKey];
      }
    } catch (_) {
      var _error = _, _hasError = true;
    } finally {
      __callDispose(_stack, _error, _hasError);
    }
  }
  get aclServer() {
    if (this.#aclServer !== void 0) {
      return this.#aclServer;
    }
    const aclServer = this.#node.act((agent) => agent.get(import_access_control.AccessControlServer));
    if (import_general.MaybePromise.is(aclServer)) {
      throw new import_general.InternalError("AccessControlServer should already be initialized.");
    }
    return this.#aclServer = aclServer;
  }
  get clientHandler() {
    return this.#clientHandler;
  }
  set clientHandler(clientHandler) {
    this.#clientHandler = clientHandler;
  }
  #prepareOnlineContext(exchange, message, fabricFiltered, timed = false) {
    return {
      activity: exchange[import_NodeActivity.NodeActivity.activityKey],
      fabricFiltered,
      timed,
      message,
      exchange,
      node: this.#node
    };
  }
  #checkSenderRevision(interactionModelRevision) {
    if (interactionModelRevision === void 0) {
      logger.debug("Sender omitted interaction model revision");
    } else if (interactionModelRevision > import_model.Specification.INTERACTION_MODEL_REVISION) {
      logger.debug(
        `Interaction model revision of sender ${interactionModelRevision} is higher than supported ${import_model.Specification.INTERACTION_MODEL_REVISION}`
      );
    }
  }
  /**
   * Returns an iterator that yields the data reports and events data for the given read request.
   */
  async *#executeReadInteraction(readRequest, exchange, message) {
    const readContext = this.#prepareOnlineContext(exchange, message, readRequest.isFabricFiltered);
    for await (const chunk of this.#serverInteraction.read(readRequest, readContext)) {
      for await (const report of chunk) {
        yield import_protocol.InteractionServerMessenger.convertServerInteractionReport(report);
      }
    }
  }
  async handleReadRequest(exchange, readRequest, message) {
    const {
      attributeRequests,
      eventRequests,
      isFabricFiltered,
      dataVersionFilters,
      eventFilters,
      interactionModelRevision
    } = readRequest;
    logger.debug(() => [
      "Read",
      import_protocol.Mark.INBOUND,
      exchange.via,
      import_general.Diagnostic.asFlags({ fabricFiltered: isFabricFiltered }),
      import_general.Diagnostic.dict({
        attributes: `${attributeRequests?.map((path) => this.#node.protocol.inspectPath(path)).join(", ") ?? "none"}${dataVersionFilters?.length ? ` with ${dataVersionFilters?.length} filters` : ""}`,
        events: `${eventRequests?.map((path) => this.#node.protocol.inspectPath(path)).join(", ") ?? "none"}${eventFilters?.length ? `, ${eventFilters?.length} filters` : ""}`
      })
    ]);
    this.#checkSenderRevision(interactionModelRevision);
    if (attributeRequests === void 0 && eventRequests === void 0) {
      return {
        dataReport: {
          interactionModelRevision: import_model.Specification.INTERACTION_MODEL_REVISION,
          suppressResponse: true
        }
      };
    }
    if (message.packetHeader.sessionType !== import_protocol.SessionType.Unicast) {
      throw new import_types.StatusResponseError(
        "Reads are only allowed on unicast sessions",
        // Means "No groups"
        import_types.Status.InvalidAction
      );
    }
    return {
      dataReport: {
        interactionModelRevision: import_model.Specification.INTERACTION_MODEL_REVISION,
        suppressResponse: true
      },
      payload: this.#executeReadInteraction(readRequest, exchange, message)
    };
  }
  async handleWriteRequest(exchange, writeRequest, messenger, message) {
    let { suppressResponse, writeRequests, moreChunkedMessages } = writeRequest;
    const { timedRequest, interactionModelRevision } = writeRequest;
    const sessionType = message.packetHeader.sessionType;
    logger.info(() => [
      "Write",
      import_protocol.Mark.INBOUND,
      exchange.via,
      import_general.Diagnostic.asFlags({ suppressResponse, moreChunkedMessages }),
      import_general.Diagnostic.weak(writeRequests.map((req) => this.#node.protocol.inspectPath(req.path)).join(", "))
    ]);
    if (moreChunkedMessages && suppressResponse) {
      throw new import_types.StatusResponseError(
        "MoreChunkedMessages and SuppressResponse cannot be used together in write messages",
        import_types.Status.InvalidAction
      );
    }
    this.#checkSenderRevision(interactionModelRevision);
    const receivedWithinTimedInteraction = exchange.hasActiveTimedInteraction();
    if (receivedWithinTimedInteraction && moreChunkedMessages) {
      throw new import_types.StatusResponseError(
        "Write Request action that is part of a Timed Write Interaction SHALL NOT be chunked.",
        import_types.Status.InvalidAction
      );
    }
    if (exchange.hasExpiredTimedInteraction()) {
      exchange.clearTimedInteraction();
      throw new import_types.StatusResponseError(`Timed request window expired. Decline write request.`, import_types.Status.Timeout);
    }
    if (timedRequest !== exchange.hasTimedInteraction()) {
      throw new import_types.StatusResponseError(
        `timedRequest flag of write interaction (${timedRequest}) mismatch with expected timed interaction (${receivedWithinTimedInteraction}).`,
        import_types.Status.TimedRequestMismatch
      );
    }
    if (receivedWithinTimedInteraction) {
      logger.debug("Write request for timed interaction on", exchange.channel.name);
      exchange.clearTimedInteraction();
      if (sessionType !== import_protocol.SessionType.Unicast) {
        throw new import_types.StatusResponseError(
          "Write requests are only allowed on unicast sessions when a timed interaction is running.",
          import_types.Status.InvalidAction
        );
      }
    }
    if (sessionType === import_protocol.SessionType.Group && !suppressResponse) {
      throw new import_types.StatusResponseError(
        "Write requests are only allowed as group casts when suppressResponse=true.",
        import_types.Status.InvalidAction
      );
    }
    let previousProcessedAttributePath;
    while (true) {
      const allResponses = new Array();
      let currentBatch = new Array();
      const processBatch = async () => {
        if (currentBatch.length === 0) {
          return;
        }
        const context = this.#prepareOnlineContext(
          exchange,
          message,
          true,
          // always fabric filtered
          receivedWithinTimedInteraction
        );
        const batchRequest = { ...writeRequest, writeRequests: currentBatch, suppressResponse: false };
        const batchResults = await this.#serverInteraction.write(batchRequest, context);
        if (batchResults) {
          allResponses.push(...batchResults);
        }
        currentBatch = [];
      };
      for (const request of writeRequests) {
        const { path } = request;
        const listIndex = path.listIndex;
        if (listIndex === null) {
          if (previousProcessedAttributePath?.endpointId !== path.endpointId || previousProcessedAttributePath?.clusterId !== path.clusterId || previousProcessedAttributePath?.attributeId !== path.attributeId) {
            await processBatch();
            allResponses.push({
              kind: "attr-status",
              path,
              status: import_types.Status.Busy
            });
            continue;
          }
        }
        currentBatch.push(request);
        if (path.endpointId !== void 0 && path.clusterId !== void 0 && path.attributeId !== void 0) {
          previousProcessedAttributePath = path;
        }
      }
      await processBatch();
      if (suppressResponse) {
        break;
      }
      const chunkResponse = {
        writeResponses: allResponses.map(({ path, status, clusterStatus }) => ({
          path,
          status: { status, clusterStatus }
        })),
        interactionModelRevision: import_model.Specification.INTERACTION_MODEL_REVISION
      };
      await messenger.sendWriteResponse(chunkResponse, {
        logContext: moreChunkedMessages ? "WriteResponse-chunk" : void 0
      });
      if (!moreChunkedMessages) {
        break;
      }
      const nextChunk = await messenger.readNextWriteRequest();
      const nextRequest = nextChunk.writeRequest;
      ({ writeRequests, moreChunkedMessages, suppressResponse } = nextRequest);
      logger.info(() => [
        "Write",
        import_protocol.Mark.INBOUND,
        exchange.via,
        import_general.Diagnostic.asFlags({ suppressResponse, moreChunkedMessages }),
        import_general.Diagnostic.weak(writeRequests.map((req) => this.#node.protocol.inspectPath(req.path)).join(", "))
      ]);
      if (suppressResponse) {
        throw new import_types.StatusResponseError(
          "Multiple chunked messages and SuppressResponse cannot be used together in write messages",
          import_types.Status.InvalidAction
        );
      }
    }
  }
  async handleSubscribeRequest(exchange, request, messenger, message) {
    const {
      minIntervalFloorSeconds,
      maxIntervalCeilingSeconds,
      attributeRequests,
      dataVersionFilters,
      eventRequests,
      eventFilters,
      keepSubscriptions,
      isFabricFiltered,
      interactionModelRevision
    } = request;
    logger.info(() => [
      "Subscribe",
      import_protocol.Mark.INBOUND,
      exchange.via,
      import_general.Diagnostic.asFlags({ fabricFiltered: isFabricFiltered, keepSubscriptions }),
      import_general.Diagnostic.dict({
        attributePaths: attributeRequests?.length,
        eventPaths: eventRequests?.length
      })
    ]);
    this.#checkSenderRevision(interactionModelRevision);
    if (message.packetHeader.sessionType !== import_protocol.SessionType.Unicast) {
      throw new import_types.StatusResponseError("Subscriptions are only allowed on unicast sessions", import_types.Status.InvalidAction);
    }
    import_protocol.NodeSession.assert(exchange.session, "Subscriptions are only implemented on secure sessions");
    const session = exchange.session;
    const fabric = session.fabric;
    if (fabric !== void 0 && !keepSubscriptions) {
      let clearedCount = 0;
      for (const sess of this.#context.sessions.sessions) {
        if (!import_protocol.PeerAddress.is(sess.peerAddress, session.peerAddress)) {
          continue;
        }
        for (const subscription2 of sess.subscriptions) {
          await subscription2.handlePeerCancel();
          clearedCount++;
        }
      }
      if (clearedCount > 0) {
        logger.debug(
          `Cleared ${clearedCount} subscriptions for Subscriber node ${session.peerNodeId} because keepSubscriptions=false`
        );
      }
    }
    if ((!Array.isArray(attributeRequests) || attributeRequests.length === 0) && (!Array.isArray(eventRequests) || eventRequests.length === 0)) {
      throw new import_types.StatusResponseError("No attributes or events requested", import_types.Status.InvalidAction);
    }
    logger.debug(() => [
      "Subscribe request details",
      import_protocol.Mark.INBOUND,
      exchange.via,
      import_general.Diagnostic.dict({
        attributes: attributeRequests?.length ? attributeRequests?.map((path) => this.#node.protocol.inspectPath(path)).join(", ") : void 0,
        dataVersionFilters: dataVersionFilters?.length ? dataVersionFilters.map(
          ({ path: { nodeId, endpointId, clusterId }, dataVersion }) => `${clusterPathToId({ nodeId, endpointId, clusterId })}=${dataVersion}`
        ).join(", ") : void 0,
        events: eventRequests?.length ? eventRequests.map((path) => this.#node.protocol.inspectPath(path)).join(", ") : void 0,
        eventFilters: eventFilters?.length ? eventFilters.map((filter) => `${filter.nodeId}/${filter.eventMin}`).join(", ") : void 0
      })
    ]);
    attributeRequests?.forEach((path) => validateReadAttributesPath(path));
    eventRequests?.forEach((path) => validateReadEventPath(path));
    if (minIntervalFloorSeconds < 0) {
      throw new import_types.StatusResponseError(
        "minIntervalFloorSeconds should be greater or equal to 0",
        import_types.Status.InvalidAction
      );
    }
    if (maxIntervalCeilingSeconds < 0) {
      throw new import_types.StatusResponseError(
        "maxIntervalCeilingSeconds should be greater or equal to 0",
        import_types.Status.InvalidAction
      );
    }
    if (maxIntervalCeilingSeconds < minIntervalFloorSeconds) {
      throw new import_types.StatusResponseError(
        "maxIntervalCeilingSeconds should be greater or equal to minIntervalFloorSeconds",
        import_types.Status.InvalidAction
      );
    }
    if (this.#nextSubscriptionId === 4294967295) this.#nextSubscriptionId = 0;
    const subscriptionId = this.#nextSubscriptionId++;
    this.#subscriptionEstablishmentStarted.emit(session.peerAddress);
    let subscription;
    try {
      subscription = await this.#establishSubscription(
        subscriptionId,
        request,
        messenger,
        session,
        exchange,
        message
      );
    } catch (error) {
      logger.warn(
        `Subscription ${import_protocol.Subscription.idStrOf(subscriptionId)} for session ${session.via}: Error while sending initial data reports:`,
        error instanceof import_general.MatterError ? error.message : error
      );
      const sre = import_types.StatusResponseError.of(error);
      if (sre && !(sre instanceof import_types.ReceivedStatusResponseError)) {
        logger.info(
          "Status",
          import_general.Diagnostic.strong(`${import_types.Status[sre.code]}(${sre.code})`),
          import_protocol.Mark.OUTBOUND,
          exchange.via,
          exchange.diagnostics,
          "Error:",
          import_general.Diagnostic.errorMessage(sre)
        );
        await messenger.sendStatus(sre.code, {
          logContext: {
            for: "I/SubscriptionSeed-Status"
          }
        });
      }
      await messenger.close();
      return;
    }
    const maxInterval = subscription.maxInterval;
    await messenger.send(
      import_protocol.MessageType.SubscribeResponse,
      import_types.TlvSubscribeResponse.encode({
        subscriptionId,
        maxInterval: import_general.Seconds.of(maxInterval),
        interactionModelRevision: import_model.Specification.INTERACTION_MODEL_REVISION
      }),
      {
        logContext: {
          ...import_protocol.Subscription.diagnosticOf(subscriptionId),
          maxInterval: import_general.Duration.format(maxInterval)
        }
      }
    );
    subscription.activate();
  }
  #initiateSubscriptionExchange(addressOrSession, protocolId) {
    if (addressOrSession instanceof import_protocol.Session) {
      return this.#context.exchangeManager.initiateExchangeForSession(addressOrSession, protocolId);
    }
    return this.#context.exchangeManager.initiateExchange(addressOrSession, protocolId);
  }
  async #establishSubscription(id, request, messenger, session, exchange, message) {
    const context = {
      session,
      node: this.#node,
      initiateExchange: (addressOrSession, protocolId) => this.#initiateSubscriptionExchange(addressOrSession, protocolId)
    };
    const subscription = new import_ServerSubscription.ServerSubscription({
      id,
      context,
      request,
      subscriptionOptions: this.#subscriptionConfig
    });
    const readContext = this.#prepareOnlineContext(exchange, message, request.isFabricFiltered);
    try {
      await subscription.sendInitialReport(messenger, readContext);
    } catch (error) {
      await subscription.close();
      throw error;
    }
    logger.notice(
      "Subscribe successful",
      import_protocol.Mark.OUTBOUND,
      exchange.via,
      exchange.diagnostics,
      import_general.Diagnostic.dict({
        ...import_protocol.Subscription.diagnosticOf(subscription),
        timing: `${import_general.Duration.format(subscription.minIntervalFloor)} - ${import_general.Duration.format(subscription.maxIntervalCeiling)} => ${import_general.Duration.format(subscription.maxInterval)}`,
        sendInterval: import_general.Duration.format(subscription.sendInterval)
      })
    );
    return subscription;
  }
  async establishFormerSubscription({
    subscriptionId,
    attributeRequests,
    eventRequests,
    isFabricFiltered,
    minIntervalFloor,
    maxIntervalCeiling,
    maxInterval,
    sendInterval
  }, session) {
    const exchange = this.#context.exchangeManager.initiateExchange(session.peerAddress, import_types.INTERACTION_PROTOCOL_ID);
    logger.info(
      `Reestablish subscription`,
      import_protocol.Mark.OUTBOUND,
      exchange.via,
      import_general.Diagnostic.dict({
        ...import_protocol.Subscription.diagnosticOf(subscriptionId),
        isFabricFiltered,
        maxInterval: import_general.Duration.format(maxInterval),
        sendInterval: import_general.Duration.format(sendInterval)
      })
    );
    const context = {
      session,
      node: this.#node,
      initiateExchange: (addressOrSession, protocolId) => this.#initiateSubscriptionExchange(addressOrSession, protocolId)
    };
    const subscription = new import_ServerSubscription.ServerSubscription({
      id: subscriptionId,
      context,
      request: {
        attributeRequests,
        eventRequests,
        isFabricFiltered,
        minIntervalFloorSeconds: import_general.Seconds.of(minIntervalFloor),
        maxIntervalCeilingSeconds: import_general.Seconds.of(maxIntervalCeiling)
      },
      subscriptionOptions: this.#subscriptionConfig,
      useAsMaxInterval: maxInterval,
      useAsSendInterval: sendInterval
    });
    const readContext = this.#prepareOnlineContext(exchange, void 0, isFabricFiltered);
    try {
      await subscription.sendInitialReport(
        new import_protocol.InteractionServerMessenger(exchange),
        readContext,
        true
        // Do not send status responses because we simulate that the subscription is still established
      );
      subscription.activate();
      logger.notice(
        `Subscription successfully reestablished`,
        import_protocol.Mark.OUTBOUND,
        exchange.via,
        exchange.diagnostics,
        import_general.Diagnostic.dict({
          ...import_protocol.Subscription.diagnosticOf(subscriptionId),
          timing: `${import_general.Duration.format(minIntervalFloor)} - ${import_general.Duration.format(maxIntervalCeiling)} => ${import_general.Duration.format(subscription.maxInterval)}`,
          sendInterval: import_general.Duration.format(subscription.sendInterval)
        })
      );
    } catch (error) {
      await subscription.close();
      throw error;
    }
    return subscription;
  }
  async handleInvokeRequest(exchange, request, messenger, message) {
    const { invokeRequests, timedRequest, suppressResponse, interactionModelRevision } = request;
    logger.info(() => [
      "Invoke",
      import_protocol.Mark.INBOUND,
      exchange.via,
      import_general.Diagnostic.asFlags({ suppressResponse, timedRequest }),
      import_general.Diagnostic.dict({
        invokes: invokeRequests.map(
          ({ commandPath: { endpointId, clusterId, commandId } }) => this.#node.protocol.inspectPath({ endpointId, clusterId, commandId })
        ).join(", ")
      })
    ]);
    this.#checkSenderRevision(interactionModelRevision);
    const receivedWithinTimedInteraction = exchange.hasActiveTimedInteraction();
    if (exchange.hasExpiredTimedInteraction()) {
      exchange.clearTimedInteraction();
      throw new import_types.StatusResponseError(`Timed request window expired. Decline invoke request.`, import_types.Status.Timeout);
    }
    if (timedRequest !== exchange.hasTimedInteraction()) {
      throw new import_types.StatusResponseError(
        `timedRequest flag of invoke interaction (${timedRequest}) mismatch with expected timed interaction (${receivedWithinTimedInteraction}).`,
        import_types.Status.TimedRequestMismatch
      );
    }
    if (receivedWithinTimedInteraction) {
      logger.debug("Invoke request for timed interaction on", exchange.channel.name);
      exchange.clearTimedInteraction();
      if (message.packetHeader.sessionType !== import_protocol.SessionType.Unicast) {
        throw new import_types.StatusResponseError(
          "Invoke requests are only allowed on unicast sessions when a timed interaction is running.",
          import_types.Status.InvalidAction
        );
      }
    }
    if (invokeRequests.length > this.#maxPathsPerInvoke) {
      throw new import_types.StatusResponseError(
        `Only ${this.#maxPathsPerInvoke} invoke requests are supported in one message. This message contains ${invokeRequests.length}`,
        import_types.Status.InvalidAction
      );
    }
    const context = this.#prepareOnlineContext(exchange, message, void 0, receivedWithinTimedInteraction);
    const isGroupSession = message.packetHeader.sessionType === import_protocol.SessionType.Group;
    const results = this.#serverInteraction.invoke(request, context);
    if (suppressResponse || isGroupSession) {
      for await (const _chunk of results) ;
      return;
    }
    const currentChunkResponses = new Array();
    const emptyInvokeResponse = {
      suppressResponse: false,
      // Deprecated but must be present
      interactionModelRevision: import_model.Specification.INTERACTION_MODEL_REVISION,
      invokeResponses: []
    };
    const emptyInvokeResponseLength = import_types.TlvInvokeResponseForSend.encode(emptyInvokeResponse).byteLength;
    let messageSize = emptyInvokeResponseLength;
    let chunkedTransmissionTerminated = false;
    const sendChunkIfNeeded = async (invokeResponse) => {
      const encodedInvokeResponse = import_types.TlvInvokeResponseData.encodeTlv(invokeResponse);
      const invokeResponseBytes = import_types.TlvAny.getEncodedByteLength(encodedInvokeResponse);
      if (messageSize + invokeResponseBytes > exchange.maxPayloadSize && currentChunkResponses.length > 0) {
        logger.debug(
          "Invoke (chunk)",
          import_protocol.Mark.OUTBOUND,
          exchange.via,
          import_general.Diagnostic.dict({ commands: currentChunkResponses.length })
        );
        const chunkResponse = {
          ...emptyInvokeResponse,
          invokeResponses: currentChunkResponses.map((r) => import_types.TlvInvokeResponseData.encodeTlv(r))
        };
        if (!await messenger.sendInvokeResponseChunk(chunkResponse)) {
          chunkedTransmissionTerminated = true;
          return;
        }
        currentChunkResponses.length = 0;
        messageSize = emptyInvokeResponseLength;
      }
      currentChunkResponses.push(invokeResponse);
      messageSize += invokeResponseBytes;
    };
    for await (const chunk of results) {
      if (chunkedTransmissionTerminated) {
        continue;
      }
      for (const data of chunk) {
        switch (data.kind) {
          case "cmd-response": {
            const { path: commandPath, commandRef, data: commandFields } = data;
            await sendChunkIfNeeded({
              command: {
                commandPath,
                commandFields,
                commandRef
              }
            });
            break;
          }
          case "cmd-status": {
            const { path, commandRef, status, clusterStatus } = data;
            await sendChunkIfNeeded({
              status: { commandPath: path, status: { status, clusterStatus }, commandRef }
            });
            break;
          }
        }
      }
    }
    if (!chunkedTransmissionTerminated) {
      if (currentChunkResponses.length > 0) {
        logger.debug(
          "Invoke (final)",
          import_protocol.Mark.OUTBOUND,
          exchange.via,
          import_general.Diagnostic.dict({ commands: currentChunkResponses.length })
        );
      }
      const finalResponse = {
        ...emptyInvokeResponse,
        invokeResponses: currentChunkResponses.map((r) => import_types.TlvInvokeResponseData.encodeTlv(r))
      };
      await messenger.sendInvokeResponse(finalResponse);
    }
  }
  handleTimedRequest(exchange, { timeout, interactionModelRevision }) {
    const interval = (0, import_general.Millis)(timeout);
    logger.debug(() => [
      "Timed request",
      import_protocol.Mark.INBOUND,
      exchange.via,
      import_general.Diagnostic.dict({
        interval: import_general.Duration.format(interval)
      })
    ]);
    this.#checkSenderRevision(interactionModelRevision);
    exchange.startTimedInteraction(interval);
  }
  async close() {
    this.#isClosing = true;
    this.#lifetime[Symbol.dispose]();
  }
}
//# sourceMappingURL=InteractionServer.js.map
