"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 ServerBehaviorBacking_exports = {};
__export(ServerBehaviorBacking_exports, {
  FeatureMismatchError: () => FeatureMismatchError,
  ServerBehaviorBacking: () => ServerBehaviorBacking
});
module.exports = __toCommonJS(ServerBehaviorBacking_exports);
var import_ClusterBehavior = require("#behavior/cluster/ClusterBehavior.js");
var import_ValidatedElements = require("#behavior/cluster/ValidatedElements.js");
var import_general = require("@matter/general");
var import_model = require("@matter/model");
var import_types = require("@matter/types");
var import_BehaviorBacking = require("./BehaviorBacking.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const NoElements = /* @__PURE__ */ new Set();
class FeatureMismatchError extends import_general.ImplementationError {
}
class ServerBehaviorBacking extends import_BehaviorBacking.BehaviorBacking {
  #elements;
  #suppressedChanges;
  #quietObservers;
  constructor(endpoint, type, store, options) {
    super(endpoint, type, store, options);
    this.#configureEventSuppression();
  }
  get elements() {
    return this.#elements;
  }
  invokeInitializer(behavior, options) {
    const finalizeState = () => {
      this.#applyTransitiveDefaults(behavior.state);
      if (behavior instanceof import_ClusterBehavior.ClusterBehavior) {
        this.#configureElements(behavior);
      } else {
        this.#elements = {
          features: NoElements,
          attributes: NoElements,
          commands: NoElements,
          events: NoElements
        };
      }
      const context = behavior.context;
      this.datasource.validate(context, behavior.state);
    };
    const promise = super.invokeInitializer(behavior, options);
    if (promise) {
      return promise.then(finalizeState);
    }
    finalizeState();
  }
  /**
   * Schema may specify that state fields default to the value of another field.  We apply these defaults after
   * initialization when the other field should be defined.
   */
  #applyTransitiveDefaults(state) {
    const schema = this.type.schema;
    if (!schema) {
      return;
    }
    for (const member of this.type.supervisor.membersOf(schema)) {
      const name = member.propertyName;
      if (state[name] === void 0) {
        const referenced = import_model.FieldValue.referenced(member.default);
        if (referenced) {
          const val = state[(0, import_general.camelize)(referenced)];
          if (val !== void 0) {
            state[name] = val;
          }
        }
      }
    }
  }
  #configureElements(behavior) {
    const validation = new import_ValidatedElements.ValidatedElements(behavior.constructor, behavior);
    validation.report();
    const globals = behavior.state;
    globals.attributeList = [...validation.attributeIds.values()].sort((a, b) => a - b);
    const schema = (0, import_model.Schema)(behavior.type);
    const commandModels = /* @__PURE__ */ new Map();
    for (const cmd of schema.commands) {
      if (cmd.isRequest) {
        commandModels.set(cmd.propertyName, cmd);
      }
    }
    const responseModels = /* @__PURE__ */ new Map();
    for (const cmd of schema.commands) {
      if (!cmd.isRequest) {
        responseModels.set(cmd.name, cmd);
      }
    }
    const acceptedIds = [];
    const generatedIds = /* @__PURE__ */ new Set();
    for (const name of validation.commands) {
      const model = commandModels.get(name);
      if (!model || model.id === import_types.CommandId.NONE) {
        continue;
      }
      acceptedIds.push(model.id);
      const responseName = model.effectiveResponse;
      if (responseName && responseName !== "status") {
        const responseModel = responseModels.get(responseName);
        if (responseModel && responseModel.id !== import_types.CommandId.NONE) {
          generatedIds.add(responseModel.id);
        }
      }
    }
    globals.acceptedCommandList = acceptedIds.sort((a, b) => a - b);
    globals.generatedCommandList = [...generatedIds].sort((a, b) => a - b);
    if (schema.tag === "cluster") {
      const { supportedFeatures, featureMap } = (0, import_model.Schema)(behavior.type);
      const { featuresSupported, featuresAvailable } = import_model.FeatureSet.normalize(
        featureMap,
        new import_model.FeatureSet(globals.featureMap)
      );
      for (const name of featuresAvailable) {
        if (supportedFeatures.has(name) !== featuresSupported.has(name)) {
          throw new FeatureMismatchError(
            `The featureMap for ${behavior} does not match the implementation; please use ${behavior.type.name}.with("FeatureName") to configure features`
          );
        }
      }
    }
    this.#elements = {
      features: behavior.type.schema.supportedFeatures ?? /* @__PURE__ */ new Set(),
      attributes: validation.attributes,
      commands: validation.commands,
      events: validation.events
    };
  }
  #onChange(props) {
    if (this.#suppressedChanges) {
      props = props.filter((name) => !this.#suppressedChanges.has(name));
    }
    this.broadcastChanges(props);
  }
  get datasourceOptions() {
    const options = super.datasourceOptions;
    options.onChange = this.#onChange.bind(this);
    return options;
  }
  close(agent) {
    this.#quietObservers?.close();
    return super.close(agent);
  }
  /**
   * We handle events in bulk via {@link Datasource.Options.onChange}, but "quieter" and "changesOmitted" events
   * require special handling.  Those we ignore in the change handler and instead report only when emitted by the
   * corresponding {@link OnlineEvent}.
   */
  #configureEventSuppression() {
    const { schema } = this.type;
    if (!schema) {
      return;
    }
    for (const property of schema.conformant.properties) {
      const { changesOmitted, quieter } = property.effectiveQuality;
      if (!changesOmitted && !quieter) {
        continue;
      }
      const name = property.propertyName;
      if (!this.#suppressedChanges) {
        this.#suppressedChanges = /* @__PURE__ */ new Set();
      }
      this.#suppressedChanges.add(name);
      if (!quieter) {
        continue;
      }
      const event = this.events[`${name}$Changed`];
      if (event === void 0) {
        continue;
      }
      if (event.isQuieter) {
        if (!this.#quietObservers) {
          this.#quietObservers = new import_general.ObserverGroup();
        }
        this.#quietObservers.on(event.quiet, () => this.broadcastChanges([name]));
      }
    }
  }
}
//# sourceMappingURL=ServerBehaviorBacking.js.map
