/**
 * @license
 * Copyright 2022-2025 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { camelize, InternalError } from "#general";
import {
  AttributeModel,
  ClusterModel,
  ElementTag,
  FeatureMap,
  FeatureSet,
  Matter,
  Scope
} from "#model";
import { AccessControl } from "#protocol";
import { ValueCaster } from "../state/managed/values/ValueCaster.js";
import { ValueManager } from "../state/managed/values/ValueManager.js";
import { ValuePatcher } from "../state/managed/values/ValuePatcher.js";
import { ValueValidator } from "../state/validation/ValueValidator.js";
const GlobalAttributeSupervisors = {};
const cache = /* @__PURE__ */ new WeakMap();
class RootSupervisor {
  #generating = /* @__PURE__ */ new Set();
  #cache = /* @__PURE__ */ new WeakMap();
  #featureMap;
  #supportedFeatures;
  #scope;
  #members;
  #rootSchema;
  #root;
  #memberNames;
  #propertyNamesAndIds;
  /**
   * Create a new supervisor.
   *
   * @param schema the {@link Schema} for the supervised data
   */
  constructor(schema) {
    this.#rootSchema = schema;
    this.#scope = Scope(schema, { forceCache: true, forceOwner: true });
    if (schema instanceof ClusterModel) {
      this.#featureMap = schema.featureMap;
      this.#supportedFeatures = schema.supportedFeatures ?? new FeatureSet();
    } else {
      this.#featureMap = new AttributeModel(FeatureMap);
      this.#supportedFeatures = new FeatureSet();
    }
    this.#members = new Set(this.membersOf(schema));
    this.#root = this.#createValueSupervisor(schema);
  }
  /**
   * Obtain the supervisor for schema.  The result is cached.
   */
  static for(schema) {
    if (!schema) {
      return void 0;
    }
    if (cache.has(schema)) {
      return cache.get(schema);
    }
    const supervisor = new RootSupervisor(schema);
    cache.set(schema, supervisor);
    return supervisor;
  }
  get owner() {
    return this.#root.owner;
  }
  get schema() {
    return this.#rootSchema;
  }
  get scope() {
    return this.#scope;
  }
  get access() {
    return this.#root.access;
  }
  get validate() {
    return this.#root.validate;
  }
  get manage() {
    return this.#root.manage;
  }
  get patch() {
    return this.#root.patch;
  }
  get cast() {
    return this.#root.cast;
  }
  /**
   * The names of all members.
   */
  get memberNames() {
    let names = this.#memberNames;
    if (!names) {
      names = /* @__PURE__ */ new Set();
      for (const member of this.#members) {
        names.add(camelize(member.name));
      }
      this.#memberNames = names;
    }
    return names;
  }
  /**
   * Retrieve names or IDs of fields configured as non-volatile.
   */
  persistentKeys(key = "name") {
    const persistent = /* @__PURE__ */ new Set();
    for (const member of this.#members) {
      if (member.effectiveQuality.nonvolatile || // Persist all writable attributes in addition to those officially marked as nonvolatile
      member.tag === ElementTag.Attribute && (member.effectiveAccess.writable || member.effectiveAccess.fabricScoped)) {
        if (key === "id") {
          const id = member.effectiveId;
          if (id !== void 0) {
            persistent.add(id.toString());
            continue;
          }
        }
        persistent.add(camelize(member.name));
      }
    }
    return persistent;
  }
  get propertyNamesAndIds() {
    let names = this.#propertyNamesAndIds;
    if (!names) {
      names = /* @__PURE__ */ new Map();
      for (const member of this.#members) {
        names.set(camelize(member.name), member.id);
      }
      this.#propertyNamesAndIds = names;
    }
    return names;
  }
  /**
   * Retrieve members for schema in {@link scope}.
   *
   * The {@link Scope.ConformanceMode} defaults to "deconflicted" if you do not override.
   */
  membersOf(schema, options = {}) {
    if (options.conformance === void 0) {
      options = { ...options, conformance: "deconflicted" };
    }
    return this.#scope.membersOf(schema, options);
  }
  /**
   * All available features defined in the schema.
   */
  get featureMap() {
    return this.#featureMap;
  }
  /**
   * Features supported by this implementation.
   */
  get supportedFeatures() {
    return this.#supportedFeatures;
  }
  /**
   * Obtain {@link ValueSupervisor} implementation for a specific schema.
   *
   * @param schema the model describing the record type
   * @returns the I/O implementation
   */
  get(schema) {
    if (schema === this.#rootSchema) {
      return this;
    }
    let supervisor = this.#cache.get(schema);
    if (supervisor === void 0) {
      if (schema.tag === "attribute" && schema.id !== void 0 && schema.id in GlobalAttributeSupervisors) {
        supervisor = {
          ...GlobalAttributeSupervisors[schema.id],
          owner: this,
          schema
        };
      } else {
        supervisor = this.#createValueSupervisor(schema);
      }
      this.#cache.set(schema, supervisor);
    }
    return supervisor;
  }
  #createValueSupervisor(schema) {
    const deferGeneration = (name, generator) => {
      let generated = false;
      return ((...args) => {
        if (!generated) {
          if (manager === void 0) {
            throw new InternalError("Deferred I/O generation invoked impossibly early");
          }
          manager[name] = generator(schema, this);
          generated = true;
        }
        return manager[name](...args);
      });
    };
    let manager;
    if (this.#isGenerating(schema)) {
      manager = {
        owner: this,
        schema,
        access: AccessControl(schema),
        validate: deferGeneration("validate", ValueValidator),
        manage: deferGeneration("manage", ValueManager),
        patch: deferGeneration("patch", ValuePatcher),
        cast: deferGeneration("cast", ValueCaster)
      };
    } else {
      try {
        this.#generating.add(schema);
        manager = {
          owner: this,
          schema,
          access: AccessControl(schema),
          validate: ValueValidator(schema, this),
          manage: ValueManager(schema, this),
          patch: ValuePatcher(schema, this),
          cast: ValueCaster(schema, this)
        };
      } finally {
        this.#generating.delete(schema);
      }
    }
    this.#cache.set(schema, manager);
    return manager;
  }
  /**
   * If a schema has circular references, code generation may need to defer generation of child functions to avoid
   * infinite loops.
   *
   * In order to keep generation code simpler we use this method to detect when lazy generation is necessary and
   * install stubs that bootstrap each method.
   */
  #isGenerating(schema) {
    return this.#generating.has(schema);
  }
}
const PrototypicalCluster = new ClusterModel({ name: "Prototype" });
for (const attribute of Matter.all(AttributeModel)) {
  if (attribute.id === void 0 || attribute.name === "FeatureMap") {
    continue;
  }
  PrototypicalCluster.children.push(attribute.clone());
}
const GlobalSupervisor = new RootSupervisor(PrototypicalCluster);
for (const attribute of PrototypicalCluster.children) {
  if (attribute.id === void 0 || attribute.id === FeatureMap.id) {
    continue;
  }
  GlobalAttributeSupervisors[attribute.id] = GlobalSupervisor.get(attribute);
}
export {
  RootSupervisor
};
//# sourceMappingURL=RootSupervisor.js.map
