"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 GroupSession_exports = {};
__export(GroupSession_exports, {
  GroupSession: () => GroupSession
});
module.exports = __toCommonJS(GroupSession_exports);
var import_MessageCodec = require("#codec/MessageCodec.js");
var import_Mark = require("#common/Mark.js");
var import_general = require("#general");
var import_ControllerDiscovery = require("#peer/ControllerDiscovery.js");
var import_PeerAddress = require("#peer/PeerAddress.js");
var import_types = require("#types");
var import_SecureSession = require("./SecureSession.js");
var import_Session = require("./Session.js");
/**
 * @license
 * Copyright 2022-2023 Project CHIP Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_general.Logger.get("SecureGroupSession");
class GroupSession extends import_SecureSession.SecureSession {
  #id;
  #fabric;
  #peerNodeId;
  #operationalGroupKey;
  supportsMRP = false;
  closingAfterExchangeFinished = false;
  // Group sessions do not close after exchange finished, they are long-lived
  keySetId;
  constructor(config) {
    const { manager, fabric, operationalGroupKey, id, peerNodeId, keySetId } = config;
    super({
      ...config,
      setActiveTimestamp: false,
      // We always set the active timestamp for Secure sessions TODO Check
      messageCounter: fabric.groups.messaging.counterFor(operationalGroupKey)
    });
    this.#id = id;
    this.#fabric = fabric;
    this.#peerNodeId = peerNodeId;
    this.keySetId = keySetId;
    this.#operationalGroupKey = operationalGroupKey;
    manager?.registerGroupSession(this);
    fabric.addSession(this);
    logger.debug(this.via, `Created secure GROUP session for fabric index ${fabric.fabricIndex}`);
  }
  /**
   * Create an outbound group session.
   */
  static async create(options) {
    const { manager, transports, id, fabric, keySetId, groupNodeId, operationalGroupKey } = options;
    const groupId = import_types.GroupId.fromNodeId(groupNodeId);
    const multicastAddress = fabric.groups.multicastAddressFor(groupId);
    const operationalInterface = transports.interfaceFor(import_general.ChannelType.UDP, multicastAddress);
    if (operationalInterface === void 0) {
      throw new import_ControllerDiscovery.PairRetransmissionLimitReachedError(`IPv6 interface not initialized`);
    }
    const channel = await operationalInterface.openChannel({
      type: import_general.ChannelType.UDP,
      ip: multicastAddress,
      port: import_general.STANDARD_MATTER_PORT
    });
    return new GroupSession({
      manager,
      channel,
      id,
      fabric,
      keySetId,
      peerNodeId: groupNodeId,
      operationalGroupKey
    });
  }
  get type() {
    return import_MessageCodec.SessionType.Group;
  }
  get fabric() {
    return this.#fabric;
  }
  get id() {
    return this.#id;
  }
  get peerSessionId() {
    return this.#id;
  }
  get via() {
    return import_general.Diagnostic.via(`${import_Mark.Mark.SESSION}group#${import_general.hex.word(this.id)}`);
  }
  get nodeId() {
    return this.#fabric.nodeId;
  }
  get peerNodeId() {
    return this.#peerNodeId;
  }
  get associatedFabric() {
    return this.#fabric;
  }
  /**
   * The peer group's address.
   */
  get peerAddress() {
    return (0, import_PeerAddress.PeerAddress)({
      fabricIndex: this.#fabric?.fabricIndex ?? import_types.FabricIndex.NO_FABRIC,
      nodeId: this.#peerNodeId
    });
  }
  subjectFor(message) {
    if (message === void 0 || message.packetHeader.destGroupId === void 0) {
      throw new import_general.ImplementationError("GroupSession requires a message with destGroupId");
    }
    return this.fabric.groups.subjectForGroup((0, import_types.GroupId)(message.packetHeader.destGroupId), this.keySetId);
  }
  notifyActivity(_messageReceived) {
  }
  updateMessageCounter(messageCounter, sourceNodeId, operationalKey) {
    if (sourceNodeId === void 0 || operationalKey === void 0) {
      throw new import_general.InternalError("Source Node ID is required for GroupSession updateMessageCounter.");
    }
    const receptionState = this.#fabric.groups.messaging.receptionStateFor(sourceNodeId, operationalKey);
    receptionState.updateMessageCounter(messageCounter);
  }
  encode(message) {
    message.packetHeader.sessionId = this.#id;
    const { header, applicationPayload } = import_MessageCodec.MessageCodec.encodePayload(message);
    if (header.destGroupId === void 0) {
      throw new import_general.UnexpectedDataError("Group ID is required for GroupSession encode.");
    }
    const headerBytes = import_MessageCodec.MessageCodec.encodePacketHeader(message.packetHeader);
    const securityFlags = headerBytes[3];
    const nonce = import_Session.Session.generateNonce(securityFlags, header.messageId, this.#fabric.nodeId);
    return {
      header,
      applicationPayload: this.#fabric.crypto.encrypt(
        this.#operationalGroupKey,
        applicationPayload,
        nonce,
        headerBytes
      )
    };
  }
  decode() {
    throw new import_general.InternalError("GroupSession does not support decode on instance.");
  }
  static decode(fabrics, { header, applicationPayload, messageExtension }, aad) {
    if (header.hasMessageExtensions) {
      logger.info(
        `Message extensions are not supported. Ignoring ${messageExtension ? import_general.Bytes.toHex(messageExtension) : void 0}`
      );
    }
    const sourceNodeId = header.sourceNodeId;
    if (sourceNodeId === void 0) {
      throw new import_general.UnexpectedDataError("Source Node ID is required for GroupSession decode.");
    }
    const nonce = import_Session.Session.generateNonce(header.securityFlags, header.messageId, sourceNodeId);
    const sessionId = header.sessionId;
    const keys = new Array();
    for (const fabric2 of fabrics) {
      const sessions = fabric2.groups.sessions.get(sessionId);
      if (sessions?.length) {
        for (const session of sessions) {
          keys.push({ ...session, fabric: fabric2 });
        }
      }
    }
    if (keys.length === 0) {
      throw new import_general.MatterFlowError("No key candidate found for group session decryption.");
    }
    let message;
    let key;
    let fabric;
    let keySetId;
    let found = false;
    for ({ key, keySetId, fabric } of keys) {
      try {
        message = import_MessageCodec.MessageCodec.decodePayload({
          header,
          applicationPayload: fabric.crypto.decrypt(key, applicationPayload, nonce, aad)
        });
        found = true;
        break;
      } catch (error) {
        import_general.CryptoDecryptError.accept(error);
      }
    }
    if (!found || !message || !key || !keySetId || !fabric) {
      throw new import_general.MatterFlowError("Failed to decode group message with any key candidate.");
    }
    if (message.payloadHeader.hasSecuredExtension) {
      logger.info(
        `Secured extensions are not supported. Ignoring ${message.securityExtension ? import_general.Bytes.toHex(message.securityExtension) : void 0}`
      );
    }
    return { message, key, sessionId, sourceNodeId, keySetId, fabric };
  }
  async close() {
    this.manager?.removeGroupSession(this);
    await super.close();
  }
}
((GroupSession2) => {
  function assert(session, errorText) {
    if (!is(session)) {
      throw new import_general.MatterFlowError(errorText ?? "Unsecured session in secure context");
    }
  }
  GroupSession2.assert = assert;
  function is(session) {
    return session?.type === import_MessageCodec.SessionType.Group;
  }
  GroupSession2.is = is;
})(GroupSession || (GroupSession = {}));
//# sourceMappingURL=GroupSession.js.map
