"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("#general");
var import_model = require("#model");
var import_types = require("#types");
var import_errors = require("../../errors.js");
var import_Val = require("../Val.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");
/**
 * @license
 * Copyright 2022-2024 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
function ValueValidator(schema, factory) {
  if (schema instanceof import_model.ClusterModel) {
    return createStructValidator(schema, factory) ?? (() => {
    });
  }
  let validator;
  const metatype = schema.effectiveMetatype;
  switch (metatype) {
    case import_model.Metatype.enum:
      validator = createEnumValidator(schema);
      break;
    case import_model.Metatype.bitmap:
      validator = createBitmapValidator(schema);
      break;
    case import_model.Metatype.integer:
    case import_model.Metatype.float:
      validator = createSimpleValidator(schema, import_assertions.assertNumeric);
      break;
    case import_model.Metatype.boolean:
      validator = createSimpleValidator(schema, import_assertions.assertBoolean);
      break;
    case import_model.Metatype.string:
      validator = createSimpleValidator(schema, import_assertions.assertString);
      break;
    case import_model.Metatype.bytes:
      validator = createSimpleValidator(schema, import_assertions.assertBytes);
      break;
    case import_model.Metatype.object:
      validator = createStructValidator(schema, factory);
      break;
    case import_model.Metatype.array:
      validator = createListValidator(schema, factory);
      break;
    case import_model.Metatype.date:
    case import_model.Metatype.any:
      break;
    case void 0:
      const type = schema.effectiveType;
      if (type === void 0) {
        if (schema.isDisallowed) {
          break;
        }
        throw new import_errors.SchemaImplementationError((0, import_model.DataModelPath)(schema.path), `No type defined`);
      }
      throw new import_errors.SchemaImplementationError(
        (0, import_model.DataModelPath)(schema.path),
        `Cannot determine metatype for type "${type}"`
      );
    default:
      throw new import_errors.SchemaImplementationError(
        (0, import_model.DataModelPath)(schema.path),
        `Unsupported validation metatype ${metatype}`
      );
  }
  validator = createNullValidator(schema, validator);
  validator = (0, import_conformance.createConformanceValidator)(schema, factory.featureMap, factory.supportedFeatures, 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) {
  const valid = new Set(schema.activeMembers.map((member) => member.id).filter((e) => e !== void 0));
  const constraint = schema.effectiveConstraint;
  const constraintValidator = constraint.in ? (0, import_constraint.createConstraintValidator)(schema.effectiveConstraint, schema) : void 0;
  return (value, session, location) => {
    (0, import_assertions.assertNumber)(value, location);
    if (!valid.has(value)) {
      throw new import_errors.DatatypeError(location, "defined in enum", value, import_types.StatusCode.ConstraintError);
    }
    constraintValidator?.(value, session, location);
  };
}
function createBitmapValidator(schema) {
  const fields = {};
  for (const field of schema.activeMembers) {
    const constraint = field.effectiveConstraint;
    let max;
    if (typeof constraint.min === "number" && typeof constraint.max === "number") {
      max = constraint.max - constraint.min;
    } else {
      max = 1;
    }
    let name;
    if (field?.parent?.id === import_model.FeatureMap.id) {
      name = (0, import_general.camelize)(field.description ?? field.name);
    } else {
      name = (0, import_general.camelize)(field.name);
    }
    fields[name] = {
      schema: field,
      max
    };
  }
  return (value, _session, location) => {
    (0, import_assertions.assertObject)(value, location);
    for (const key in value) {
      const field = fields[key];
      const subpath = location.path.at(key);
      if (field === void 0) {
        throw new import_errors.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_errors.DatatypeError(subpath, "in range of bit field", fieldValue);
        }
      }
    }
  };
}
function createSimpleValidator(schema, validateType) {
  const validateConstraint = (0, import_constraint.createConstraintValidator)(schema.effectiveConstraint, schema);
  return (value, session, location) => {
    if (value === void 0) {
      return;
    }
    validateType(value, location);
    validateConstraint?.(value, session, location);
  };
}
function createStructValidator(schema, factory) {
  const validators = {};
  for (const field of schema.activeMembers) {
    if (field.isDeprecated || import_model.AttributeModel.isGlobal(field)) {
      continue;
    }
    const validate = factory.get(field).validate;
    if (validate) {
      validators[(0, import_general.camelize)(field.name)] = validate;
    }
  }
  const validateStruct = (struct, session, location) => {
    (0, import_assertions.assertObject)(struct, location);
    const sublocation = {
      path: location.path.at(""),
      siblings: struct,
      choices: {}
    };
    for (const name in validators) {
      let value;
      if (struct[import_Val.Val.properties]) {
        const rootOwner = struct[import_Internal.Internal.reference];
        const properties = struct[import_Val.Val.properties](rootOwner, session);
        if (name in properties) {
          value = properties[name];
        } else {
          value = struct[name];
        }
      } else {
        value = struct[name];
      }
      sublocation.path.id = name;
      validators[name](value, session, sublocation);
    }
    for (const name in sublocation.choices) {
      const choice = sublocation.choices[name];
      if (choice.count < choice.target) {
        throw new import_errors.ConformanceError(
          schema,
          location,
          `Too few fields present (${choice.count} of min ${choice.target})`,
          name
        );
      }
      if (choice.count > choice.target && !choice.orMore) {
        throw new import_errors.ConformanceError(
          schema,
          location,
          `Too many fields present (${choice.count} of max ${choice.target})`,
          name
        );
      }
    }
  };
  return validateStruct;
}
function createListValidator(schema, factory) {
  const entry = schema.listEntry;
  let validateEntries;
  if (entry) {
    const entryValidator = factory.get(entry).validate;
    if (entryValidator) {
      validateEntries = (list, session, location) => {
        if (!list || typeof list[Symbol.iterator] !== "function") {
          throw new import_errors.DatatypeError(location, "a list", list);
        }
        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;
          entryValidator(e, session, sublocation);
          index++;
        }
      };
    }
  }
  const validateConstraint = (0, import_constraint.createConstraintValidator)(schema.constraint, schema);
  return (value, session, location) => {
    (0, import_assertions.assertArray)(value, location);
    validateConstraint?.(value, session, location);
    validateEntries?.(value, session, location);
  };
}
//# sourceMappingURL=ValueValidator.js.map
