"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 StructManager_exports = {};
__export(StructManager_exports, {
  StructManager: () => StructManager
});
module.exports = __toCommonJS(StructManager_exports);
var import_general = require("#general");
var import_model = require("#model");
var import_protocol = require("#protocol");
var import_Instrumentation = require("../Instrumentation.js");
var import_Internal = require("../Internal.js");
var import_ManagedReference = require("../ManagedReference.js");
var import_NameResolver = require("../NameResolver.js");
var import_PrimitiveManager = require("./PrimitiveManager.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const AUTHORIZE_READ = /* @__PURE__ */ Symbol("authorize-read");
function StructManager(owner, schema) {
  const prototypeDescriptors = {
    // TODO - interferes with Chai deep equal.  Best fix would probably be a custom deep equal assertion but
    // leaving out for now
    // [Symbol.toStringTag]: {
    //     value: name,
    // },
    // TODO - makes Mocha diffs pretty useless.  Best fix is probably customized diff but leaving out for now
    // toString: {
    //     value() {
    //         return serialize(this);
    //     }
    // },
    // AUTHORIZE_READ is effectively a protected method, see StructManager.assertDirectReadAuthorized below
    [AUTHORIZE_READ]: {
      value(attributeId) {
        const access = propertyAccessControls[attributeId];
        if (access === void 0) {
          throw new import_general.ImplementationError(`Direct read of unknown property ${attributeId}`);
        }
        access.authorizeRead(this[import_Internal.Internal.session], this[import_Internal.Internal.reference].location);
      }
    }
  };
  const instanceDescriptors = {};
  const propertyAccessControls = {};
  let hasFabricIndex = false;
  const isCluster = schema.tag === import_model.ElementTag.Cluster;
  for (const member of owner.membersOf(schema)) {
    const name2 = (0, import_general.camelize)(member.name);
    const { access, descriptor } = configureProperty(owner, member);
    instanceDescriptors[name2] = descriptor;
    if (member.id !== void 0) {
      prototypeDescriptors[member.id] = { ...descriptor, enumerable: false };
      propertyAccessControls[member.id] = access;
    }
    if (member.name === "FabricIndex") {
      hasFabricIndex = true;
    }
  }
  let name = schema.name;
  if (schema.tag === import_model.ElementTag.Cluster && !name.endsWith("$State")) {
    name = `${name}$State`;
  }
  const Wrapper = (0, import_general.GeneratedClass)({
    name,
    initialize(ref, session) {
      Object.defineProperties(this, {
        [import_Internal.Internal.reference]: {
          value: ref
        },
        [import_Internal.Internal.session]: {
          value: session
        }
      });
      if (!(0, import_general.isObject)(ref.value)) {
        throw new import_protocol.SchemaImplementationError(
          ref.location,
          `Cannot manage ${typeof ref.value} because it is not a struct`
        );
      }
      if (hasFabricIndex) {
        const owningFabric = ref.value.fabricIndex;
        ref.location = { ...ref.location, owningFabric };
      }
      if (!isCluster || !session.protocol) {
        Object.defineProperties(this, instanceDescriptors);
      }
    },
    instanceDescriptors: prototypeDescriptors
  });
  import_Instrumentation.Instrumentation.instrumentStruct(Wrapper);
  return (reference, session) => {
    reference.owner = new Wrapper(reference, session);
    return reference.owner;
  };
}
((StructManager2) => {
  function assertDirectReadAuthorized(struct, attributeId) {
    if (!struct?.[AUTHORIZE_READ]) {
      throw new import_general.ImplementationError("Cannot authorize read of unmanaged value");
    }
    return struct[AUTHORIZE_READ](attributeId);
  }
  StructManager2.assertDirectReadAuthorized = assertDirectReadAuthorized;
})(StructManager || (StructManager = {}));
function configureProperty(supervisor, schema) {
  const name = (0, import_general.camelize)(schema.name);
  const id = schema.id;
  const { access, manage, validate } = supervisor.get(schema);
  const fabricScopedList = schema.effectiveAccess.fabric === import_model.Access.Fabric.Scoped && schema.effectiveMetatype === import_model.Metatype.array;
  let defaultReader;
  if (typeof import_model.FieldValue.referenced(schema.default) === "string") {
    defaultReader = (0, import_NameResolver.NameResolver)(supervisor, schema.parent, (0, import_general.camelize)(import_model.FieldValue.referenced(schema.default)));
  }
  const descriptor = {
    enumerable: true,
    set(value) {
      access.authorizeWrite(this[import_Internal.Internal.session], this[import_Internal.Internal.reference].location);
      const pk = this[import_Internal.Internal.reference].primaryKey;
      let key = pk === "id" ? id ?? name : name;
      let storedKey;
      if (key in this[import_Internal.Internal.reference].value) {
        storedKey = key;
      } else if (pk === "id") {
        if (name in this[import_Internal.Internal.reference].value) {
          storedKey = name;
        }
      } else if (id !== void 0) {
        if (id in this[import_Internal.Internal.reference].value) {
          storedKey = id;
        }
      }
      const oldValue = storedKey === void 0 ? void 0 : this[import_Internal.Internal.reference].value[storedKey];
      const self = this;
      this[import_Internal.Internal.reference].change(() => {
        const struct = this[import_Internal.Internal.reference].value;
        let target;
        if (import_protocol.Val.properties in struct) {
          const properties = struct[import_protocol.Val.properties](
            this[import_Internal.Internal.reference].rootOwner,
            this[import_Internal.Internal.session]
          );
          if (name in properties) {
            key = storedKey = name;
            target = properties;
          } else {
            target = struct;
          }
        } else {
          target = struct;
        }
        if (value && value[import_Internal.Internal.reference]) {
          value = value[import_Internal.Internal.reference].value;
        }
        if (fabricScopedList && Array.isArray(value) && Array.isArray(oldValue)) {
          const proxy = self[name];
          for (let i = 0; i < value.length; i++) {
            proxy[i] = value[i];
          }
          proxy.length = value.length;
        } else {
          target[key] = value;
          if (storedKey !== void 0 && storedKey !== key) {
            delete target[storedKey];
          }
        }
        if (!this[import_Internal.Internal.session].acceptInvalid && validate) {
          try {
            validate(value, this[import_Internal.Internal.session], {
              path: this[import_Internal.Internal.reference].location.path,
              siblings: struct
            });
          } catch (e) {
            target[key] = oldValue;
            throw e;
          }
        }
      });
    }
  };
  if (manage === import_PrimitiveManager.PrimitiveManager) {
    descriptor.get = function() {
      if (access.mayRead(this[import_Internal.Internal.session], this[import_Internal.Internal.reference].location)) {
        const struct = this[import_Internal.Internal.reference].value;
        if (struct === void 0) {
          throw new import_protocol.PhantomReferenceError(this[import_Internal.Internal.reference].location);
        }
        if (struct[import_protocol.Val.properties]) {
          const properties = struct[import_protocol.Val.properties](
            this[import_Internal.Internal.reference].rootOwner,
            this[import_Internal.Internal.session]
          );
          if (name in properties) {
            return properties[name];
          }
        }
        const key = this[import_Internal.Internal.reference].primaryKey === "id" ? id ?? name : name;
        if (key in struct) {
          return struct[key];
        }
        const key2 = this[import_Internal.Internal.reference].primaryKey === "id" ? name : id;
        if (key2 !== void 0 && key2 in struct) {
          return struct[key2];
        }
        return void 0;
      }
    };
  } else {
    let cloneContainer;
    switch (schema.effectiveMetatype) {
      case import_model.Metatype.array:
        cloneContainer = (container) => [...container];
        break;
      case import_model.Metatype.bitmap:
        cloneContainer = (container) => {
          if (typeof container === "number" || typeof container === "bigint") {
            return {};
          }
          return { ...container };
        };
        break;
      default:
        cloneContainer = (container) => ({ ...container });
    }
    descriptor.get = function() {
      let value;
      const pk = this[import_Internal.Internal.reference].primaryKey;
      const struct = this[import_Internal.Internal.reference].value;
      const key = pk === "id" ? id ?? name : name;
      if (struct[import_protocol.Val.properties]) {
        const properties = struct[import_protocol.Val.properties](
          this[import_Internal.Internal.reference].rootOwner,
          this[import_Internal.Internal.session]
        );
        if (name in properties) {
          value = properties[name];
        } else {
          value = struct[name];
        }
      } else {
        if (key in struct) {
          value = struct[key];
        } else {
          const key2 = pk === "id" ? id : name;
          if (key2 !== void 0 && key2 in struct) {
            value = struct[key2];
          }
        }
      }
      if (!access.mayRead(this[import_Internal.Internal.session], this[import_Internal.Internal.reference].location)) {
        return void 0;
      }
      if (value === void 0) {
        return defaultReader?.(this);
      }
      if (value === null) {
        return value;
      }
      const managed = this[import_Internal.Internal.reference].subrefs?.[name];
      if (managed) {
        return managed.owner;
      }
      const assertWriteOk = (value2) => {
        access.authorizeWrite(this[import_Internal.Internal.session], this[import_Internal.Internal.reference].location);
        if (validate) {
          validate(value2, this[import_Internal.Internal.session], {
            path: this[import_Internal.Internal.reference].location.path,
            siblings: this[import_Internal.Internal.reference].value
          });
        }
      };
      const ref = (0, import_ManagedReference.ManagedReference)(
        this[import_Internal.Internal.reference],
        pk,
        name,
        id,
        assertWriteOk,
        cloneContainer,
        this[import_Internal.Internal.session]
      );
      ref.owner = manage(ref, this[import_Internal.Internal.session]);
      return ref.owner;
    };
  }
  return { descriptor, access };
}
//# sourceMappingURL=StructManager.js.map
