/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { createPromise, isObject, Logger, MaybePromise, Seconds, withTimeout } from "@matter/general";
import { Access, Schema } from "@matter/model";
import { OccurrenceManager } from "@matter/protocol";
const logger = 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 = Schema(type);
  if (schema.tag === "cluster") {
    for (const attr of schema.attributes) {
      if (attr.effectiveAccess.fabric === 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 } = createPromise();
  endpoint.eventsOf(type).stateChanged?.on(resolver);
  try {
    await endpoint.setStateOf(type, stateUpdate);
    stateUpdatePromises.push(withTimeout(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 = 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 === Access.Fabric.Scoped || fabric === 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(OccurrenceManager);
    for await (const event of occurrences.get()) {
      if (fabricRelevantEvents.has(`${event.clusterId}-${event.eventId}`) && isObject(event.payload) && !allowedIndices.includes(event.payload.fabricIndex)) {
        const result = occurrences.remove(event.number);
        if (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);
  });
}
export {
  limitEndpointAttributeDataToAllowedFabrics,
  limitNodeDataToAllowedFabrics
};
//# sourceMappingURL=FabricScopedDataHandler.js.map
