"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 FabricScopedDataHandler_exports = {};
__export(FabricScopedDataHandler_exports, {
  limitEndpointAttributeDataToAllowedFabrics: () => limitEndpointAttributeDataToAllowedFabrics,
  limitNodeDataToAllowedFabrics: () => limitNodeDataToAllowedFabrics
});
module.exports = __toCommonJS(FabricScopedDataHandler_exports);
var import_general = require("@matter/general");
var import_model = require("@matter/model");
var import_protocol = require("@matter/protocol");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_general.Logger.get("FabricScopedDataHandler");
async function forBehaviors(endpoint, callback) {
  for (const type of Object.values(endpoint.behaviors.supported)) {
    const cluster = type?.cluster;
    if (!cluster) {
      continue;
    }
    const elements = endpoint.behaviors.elementsOf(type);
    if (elements.attributes.size || elements.events.size) {
      await callback(type, cluster, elements);
    }
  }
}
function computeFabricScopedUpdate(endpoint, type, supportedAttributes, allowedIndices) {
  const fabricScopedAttrs = /* @__PURE__ */ new Set();
  const schema = (0, import_model.Schema)(type);
  if (schema.tag === "cluster") {
    for (const attr of schema.attributes) {
      if (attr.effectiveAccess.fabric === import_model.Access.Fabric.Scoped) {
        fabricScopedAttrs.add(attr.propertyName);
      }
    }
  }
  const stateUpdate = {};
  const state = endpoint.stateOf(type);
  for (const attributeName of supportedAttributes) {
    if (fabricScopedAttrs.has(attributeName)) {
      const value = state[attributeName];
      if (Array.isArray(value) && value.length > 0) {
        const filtered = value.filter((entry) => allowedIndices.includes(entry.fabricIndex));
        if (filtered.length !== value.length) {
          stateUpdate[attributeName] = filtered;
        }
      }
    }
  }
  return Object.keys(stateUpdate).length > 0 ? stateUpdate : void 0;
}
async function sanitizeAttributeData(endpoint, type, cluster, supportedAttributes, allowedIndices) {
  const stateUpdate = computeFabricScopedUpdate(endpoint, type, supportedAttributes, allowedIndices);
  if (stateUpdate === void 0) {
    return [];
  }
  const stateUpdatePromises = new Array();
  const { resolver, promise } = (0, import_general.createPromise)();
  endpoint.eventsOf(type).stateChanged?.on(resolver);
  try {
    await endpoint.setStateOf(type, stateUpdate);
    stateUpdatePromises.push((0, import_general.withTimeout)((0, import_general.Seconds)(5), promise));
  } catch (error) {
    logger.warn(
      `Could not sanitize fabric-scoped attributes for cluster ${cluster.name} on endpoint ${endpoint.id}`,
      error
    );
  }
  return stateUpdatePromises;
}
async function limitNodeDataToAllowedFabrics(node, allowedIndices) {
  const fabricRelevantEvents = /* @__PURE__ */ new Set();
  const stateUpdatePromises = new Array();
  await node.visit(async (endpoint) => {
    await forBehaviors(endpoint, async (type, cluster, elements) => {
      if (elements.attributes.size) {
        stateUpdatePromises.push(
          ...await sanitizeAttributeData(endpoint, type, cluster, elements.attributes, allowedIndices)
        );
      }
      if (elements.events.size) {
        const clusterSchema = (0, import_model.Schema)(type);
        const nsEvents = cluster.events;
        if (nsEvents && clusterSchema.tag === "cluster") {
          const fabricScopedEvents = /* @__PURE__ */ new Set();
          for (const event of clusterSchema.events) {
            const fabric = event.effectiveAccess.fabric;
            if (fabric === import_model.Access.Fabric.Scoped || fabric === import_model.Access.Fabric.Sensitive) {
              fabricScopedEvents.add(event.propertyName);
            }
          }
          for (const eventName of elements.events) {
            const nsEvent = nsEvents[eventName];
            if (nsEvent && fabricScopedEvents.has(eventName)) {
              fabricRelevantEvents.add(`${cluster.id}-${nsEvent.id}`);
            }
          }
        }
      }
    });
  });
  await Promise.allSettled(stateUpdatePromises);
  if (fabricRelevantEvents.size > 0) {
    const occurrences = node.env.get(import_protocol.OccurrenceManager);
    for await (const event of occurrences.get()) {
      if (fabricRelevantEvents.has(`${event.clusterId}-${event.eventId}`) && (0, import_general.isObject)(event.payload) && !allowedIndices.includes(event.payload.fabricIndex)) {
        const result = occurrences.remove(event.number);
        if (import_general.MaybePromise.is(result)) {
          await result;
        }
      }
    }
  }
}
async function limitEndpointAttributeDataToAllowedFabrics(agent, allowedIndices) {
  await forBehaviors(agent.endpoint, async (type, _cluster, elements) => {
    if (!elements.attributes.size) {
      return;
    }
    const stateUpdate = computeFabricScopedUpdate(agent.endpoint, type, elements.attributes, allowedIndices);
    if (stateUpdate === void 0) {
      return;
    }
    const behavior = agent.get(type);
    behavior.type.supervisor.patch(stateUpdate, behavior.state, agent.endpoint.path);
  });
}
//# sourceMappingURL=FabricScopedDataHandler.js.map
