"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 ClusterComposer_exports = {};
__export(ClusterComposer_exports, {
  ClusterComposer: () => ClusterComposer,
  IllegalClusterError: () => IllegalClusterError
});
module.exports = __toCommonJS(ClusterComposer_exports);
var import_general = require("#general");
var import_model = require("#model");
var import_BitmapSchema = require("../../schema/BitmapSchema.js");
var import_ClusterType = require("../ClusterType.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
class IllegalClusterError extends import_general.MatterError {
}
const featureSelectionCache = /* @__PURE__ */ new WeakMap();
class ClusterComposer {
  constructor(cluster, allowInvalidFeatures = false) {
    this.cluster = cluster;
    this.#allowInvalidFeatureCombinations = allowInvalidFeatures;
  }
  #allowInvalidFeatureCombinations;
  /**
   * Build a cluster using selected feature flags.
   *
   * @param selection the name(s) of optional features to support
   * @throws {IllegalClusterError} if the feature combination is disallowed by the Matter specification
   */
  compose(selection) {
    this.#validateFeatureSelection(selection);
    const extensions = this.cluster.extensions;
    let cluster;
    const cacheKey = [...selection].sort().join("\u241C");
    cluster = featureSelectionCache.get(this.cluster)?.[cacheKey]?.deref();
    if (!cluster) {
      const currentCacheKey = [...new import_model.FeatureSet(this.cluster.supportedFeatures)].sort().join("\u241C");
      if (currentCacheKey === cacheKey) {
        cluster = featureSelectionCache.get(this.cluster)?.[cacheKey]?.deref();
      }
    }
    if (cluster) {
      return cluster;
    }
    if (extensions) {
      const base = this.cluster.base ?? this.cluster;
      const baseElements = (type) => {
        const elements = {};
        const clusterElements = this.cluster[type];
        for (const name in base[type]) {
          elements[name] = clusterElements[name] ?? base[type][name];
        }
        return elements;
      };
      cluster = (0, import_ClusterType.ClusterType)({
        ...base,
        attributes: baseElements("attributes"),
        commands: baseElements("commands"),
        events: baseElements("events"),
        supportedFeatures: (0, import_BitmapSchema.BitFlags)(this.cluster.features, ...selection),
        base
      });
      for (const selector of extensions) {
        if (selector.component) {
          this.#accept(cluster, selector.component, this.cluster, selector.flags);
        } else {
          this.reject(cluster, selector.flags);
        }
      }
    } else {
      const supportedFeatures = (0, import_BitmapSchema.BitFlags)(this.cluster.features, ...selection);
      cluster = (0, import_ClusterType.ClusterType)({
        ...this.cluster,
        supportedFeatures,
        base: this.cluster.base ?? this.cluster
      });
    }
    const baseVariants = featureSelectionCache.get(this.cluster);
    if (baseVariants === void 0) {
      featureSelectionCache.set(this.cluster, { [cacheKey]: new WeakRef(cluster) });
    } else {
      baseVariants[cacheKey] = new WeakRef(cluster);
    }
    return cluster;
  }
  /**
   * Validates a set of feature flags against the features supported by a cluster.
   */
  #validateFeatureSelection(features) {
    for (const f of features) {
      if (!this.cluster.features[(0, import_general.camelize)(f)]) {
        throw new IllegalClusterError(`"${f}" is not a valid feature identifier`);
      }
    }
  }
  /**
   * Injects a component into a cluster if the cluster supports the specified features.  Uses matching element from
   * "original" if present.  This allows for component insertion without overwrite of other changes to the named
   * element.
   */
  #accept(definition, component, original, flags) {
    for (const k in flags) {
      if (!!definition.supportedFeatures[k] !== !!flags[k]) {
        return;
      }
    }
    ClusterComposer.injectElements(definition, component, original);
  }
  static injectElements(definition, component, original) {
    function installElements(name) {
      const src = component[name];
      if (!src) {
        return;
      }
      let dest = definition[name];
      if (dest) {
        dest = { ...dest };
      } else {
        dest = {};
      }
      definition[name] = dest;
      for (const key in src) {
        const orig = original?.[name]?.[key];
        if (orig) {
          dest[key] = orig;
        } else {
          dest[key] = src[key];
        }
      }
    }
    installElements("attributes");
    installElements("commands");
    installElements("events");
  }
  /**
   * Throws an error if a feature combination is illegal per the Matter specification.
   */
  reject(definition, flags) {
    if (this.#allowInvalidFeatureCombinations) {
      return;
    }
    for (const k in flags) {
      if (!!definition.supportedFeatures[k] !== !!flags[k]) {
        return;
      }
    }
    throw new IllegalClusterError(
      `Feature combination ${(0, import_general.serialize)(flags)} is disallowed by the Matter specification`
    );
  }
}
//# sourceMappingURL=ClusterComposer.js.map
