"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 ValueValidator_exports = {};
__export(ValueValidator_exports, {
  ValueValidator: () => ValueValidator
});
module.exports = __toCommonJS(ValueValidator_exports);
var import_general = require("@matter/general");
var import_model = require("@matter/model");
var import_protocol = require("@matter/protocol");
var import_types = require("@matter/types");
var import_SupervisionConfig = require("../../supervision/SupervisionConfig.js");
var import_Internal = require("../managed/Internal.js");
var import_assertions = require("./assertions.js");
var import_conformance = require("./conformance.js");
var import_constraint = require("./constraint.js");
var import_FabricIndexSentinel = require("./FabricIndexSentinel.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = import_general.Logger.get("ValueValidator");
function ValueValidator(schema, supervisor) {
  if (schema instanceof import_model.ClusterModel) {
    return createStructValidator(schema, supervisor);
  }
  let validator;
  const metabase = schema.metabase;
  switch (metabase?.metatype) {
    case import_model.Metatype.enum:
      validator = createEnumValidator(schema, supervisor);
      break;
    case import_model.Metatype.bitmap:
      validator = createBitmapValidator(schema, supervisor);
      break;
    case import_model.Metatype.integer:
      validator = createIntegerValidator(schema, supervisor, metabase.name);
      break;
    case import_model.Metatype.float:
      validator = createSimpleValidator(schema, supervisor, import_assertions.assertNumeric);
      break;
    case import_model.Metatype.boolean:
      validator = createSimpleValidator(schema, supervisor, import_assertions.assertBoolean);
      break;
    case import_model.Metatype.string:
      validator = createSimpleValidator(schema, supervisor, import_assertions.assertString);
      break;
    case import_model.Metatype.bytes:
      validator = createSimpleValidator(schema, supervisor, import_assertions.assertBytes);
      break;
    case import_model.Metatype.object:
      validator = createStructValidator(schema, supervisor);
      break;
    case import_model.Metatype.array:
      validator = createListValidator(schema, supervisor);
      break;
    case import_model.Metatype.date:
    case import_model.Metatype.duration:
    case import_model.Metatype.any:
      break;
    case void 0:
      const type = schema.effectiveType;
      if (type === void 0) {
        if (schema.isDisallowed || schema.isDeprecated && !schema.type) {
          break;
        }
        throw new import_protocol.SchemaImplementationError(new import_model.DataModelPath(schema.path), `No type defined`);
      }
      throw new import_protocol.SchemaImplementationError(
        new import_model.DataModelPath(schema.path),
        `Cannot determine metatype for type "${type}"`
      );
    default:
      throw new import_protocol.SchemaImplementationError(
        new import_model.DataModelPath(schema.path),
        `Unsupported validation metatype ${metabase?.metatype}`
      );
  }
  validator = createNullValidator(schema, validator);
  validator = (0, import_conformance.createConformanceValidator)(schema, supervisor, validator);
  return validator;
}
function createNullValidator(schema, nextValidator) {
  if (schema.effectiveQuality.nullable === true) {
    return (value, options, location) => {
      if (value !== null) {
        nextValidator?.(value, options, location);
      }
    };
  }
  return nextValidator;
}
function createEnumValidator(schema, supervisor) {
  const valid = new Set(
    supervisor.membersOf(schema).map((member) => member.id).filter((e) => e !== void 0)
  );
  const constraint = schema.effectiveConstraint;
  const constraintValidator = constraint.in ? (0, import_constraint.createConstraintValidator)(schema.effectiveConstraint, schema, supervisor) : void 0;
  return (value, session, location) => {
    (0, import_assertions.assertNumber)(value, location);
    if (!valid.has(value)) {
      throw new import_protocol.DatatypeError(location, "defined in enum", value, import_types.Status.ConstraintError);
    }
    constraintValidator?.(value, session, location);
  };
}
function addBitsToMask(mask, start, length) {
  if (mask === void 0 || start < 0 || length <= 0 || start + length > 32) {
    return void 0;
  }
  const lowMask = length >= 32 ? 4294967295 : 2 ** length - 1;
  return (mask | lowMask << start >>> 0) >>> 0;
}
function createBitmapValidator(schema, supervisor) {
  const fields = {};
  let definedMask = 0;
  for (const field of supervisor.membersOf(schema)) {
    const constraint = field.effectiveConstraint;
    let max;
    if (typeof constraint.min === "number" && typeof constraint.max === "number") {
      max = Math.pow(2, constraint.max - constraint.min + 1) - 1;
      definedMask = addBitsToMask(definedMask, constraint.min, constraint.max - constraint.min + 1);
    } else {
      max = 1;
      if (typeof constraint.value === "number") {
        definedMask = addBitsToMask(definedMask, constraint.value, 1);
      } else {
        definedMask = void 0;
      }
    }
    let name;
    if (field?.parent?.id === import_model.FeatureMap.id) {
      name = (0, import_general.camelize)(field.title ?? field.name);
    } else {
      name = field.propertyName;
    }
    fields[name] = {
      schema: field,
      max
    };
  }
  return (value, _session, location) => {
    (0, import_assertions.assertObject)(value, location);
    if (definedMask !== void 0) {
      const encoded = value[import_types.BitmapEncodedValue];
      if (typeof encoded === "number" && (encoded & ~definedMask) >>> 0 !== 0) {
        throw new import_protocol.DatatypeError(location, "free of reserved bits", encoded, import_types.Status.ConstraintError);
      }
    }
    for (const key in value) {
      const field = fields[key];
      const subpath = location.path.at(key);
      if (field === void 0) {
        throw new import_protocol.DatatypeError(subpath, "defined in bitmap", key);
      }
      const fieldValue = value[key];
      if (fieldValue === void 0) {
        continue;
      }
      if (field.max === 1) {
        (0, import_assertions.assertBoolean)(fieldValue, subpath);
      } else {
        (0, import_assertions.assertNumber)(fieldValue, subpath);
        if (fieldValue > field.max) {
          throw new import_protocol.DatatypeError(subpath, "in range of bit field", fieldValue);
        }
      }
    }
  };
}
function createSimpleValidator(schema, supervisor, validateType) {
  const validateConstraint = (0, import_constraint.createConstraintValidator)(schema.effectiveConstraint, schema, supervisor);
  return (value, session, location) => {
    if (value === void 0) {
      return;
    }
    validateType(value, location);
    validateConstraint?.(value, session, location);
  };
}
function createIntegerValidator(schema, supervisor, name) {
  const nullable = schema.effectiveQuality.nullable;
  let assertion;
  if (schema.type === "epoch-s" || schema.type === "epoch-us") {
    if (nullable) {
      assertion = import_assertions.assertInt.nullable[schema.type];
    } else {
      assertion = import_assertions.assertInt.notNullable[schema.type];
    }
  }
  if (assertion === void 0) {
    if (nullable) {
      assertion = import_assertions.assertInt.nullable[name];
    } else {
      assertion = import_assertions.assertInt.notNullable[name];
    }
  }
  if (assertion === void 0) {
    throw new import_general.InternalError(`No integer assertion implemented for integer type ${name}`);
  }
  const inner = createSimpleValidator(schema, supervisor, assertion);
  if ((0, import_FabricIndexSentinel.isFabricIndexSentinel)(schema)) {
    const fabricSentinelOrSubstitute = (value, session, location) => {
      const peerContext = session.clientPeerContext;
      if (value !== import_types.FabricIndex.OMIT_FABRIC || peerContext === void 0) {
        inner(value, session, location);
        return;
      }
      const siblings = location.siblings;
      if (siblings !== void 0 && peerContext.fabricIndexOnPeer !== void 0) {
        siblings.fabricIndex = peerContext.fabricIndexOnPeer;
        return;
      }
      logger.debug(
        "fabricIndex was not replaced with the peer's fabric index because it is not yet known; local cache will hold the OMIT_FABRIC sentinel (-1) until the peer's subscription delivers the real value at",
        location.path
      );
    };
    return fabricSentinelOrSubstitute;
  }
  return inner;
}
function createStructValidator(schema, supervisor) {
  const validators = {};
  for (const field of supervisor.membersOf(schema)) {
    if (import_model.AttributeModel.isGlobal(field) || field.isDeprecated && !field.type) {
      continue;
    }
    const validate = supervisor.get(field).validate;
    if (validate) {
      validators[field.propertyName] = validate;
    }
  }
  const validateStruct = (struct, session, location) => {
    (0, import_assertions.assertObject)(struct, location);
    const config = location.config ?? (0, import_SupervisionConfig.maybeConfigOf)(struct);
    const sublocation = {
      path: location.path.at(""),
      siblings: struct,
      choices: {},
      outerResolve: location.outerResolve,
      config
    };
    for (const name in validators) {
      let value;
      if (struct[import_protocol.Val.properties]) {
        const rootOwner = struct[import_Internal.Internal.reference];
        const properties = struct[import_protocol.Val.properties](rootOwner, session);
        if (name in properties) {
          value = properties[name];
        } else {
          value = struct[name];
        }
      } else {
        value = struct[name];
      }
      sublocation.path.id = name;
      sublocation.config = config?.readonlyChild?.(name);
      validators[name](value, session, sublocation);
    }
    for (const name in sublocation.choices) {
      const choice = sublocation.choices[name];
      if (choice.count < choice.target && !choice.orLess) {
        throw new import_protocol.ConformanceError(
          schema,
          location,
          `Too few fields present (${choice.count} of min ${choice.target})`,
          name
        );
      }
      if (choice.count > choice.target && !choice.orMore) {
        throw new import_protocol.ConformanceError(
          schema,
          location,
          `Too many fields present (${choice.count} of max ${choice.target})`,
          name
        );
      }
    }
  };
  return validateStruct;
}
function createListValidator(schema, supervisor) {
  const entry = schema.listEntry;
  let validateEntries;
  if (entry) {
    const entryValidator = supervisor.get(entry).validate;
    if (entryValidator) {
      validateEntries = (list, session, location) => {
        if (!list || typeof list[Symbol.iterator] !== "function") {
          throw new import_protocol.DatatypeError(location, "a list", list);
        }
        const entryConfig = location.config?.readonlyChild?.("entry");
        let index = 0;
        const sublocation = {
          path: location.path.at("")
        };
        for (const e of list) {
          if (e === void 0 || e === null) {
            continue;
          }
          sublocation.path.id = index;
          sublocation.config = location.config?.readonlyChild?.(index) ?? entryConfig;
          entryValidator(e, session, sublocation);
          index++;
        }
      };
    }
  }
  const validateConstraint = (0, import_constraint.createConstraintValidator)(schema.constraint, schema, supervisor);
  return (value, session, location) => {
    (0, import_assertions.assertArray)(value, location);
    validateConstraint?.(value, session, location);
    validateEntries?.(value, session, location);
  };
}
//# sourceMappingURL=ValueValidator.js.map
