"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 MessageChannel_exports = {};
__export(MessageChannel_exports, {
  MessageChannel: () => MessageChannel
});
module.exports = __toCommonJS(MessageChannel_exports);
var import_MessageCodec = require("#codec/MessageCodec.js");
var import_Mark = require("#common/Mark.js");
var import_general = require("@matter/general");
var import_MRP = require("./MRP.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = new import_general.Logger("MessageChannel");
class MessageChannel {
  // When the session is supporting MRP and the channel is not reliable, use MRP handling
  constructor(channel, session, onClose) {
    this.session = session;
    this.#channel = channel;
    if ((0, import_general.isIpNetworkChannel)(channel)) {
      this.#isIpNetworkChannel = true;
      this.#observeChannelAddress(channel);
    }
    this.#onClose = onClose;
  }
  #channel;
  #networkAddressChanged = (0, import_general.Observable)();
  #isIpNetworkChannel = false;
  #channelAddressObserver;
  closed = false;
  #onClose;
  set onClose(callback) {
    this.#onClose = callback;
  }
  /** Is the underlying transport reliable? */
  get isReliable() {
    return this.#channel.isReliable;
  }
  /**
   * Does the underlying transport support large messages?
   * This is only true for TCP channels currently.
   */
  get supportsLargeMessages() {
    return this.type === "tcp";
  }
  get type() {
    return this.#channel.type;
  }
  /**
   * Max Payload size of the exchange which bases on the maximum payload size of the channel. The full encoded matter
   * message payload sent here can be as huge as allowed by the channel.
   */
  get maxPayloadSize() {
    return this.#channel.maxPayloadSize;
  }
  async send(message, options) {
    const { exchange, addressOverride } = options ?? {};
    let { logContext } = options ?? {};
    if (addressOverride && this.#isIpNetworkChannel) {
      logContext = {
        ...logContext,
        address: import_general.ServerAddress.urlFor(addressOverride)
      };
    }
    logger.debug("Message", import_Mark.Mark.OUTBOUND, import_MessageCodec.Message.diagnosticsOf(exchange ?? this.session, message, logContext));
    const packet = this.session.encode(message);
    const bytes = import_MessageCodec.MessageCodec.encodePacket(packet);
    if (bytes.byteLength > this.maxPayloadSize) {
      logger.warn(
        `Matter message to send to ${this.name} is ${bytes.byteLength}bytes long, which is larger than the maximum allowed size of ${this.maxPayloadSize}. This only works if both nodes support it.`
      );
    }
    if (addressOverride && this.#isIpNetworkChannel) {
      return await this.#channel.send(bytes, addressOverride);
    }
    return await this.#channel.send(bytes);
  }
  get name() {
    return import_general.Diagnostic.via(`${this.session.via}@${this.#channel.name}`);
  }
  get networkAddress() {
    if (this.#isIpNetworkChannel) {
      return this.#channel.networkAddress;
    }
  }
  set networkAddress(networkAddress) {
    if (this.#isIpNetworkChannel) {
      this.#channel.networkAddress = networkAddress;
    }
  }
  get networkAddressChanged() {
    return this.#networkAddressChanged;
  }
  get channel() {
    return this.#channel;
  }
  /**
   * Sync the addresses for IP network channels and replace channel if the IPs change
   * If the channel is on a non ip network then the call is basically ignored
   * We already use a new naming here which will be more used in future, so yes inconsistency in naming is ok for now
   * TODO refactor this out again and remove the address from the channel
   */
  set socket(channel) {
    if (this.closed || !this.#isIpNetworkChannel || !(0, import_general.isIpNetworkChannel)(channel) || channel.type !== "udp" || this.#channel.type !== "udp") {
      return;
    }
    const addressChanged = !(0, import_general.sameIpNetworkChannel)(channel, this.#channel);
    this.#observeChannelAddress(channel);
    this.#channel = channel;
    if (addressChanged) {
      logger.debug(`Updated address of channel to`, this.name);
      this.#networkAddressChanged.emit(channel.networkAddress);
    }
  }
  #observeChannelAddress(newChannel) {
    if (this.#channelAddressObserver && this.#isIpNetworkChannel) {
      this.#channel.networkAddressChanged.off(this.#channelAddressObserver);
    }
    this.#channelAddressObserver = (networkAddress) => {
      logger.debug(`Network address of UDP Channel changed to ${import_general.ServerAddress.urlFor(networkAddress)}`);
      this.#networkAddressChanged.emit(networkAddress);
    };
    newChannel.networkAddressChanged.on(this.#channelAddressObserver);
  }
  async close() {
    const wasAlreadyClosed = this.closed;
    this.closed = true;
    await this.#channel.close();
    if (!wasAlreadyClosed) {
      await this.#onClose?.();
    }
  }
  calculateMaximumPeerResponseTime(peerSessionParameters, localSessionParameters, expectedProcessingTime, includeMaximumSendingTime) {
    return import_MRP.MRP.maxPeerResponseTimeOf({
      peerSessionParameters: includeMaximumSendingTime ? peerSessionParameters : void 0,
      localSessionParameters,
      channelType: this.#channel.type,
      isPeerActive: this.session.isPeerActive,
      usesMrp: this.session.usesMrp,
      expectedProcessingTime
    });
  }
  /**
   * Calculates the backoff time for a resubmission based on the current retransmission count.
   * If no session parameters are provided, the parameters of the current session are used.
   * If session parameters are provided, the method can be used to calculate the maximum backoff time for the other
   * side of the exchange.
   *
   * When `calculateMaximum` is set to true, we calculate the maximum time without any randomness.
   *
   * @see {@link MatterSpecification.v10.Core}, section 4.11.2.1
   */
  getMrpResubmissionBackOffTime(retransmissionCount, sessionParameters, calculateMaximum = false) {
    return import_MRP.MRP.retransmissionIntervalOf(
      {
        transmissionNumber: retransmissionCount,
        sessionParameters: sessionParameters ?? this.session.parameters,
        isPeerActive: this.session.isPeerActive
      },
      calculateMaximum
    );
  }
}
//# sourceMappingURL=MessageChannel.js.map
