"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 KeySets_exports = {};
__export(KeySets_exports, {
  GROUP_KEY_INFO: () => GROUP_KEY_INFO,
  KeySets: () => KeySets
});
module.exports = __toCommonJS(KeySets_exports);
var import_general = require("#general");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const GROUP_KEY_INFO = import_general.Bytes.fromString("GroupKeyHash");
class KeySets extends import_general.BasicSet {
  /** Operational enhanced structure for fast access based on the group session id. */
  #sessions = /* @__PURE__ */ new Map();
  get sessions() {
    return this.#sessions;
  }
  add(item) {
    this.delete("groupKeySetId", item.groupKeySetId);
    super.add(item);
    this.#updateSessions();
  }
  delete(itemOrField, value) {
    const deleted = super.delete(itemOrField, value);
    if (deleted) {
      this.#updateSessions();
    }
    return deleted;
  }
  forId(groupKeySetId) {
    return this.get("groupKeySetId", groupKeySetId);
  }
  /**
   * Return an operative list of operational keys, start time and their session IDs for a specified
   * group key set id. This is mainly used for receiving messages.
   */
  allKeysForId(keySetId) {
    const groupKeySet = this.forId(keySetId);
    if (groupKeySet === void 0) {
      throw new import_general.MatterFlowError(`GroupKeySet for groupKeySet ${keySetId} not found.`);
    }
    const operationalKeys = Array();
    const {
      operationalEpochKey0,
      groupSessionId0,
      epochStartTime0,
      operationalEpochKey1,
      groupSessionId1,
      epochStartTime1,
      operationalEpochKey2,
      groupSessionId2,
      epochStartTime2
    } = groupKeySet;
    if (operationalEpochKey0 === null || epochStartTime0 === null || keySetId !== 0 && groupSessionId0 === null) {
      throw new import_general.MatterFlowError(`EpochKey0 for groupKeySet ${keySetId} not found.`);
    }
    operationalKeys.push({
      key: operationalEpochKey0,
      sessionId: groupSessionId0 !== null ? groupSessionId0 : void 0,
      startTime: import_general.Timestamp.fromMicroseconds(epochStartTime0)
    });
    if (operationalEpochKey1 !== null && groupSessionId1 !== null && epochStartTime1 !== null) {
      operationalKeys.push({
        key: operationalEpochKey1,
        sessionId: groupSessionId1,
        startTime: import_general.Timestamp.fromMicroseconds(epochStartTime1)
      });
    }
    if (operationalEpochKey2 !== null && groupSessionId2 !== null && epochStartTime2 !== null) {
      operationalKeys.push({
        key: operationalEpochKey2,
        sessionId: groupSessionId2,
        startTime: import_general.Timestamp.fromMicroseconds(epochStartTime2)
      });
    }
    return operationalKeys;
  }
  /**
   * Returns the current operational group key for a given group KeySet Id and returns the keys, start time and
   * their session IDs. This is mainly used for sending messages.
   */
  currentKeyForId(keySetId) {
    const operationalKeys = this.allKeysForId(keySetId);
    if (operationalKeys.length === 0) {
      throw new import_general.MatterFlowError(`No operational keys found for groupKeySet ${keySetId}.`);
    }
    if (keySetId === 0) {
      if (operationalKeys.length > 2) {
        return operationalKeys[1];
      }
      return operationalKeys[0];
    } else {
      const now = import_general.Time.nowUs;
      const relevantKeys = operationalKeys.filter(({ startTime }) => startTime <= now);
      if (relevantKeys.length === 0) {
        throw new import_general.ImplementationError(
          `No operational keys found for groupKeySet ${keySetId} that are not in the future.`
        );
      }
      return relevantKeys[operationalKeys.length - 1];
    }
  }
  /**
   * Returns the group key set for a given group key set id in the official data format from the Group key Management
   * cluster.
   */
  asGroupKeySet(groupKeySetId) {
    const groupKeySet = this.forId(groupKeySetId);
    if (groupKeySet === void 0) {
      return void 0;
    }
    const {
      epochKey0,
      epochStartTime0,
      epochKey1,
      epochStartTime1,
      epochKey2,
      epochStartTime2,
      groupKeySecurityPolicy,
      groupKeyMulticastPolicy
    } = groupKeySet;
    return {
      groupKeySetId,
      epochKey0,
      epochStartTime0,
      epochKey1,
      epochStartTime1,
      epochKey2,
      epochStartTime2,
      groupKeySecurityPolicy,
      groupKeyMulticastPolicy
    };
  }
  /** Calculates a group session id based on the operational group key. */
  async sessionIdFromKey(crypto, operationalGroupKey) {
    const groupKeyHash = await crypto.createHkdfKey(operationalGroupKey, new Uint8Array(), GROUP_KEY_INFO, 2);
    return new import_general.DataReader(groupKeyHash).readUInt16();
  }
  /**
   * Updates the group session map based on the current group key sets.
   * This is used to quickly find the operational keys by their group session id.
   */
  #updateSessions() {
    this.#sessions.clear();
    for (const [id, keySet] of this.mapOf("groupKeySetId").entries()) {
      if (id === 0) {
        continue;
      }
      if (keySet.groupSessionId0 !== null) {
        const list = this.#sessions.get(keySet.groupSessionId0) ?? [];
        list.push({ key: keySet.operationalEpochKey0, keySetId: id });
        this.#sessions.set(keySet.groupSessionId0, list);
      }
      if (keySet.groupSessionId1 !== null && keySet.operationalEpochKey1 !== null) {
        const list = this.#sessions.get(keySet.groupSessionId1) ?? [];
        list.push({ key: keySet.operationalEpochKey1, keySetId: id });
        this.#sessions.set(keySet.groupSessionId1, list);
      }
      if (keySet.groupSessionId2 !== null && keySet.operationalEpochKey2 !== null) {
        const list = this.#sessions.get(keySet.groupSessionId2) ?? [];
        list.push({ key: keySet.operationalEpochKey2, keySetId: id });
        this.#sessions.set(keySet.groupSessionId2, list);
      }
    }
  }
}
//# sourceMappingURL=KeySets.js.map
