/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { ModelTraversal } from "#logic/ModelTraversal.js";
import { camelize, describeList } from "@matter/general";
import { Access } from "../aspects/Access.js";
import { Quality } from "../aspects/Quality.js";
import { SchemaImplementationError } from "../common/errors.js";
import { ElementTag, FeatureSet, Metatype } from "../common/index.js";
import { ClusterElement } from "../elements/index.js";
import { ClusterRevision, FeatureMap } from "../standard/elements/definitions.js";
import { AttributeModel } from "./AttributeModel.js";
import { Model } from "./Model.js";
import { ScopeModel } from "./ScopeModel.js";
class ClusterModel extends ScopeModel {
  tag = ClusterElement.Tag;
  classification;
  #quality;
  get diagnostics() {
    return this.effectiveQuality.diagnostics;
  }
  get quality() {
    return this.#quality;
  }
  set quality(definition) {
    this.#quality = Quality.create(definition);
  }
  get effectiveQuality() {
    return new ModelTraversal().findAspect(this, "quality", Quality) ?? this.#quality;
  }
  get attributes() {
    return this.scope.membersOf(this, { tags: [ElementTag.Attribute] });
  }
  get commands() {
    return this.scope.membersOf(this, { tags: [ElementTag.Command] });
  }
  get events() {
    return this.scope.membersOf(this, { tags: [ElementTag.Event] });
  }
  get datatypes() {
    return this.scope.membersOf(this, { tags: [ElementTag.Datatype] });
  }
  /**
   * Fields on a cluster are not part of the standard Matter data model.  They are used for internal extensions that
   * should not be served via the Matter protocol.
   */
  get fields() {
    return this.scope.membersOf(this, { tags: [ElementTag.Field] });
  }
  get conformant() {
    return new ClusterModel.Conformant(this);
  }
  get pics() {
    return this.hasLocalResource ? this.localResource.pics : void 0;
  }
  set pics(pics) {
    if (pics || this.hasLocalResource) {
      this.localResource.pics = pics;
    }
  }
  /**
   * Get attributes, commands and events whether inherited or defined directly in this model.
   */
  get allAces() {
    return this.scope.membersOf(this, {
      tags: [ElementTag.Attribute, ElementTag.Command, ElementTag.Event]
    });
  }
  get revision() {
    let revision = 1;
    const revisionAttr = this.attributes(ClusterRevision.id);
    if (typeof revisionAttr?.default === "number") {
      revision = revisionAttr.default;
    }
    return revision;
  }
  get features() {
    const features = /* @__PURE__ */ new Map();
    new ModelTraversal().visitInheritance(this.featureMap, (model) => {
      for (const child of model.children) {
        if (child.tag !== "field") {
          continue;
        }
        if (features.has(child.name)) {
          continue;
        }
        features.set(child.name, child);
      }
    });
    return [...features.values()];
  }
  get featureMap() {
    return new ModelTraversal().findMember(this, FeatureMap.id, [ElementTag.Attribute]) ?? new AttributeModel(FeatureMap);
  }
  get definedFeatures() {
    return new FeatureSet(this.features.map((feature) => feature.name));
  }
  get supportedFeatures() {
    const supported = {};
    for (const feature of this.features) {
      if (feature.default) {
        supported[feature.name] = true;
      }
    }
    return new FeatureSet(supported);
  }
  set supportedFeatures(features) {
    const featureSet = new FeatureSet(features);
    let featureMap = this.featureMap;
    if (featureMap.parent !== this) {
      featureMap = featureMap.clone();
      this.children.push(featureMap);
    }
    for (let feature of this.features) {
      const desc = feature.title && camelize(feature.title);
      let isSupported;
      if (desc !== void 0 && featureSet.has(desc)) {
        isSupported = true;
        featureSet.delete(desc);
      } else if (featureSet.has(feature.name)) {
        isSupported = true;
        featureSet.delete(feature.name);
      }
      if (!!feature.default === isSupported) {
        continue;
      }
      if (feature.parent !== featureMap) {
        feature = feature.clone();
        featureMap.children.push(feature);
      }
      feature.default = isSupported ? true : void 0;
    }
    if (featureSet.size) {
      throw new SchemaImplementationError(
        this,
        `Cannot set unknown feature${featureSet.size > 1 ? "s" : ""} ${describeList("and", ...featureSet)}`
      );
    }
  }
  get effectiveMetatype() {
    return Metatype.object;
  }
  get effectiveAccess() {
    return Access.Default;
  }
  constructor(definition, ...children) {
    super(definition, ...children);
    this.#quality = Quality.create(definition.quality);
    this.classification = definition.classification;
    if (!(definition instanceof Model)) {
      this.pics = definition.pics;
    }
  }
  toElement(omitResources = false, extra) {
    return super.toElement(omitResources, {
      quality: this.quality.valueOf(),
      classification: this.classification,
      ...extra
    });
  }
  static Tag = ClusterElement.Tag;
}
ClusterModel.register();
((ClusterModel2) => {
  class Conformant {
    #model;
    constructor(model) {
      this.#model = model;
    }
    /**
     * The cluster's conformant attributes.
     */
    get attributes() {
      return this.#model.scope.membersOf(this.#model, {
        tags: [ElementTag.Attribute],
        conformance: "conformant"
      });
    }
    /**
     * The cluster's conformant properties (includes fields and attributes).
     *
     * This offers compatibility with the equivalent field for ValueModel.  It is useful when treating a cluster as
     * a struct.
     */
    get properties() {
      return this.#model.scope.membersOf(this.#model, {
        tags: [ElementTag.Attribute, ElementTag.Field],
        conformance: "conformant"
      });
    }
    /**
     * The cluster's conformant fields.
     *
     * Fields are not formally allowed on clusters but we allow for extensions that should not be served via the
     * Matter protocol.
     *
     * This is primarily here to allow simplified access to fields in contexts where a variable is typed as a
     * ClusterModel or ValueModel.
     */
    get fields() {
      return this.#model.scope.membersOf(this.#model, {
        tags: [ElementTag.Field],
        conformance: "conformant"
      });
    }
    /**
     * The cluster's conformant commands.
     */
    get commands() {
      return this.#model.scope.membersOf(this.#model, {
        tags: [ElementTag.Command],
        conformance: "conformant"
      });
    }
    /**
     * The cluster's conformant events.
     */
    get events() {
      return this.#model.scope.membersOf(this.#model, {
        tags: [ElementTag.Event],
        conformance: "conformant"
      });
    }
  }
  ClusterModel2.Conformant = Conformant;
})(ClusterModel || (ClusterModel = {}));
export {
  ClusterModel
};
//# sourceMappingURL=ClusterModel.js.map
