"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_errors = require("../../../errors.js");
var import_Val = require("../../Val.js");
var import_Instrumentation = require("../Instrumentation.js");
var import_Internal = require("../Internal.js");
var import_ManagedReference = require("../ManagedReference.js");
var import_PrimitiveManager = require("./PrimitiveManager.js");
/**
 * @license
 * Copyright 2022-2024 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const SESSION = Symbol("options");
const AUTHORIZE_READ = Symbol("authorize-read");
function StructManager(owner, schema) {
  const instanceDescriptors = {};
  const propertyAccessControls = {};
  let hasFabricIndex = false;
  for (const member of schema.activeMembers) {
    const name2 = (0, import_general.camelize)(member.name);
    const { access, descriptor } = configureProperty(owner, member);
    instanceDescriptors[name2] = descriptor;
    propertyAccessControls[name2] = 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) {
      if (!(0, import_general.isObject)(ref.value)) {
        throw new import_errors.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 };
      }
      Object.defineProperties(this, {
        [import_Internal.Internal.reference]: {
          value: ref
        },
        [SESSION]: {
          value: session
        }
      });
      Object.defineProperties(this, instanceDescriptors);
    },
    instanceDescriptors: {
      // 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(index) {
          const access = propertyAccessControls[index];
          if (access === void 0) {
            throw new import_general.ImplementationError(`Direct read of unknown property ${index}`);
          }
          access.authorizeRead(this[SESSION], this[import_Internal.Internal.reference].location);
        }
      }
    }
  });
  import_Instrumentation.Instrumentation.instrumentStruct(Wrapper);
  return (reference, session) => {
    reference.owner = new Wrapper(reference, session);
    return reference.owner;
  };
}
((StructManager2) => {
  function assertDirectReadAuthorized(struct, index) {
    if (!struct?.[AUTHORIZE_READ]) {
      throw new import_general.ImplementationError("Cannot authorize read of unmanaged value");
    }
    return struct[AUTHORIZE_READ](index);
  }
  StructManager2.assertDirectReadAuthorized = assertDirectReadAuthorized;
})(StructManager || (StructManager = {}));
function configureProperty(manager, schema) {
  const name = (0, import_general.camelize)(schema.name);
  const { access, manage, validate } = manager.get(schema);
  const fabricScopedList = schema.effectiveAccess.fabric === import_model.Access.Fabric.Scoped && schema.effectiveMetatype === import_model.Metatype.array;
  const descriptor = {
    enumerable: true,
    set(value) {
      access.authorizeWrite(this[SESSION], this[import_Internal.Internal.reference].location);
      const oldValue = this[import_Internal.Internal.reference].value[name];
      const self = this;
      this[import_Internal.Internal.reference].change(() => {
        const struct = this[import_Internal.Internal.reference].value;
        let target;
        if (struct[import_Val.Val.properties]) {
          const properties = struct[import_Val.Val.properties](
            this[import_Internal.Internal.reference].rootOwner,
            this[SESSION]
          );
          if (name in properties) {
            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(target[name])) {
          const proxy = self[name];
          for (let i = 0; i < value.length; i++) {
            proxy[i] = value[i];
          }
          proxy.length = value.length;
        } else {
          target[name] = value;
        }
        if (!this[SESSION].acceptInvalid && validate) {
          try {
            validate(value, this[SESSION], {
              path: this[import_Internal.Internal.reference].location.path,
              siblings: struct
            });
          } catch (e) {
            target[name] = oldValue;
            throw e;
          }
        }
      });
    }
  };
  if (manage === import_PrimitiveManager.PrimitiveManager) {
    descriptor.get = function() {
      if (access.mayRead(this[SESSION], this[import_Internal.Internal.reference].location)) {
        const struct = this[import_Internal.Internal.reference].value;
        if (struct === void 0) {
          throw new import_errors.PhantomReferenceError(this[import_Internal.Internal.reference].location);
        }
        if (struct[import_Val.Val.properties]) {
          const properties = struct[import_Val.Val.properties](this[import_Internal.Internal.reference].rootOwner, this[SESSION]);
          if (name in properties) {
            return properties[name];
          }
        }
        return struct[name];
      }
    };
  } 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 struct = this[import_Internal.Internal.reference].value;
      if (struct[import_Val.Val.properties]) {
        const properties = struct[import_Val.Val.properties](
          this[import_Internal.Internal.reference].rootOwner,
          this[SESSION]
        );
        if (name in properties) {
          value = properties[name];
        } else {
          value = struct[name];
        }
      } else {
        value = struct[name];
      }
      if (value === void 0) {
        return;
      }
      if (!access.mayRead(this[SESSION], this[import_Internal.Internal.reference].location)) {
        return void 0;
      }
      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[SESSION], this[import_Internal.Internal.reference].location);
        if (validate) {
          validate(value2, this[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], name, assertWriteOk, cloneContainer);
      ref.owner = manage(ref, this[SESSION]);
      return ref.owner;
    };
  }
  return { descriptor, access };
}
//# sourceMappingURL=StructManager.js.map
