"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 ValidatedElements_exports = {};
__export(ValidatedElements_exports, {
  ClusterElementError: () => ClusterElementError,
  ClusterImplementationError: () => ClusterImplementationError,
  ValidatedElements: () => ValidatedElements
});
module.exports = __toCommonJS(ValidatedElements_exports);
var import_general = require("@matter/general");
var import_model = require("@matter/model");
var import_Behavior = require("../Behavior.js");
var import_cluster_behavior_utils = require("./cluster-behavior-utils.js");
var import_inter_element_conformance = require("./inter-element-conformance.js");
var import_NameDependentElements = require("./NameDependentElements.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_general.Logger.get("ValidatedElements");
class ClusterImplementationError extends import_general.MatterAggregateError {
  constructor(name, errors) {
    super(
      errors,
      import_general.Diagnostic.upgrade(
        `Cluster behavior ${name} has fatal implementation errors`,
        import_general.Diagnostic.squash("Cluster behavior", import_general.Diagnostic.strong(name), "has fatal implementation errors")
      )
    );
  }
}
class ClusterElementError extends import_general.ImplementationError {
  constructor(element, message) {
    super(
      import_general.Diagnostic.upgrade(
        `Error in ${element}: ${message}`,
        import_general.Diagnostic.squash("Error in ", import_general.Diagnostic.strong(element), ": ", message)
      )
    );
  }
}
class ValidatedElements {
  /**
   * Supported attributes.
   */
  attributes = /* @__PURE__ */ new Set();
  /**
   * Attribute name → ID mapping for all supported attributes (including globals).
   */
  attributeIds = /* @__PURE__ */ new Map();
  /**
   * Supported commands.
   */
  commands = /* @__PURE__ */ new Set();
  /**
   * Commands that are structurally present on the prototype, including {@link Behavior.unimplemented} stubs.
   */
  presentCommands = /* @__PURE__ */ new Set();
  /**
   * Supported events.
   */
  events = /* @__PURE__ */ new Set();
  /**
   * A list of implementation errors, if any.
   */
  errors;
  #name;
  #type;
  #instance;
  #cluster;
  #schema;
  #nameDependentElements;
  /**
   * Obtain validation information.
   *
   * Validation may run against the type alone or with a specific instance of the behavior.  The latter option allows
   * for per-instance specialization.
   *
   * @param type the behavior type to analyze
   * @param instance optional concrete instance of the behavior
   */
  constructor(type, instance) {
    this.#type = type;
    this.#instance = instance;
    this.#name = type.name;
    this.#cluster = type.cluster;
    this.#schema = (0, import_model.Schema)(type);
    if (typeof type !== "function") {
      this.error(void 0, "Is not a class", true);
    }
    if (this.#cluster === void 0) {
      this.error("cluster", "Property missing", true);
      return;
    }
    if (typeof this.#cluster !== "object") {
      this.error("cluster", "Property is not an object", true);
      return;
    }
    if (instance !== void 0 && (instance === null || typeof instance !== "object")) {
      this.error("instance", "Is not an object", true);
    }
    this.#validateAttributes();
    this.#validateCommands();
    this.#validateEvents();
    if (this.#nameDependentElements) {
      (0, import_inter_element_conformance.resolveInterElementConformance)(this, this.#schema, this.#nameDependentElements);
    }
  }
  /**
   * If there are errors, log and throw an exception.
   */
  report() {
    if (!this.errors) {
      return;
    }
    let fatalErrors;
    for (const { element, message, fatal } of this.errors) {
      const error = new ClusterElementError(element, message);
      if (fatal) {
        if (!fatalErrors) {
          fatalErrors = [];
        }
        fatalErrors.push(error);
      } else {
        logger.warn(error.message);
      }
    }
    if (fatalErrors) {
      throw new ClusterImplementationError(this.#type.name, fatalErrors);
    }
  }
  #validateAttributes() {
    let state;
    if (this.#instance) {
      state = this.#instance.state;
    } else {
      const constructor = this.#type.State;
      if (!constructor) {
        this.error("State", "Property missing", true);
        return;
      }
      try {
        state = new constructor();
      } catch (e) {
        this.error("State", "Not constructable", true);
        return;
      }
    }
    for (const member of this.#schema.conformant.attributes) {
      if (member.id === void 0) {
        continue;
      }
      const name = member.propertyName;
      const isImplemented = state[name] !== void 0;
      const applicability = member.effectiveConformance.applicabilityFor(this.#schema);
      if (applicability === import_model.Conformance.Applicability.Conditional) {
        if (!this.#nameDependentElements) {
          this.#nameDependentElements = new import_NameDependentElements.NameDependentElements(this.#schema);
        }
        this.#nameDependentElements.add(member, "attribute", isImplemented);
        if (isImplemented) {
          this.attributes.add(name);
          this.attributeIds.set(name, member.id);
        }
        continue;
      }
      if (!isImplemented) {
        if (applicability === import_model.Conformance.Applicability.Mandatory) {
          this.error(`State.${name}`, "Mandatory element unsupported", false);
        }
        continue;
      }
      this.attributes.add(name);
      this.attributeIds.set(name, member.id);
    }
  }
  #validateCommands() {
    let implementations;
    if (this.#instance) {
      implementations = this.#instance;
    } else {
      try {
        implementations = (0, import_cluster_behavior_utils.introspectionInstanceOf)(this.#type);
      } catch (e) {
        this.error("constructor", "Not constructable", true);
        return;
      }
    }
    for (const member of this.#schema.conformant.commands) {
      if (member.isResponse) {
        continue;
      }
      const name = member.propertyName;
      const applicability = member.effectiveConformance.applicabilityFor(this.#schema);
      const implementation = implementations[name];
      const isPresent = name in implementations && implementation !== void 0;
      if (applicability === import_model.Conformance.Applicability.Conditional) {
        if (!this.#nameDependentElements) {
          this.#nameDependentElements = new import_NameDependentElements.NameDependentElements(this.#schema);
        }
        let isImplemented = false;
        if (isPresent && typeof implementation === "function") {
          this.presentCommands.add(name);
          if (implementation !== import_Behavior.Behavior.unimplemented) {
            isImplemented = true;
            this.commands.add(name);
          }
        }
        this.#nameDependentElements.add(member, "command", isImplemented);
        continue;
      }
      if (!isPresent) {
        if (applicability === import_model.Conformance.Applicability.Mandatory) {
          this.error(name, `Implementation missing`, true);
        }
        continue;
      }
      if (typeof implementation !== "function") {
        this.error(name, `Implementation is not a function`, true);
        continue;
      }
      this.presentCommands.add(name);
      if (implementation === import_Behavior.Behavior.unimplemented) {
        if (applicability === import_model.Conformance.Applicability.Mandatory) {
          if (this.#name.match(/^(?:Groups|Scenes|GroupKeyManagement)(?:Server|Behavior)/)) {
            continue;
          }
          this.error(name, `Throws unimplemented exception`, false);
        }
        continue;
      }
      this.commands.add(name);
    }
  }
  #validateEvents() {
    const constructor = this.#type.Events;
    if (!constructor) {
      for (const member of this.#schema.conformant.events) {
        if (member.effectiveConformance.applicabilityFor(this.#schema) === import_model.Conformance.Applicability.Mandatory) {
          this.error("Events", "Implementation missing", true);
          return;
        }
      }
      return;
    }
    let emitters;
    if (this.#instance) {
      emitters = this.#instance.events;
    } else {
      try {
        emitters = new constructor();
      } catch (e) {
        this.error("Events", "Not constructable", true);
        return;
      }
    }
    for (const member of this.#schema.conformant.events) {
      const name = member.propertyName;
      const isImplemented = name in emitters;
      const applicability = member.effectiveConformance.applicabilityFor(this.#schema);
      if (applicability === import_model.Conformance.Applicability.Conditional) {
        if (!this.#nameDependentElements) {
          this.#nameDependentElements = new import_NameDependentElements.NameDependentElements(this.#schema);
        }
        this.#nameDependentElements.add(member, "event", isImplemented);
        if (isImplemented) {
          this.events.add(name);
        }
        continue;
      }
      if (!isImplemented) {
        if (applicability === import_model.Conformance.Applicability.Mandatory) {
          this.error(`cluster.events.${name}`, "Implementation missing", true);
        }
        continue;
      }
      this.events.add(name);
    }
  }
  error(element, message, fatal) {
    if (!this.errors) {
      this.errors = [];
    }
    const name = element === void 0 ? this.#name : `${this.#name}.${element}`;
    this.errors?.push({ element: name, message, fatal });
  }
}
//# sourceMappingURL=ValidatedElements.js.map
