"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_ModelTraversal = require("#logic/ModelTraversal.js");
var import_general = require("@matter/general");
var import_aspects = require("../../aspects/index.js");
var import_common = require("../../common/index.js");
var import_elements = require("../../elements/index.js");
var import_models = require("../../models/index.js");
var import_ModelValidator = require("./ModelValidator.js");
var import_ValidationExceptions = require("./ValidationExceptions.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
class ValueValidator extends import_ModelValidator.ModelValidator {
  validate() {
    this.validateProperty({ name: "type", type: "string" });
    this.validateProperty({ name: "byteSize", type: "number" });
    this.validateProperty({ name: "constraint", type: import_aspects.Constraint });
    this.validateProperty({ name: "conformance", type: import_aspects.Conformance });
    this.validateProperty({ name: "access", type: import_aspects.Access });
    this.validateProperty({ name: "quality", type: import_aspects.Quality });
    this.validateProperty({ name: "metatype", type: import_common.Metatype });
    this.#validateAspect("conformance");
    this.model.conformance.validateReferences(this, (name) => this.resolveReference(name));
    this.model.conformance.validateComputation(this, this.model.owner(import_models.ClusterModel)?.definedFeatures);
    this.#validateAspect("constraint");
    this.#validateAspect("access");
    this.#validateAspect("quality");
    this.#validateType();
    this.#validateEntries();
    super.validate();
  }
  /**
   * Resolve a conformance reference.  Features (all-caps strings) resolve against the cluster's feature map.  Field
   * references resolve via {@link Model.resolve}.  Qualified names arrive as `string[]` segments from DOT AST nodes.
   */
  resolveReference(name) {
    if (typeof name === "string") {
      if (name.match(/^[A-Z0-9_$]+$/)) {
        const cluster = this.model.owner(import_models.ClusterModel);
        return cluster?.features.find((f) => f.name === name);
      }
      name = [name];
    }
    const path = name.map((s) => (0, import_general.camelize)(s, true));
    return this.model.parent?.resolve(path, this.resolveOptions());
  }
  /**
   * Options for {@link Model.resolve} during reference validation.
   *
   * Provides an {@link Model.ResolveOptions.outerResolve} that extends resolution beyond the normal scope boundary.
   * Currently this enables response command fields to reference request command fields via qualified names (e.g.,
   * "Foo.Bar" referencing field Bar in request command Foo).
   */
  resolveOptions() {
    return {
      outerResolve: (path, boundaryScope) => {
        const command = this.model.owner(import_models.CommandModel);
        if (command?.direction !== import_elements.CommandElement.Direction.Response) {
          return void 0;
        }
        const cluster = boundaryScope instanceof import_models.ClusterModel ? boundaryScope : void 0;
        const request = cluster?.commands.find((c) => c.effectiveResponse === command.name);
        if (!request || (0, import_general.camelize)(path[0]) !== (0, import_general.camelize)(request.name)) {
          return void 0;
        }
        if (path.length < 2) {
          return request;
        }
        return request.resolve(path.slice(1));
      }
    };
  }
  #validateAspect(name) {
    const aspect = this.model[name];
    if (aspect?.errors) {
      aspect.errors.forEach((e) => this.model.error(e.code, `${e.source}: ${e.message}`));
    }
  }
  #validateType() {
    if (this.model.effectiveType === void 0) {
      if (this.model.metatype) {
        return;
      }
      if (this.model.isDeprecated || this.model.isDisallowed) {
        return;
      }
      if (!this.#correctCaseFromShadow()) {
        this.error("NO_TYPE", "No type information");
        return;
      }
    }
    const base = this.model.base;
    if (base === void 0) {
      return;
    }
    const metabase = this.model.metabase;
    if (metabase === void 0) {
      this.error("METATYPE_UNKNOWN", `No metatype for ${this.model.name}`);
      return;
    }
    const metatype = metabase.metatype;
    if (metatype === void 0) {
      this.error("METATYPE_MISSING", `Metabase ${metabase.name} has no metatype`);
      return;
    }
    let defaultValue = this.model.default;
    if (defaultValue === void 0) {
      return;
    }
    if (this.#validateSpecialDefault(metatype, defaultValue)) {
      return;
    }
    if (metatype === import_common.Metatype.string && defaultValue === "empty") {
      delete this.model.default;
      return;
    }
    const cast = import_common.FieldValue.cast(metatype, defaultValue);
    if (typeof defaultValue === "string") {
      let referenced = this.model?.member(defaultValue);
      if (referenced === void 0) {
        referenced = this.model.owner(import_models.ClusterModel)?.member(defaultValue);
      }
      if (referenced instanceof import_models.ValueModel && referenced.effectiveType === this.model.effectiveType) {
        this.model.default = import_common.FieldValue.Reference(referenced.name);
        return;
      }
    }
    if (cast === import_common.FieldValue.Invalid) {
      this.error("INVALID_VALUE", `Value "${defaultValue}" is not a ${metatype}`);
      return;
    }
    defaultValue = cast;
    if (metatype === import_common.Metatype.enum) {
      if (typeof defaultValue === "string") {
        let member = this.model.member(defaultValue);
        if (!member) {
          member = this.model.member(
            (model) => model.name.toLowerCase() === defaultValue.toLowerCase()
          );
        }
        if (member && member.effectiveId !== void 0) {
          defaultValue = member.effectiveId;
        } else {
          this.error("INVALID_ENTRY", `"${defaultValue}" is not in ${metatype} ${this.model.type}`);
        }
      }
    }
    this.model.default = defaultValue;
  }
  #validateEntries() {
    const metatype = this.model.type === void 0 ? void 0 : import_models.Globals[this.model.type]?.metatype;
    switch (metatype) {
      case import_common.Metatype.object:
        if (!this.model.children.length) {
          this.error("CHILDLESS_STRUCT", `struct element with no children`);
        }
        break;
      case import_common.Metatype.enum:
      case import_common.Metatype.bitmap:
        const base = this.model.base;
        if (!base || !base.isSeed || !base.name.startsWith("enum") || this.model.parent?.name === "semtag") {
          break;
        }
        if (!this.model.members.length && !import_ValidationExceptions.ValidationExceptions.AllowedEmptyEnums.has(this.model.path)) {
          this.error(`CHILDLESS_${metatype.toUpperCase()}`, `${this.model.type} with no children`);
        }
        if (metatype === import_common.Metatype.enum) {
          this.#validateEnumKeys();
        } else {
          this.#validateBitFields();
        }
        break;
      case import_common.Metatype.array:
        if (!this.model.children.length) {
          this.error("UNTYPED_ARRAY", `array element with no entry type`);
        } else if (this.model.children.length > 1) {
          this.error("OVERLY_TYPED_ARRAY", `array element with multiple entry types`);
        }
        break;
    }
  }
  #validateEnumKeys() {
    const ids = /* @__PURE__ */ new Set();
    const names = /* @__PURE__ */ new Set();
    for (const c of this.model.children) {
      if (c.id) {
        if (ids.has(c.id)) {
          this.error(
            "DUPLICATE_ENUM_ID",
            `${this.model.type} ID 0x${c.id.toString(16)} appears more than once`
          );
        } else {
          ids.add(c.id);
        }
      }
      if (names.has(c.name)) {
        this.error("DUPLICATE_ENUM_NAME", `${this.model.type} name "${c.name}" appears more than once`);
      }
    }
  }
  #validateBitFields() {
    const ranges = Array();
    for (const c of this.model.children) {
      let min, max;
      if (typeof c.constraint.value === "number") {
        min = c.constraint.value;
        max = c.constraint.value + 1;
      } else {
        min = c.constraint.min;
        max = c.constraint.max;
        if (typeof min !== "number" || typeof max !== "number" || min < 0 || min > max) {
          this.error(
            "UNCONSTRAINED_BIT_RANGE",
            `${this.model.type} bit field "${c.name}" is not properly constrained`
          );
          continue;
        }
      }
      for (const r of ranges) {
        if (min < r.max && max > r.min) {
          this.error(
            "OVERLAPPING_BIT_RANGE",
            `${this.model.type} bit fields "${r.name}" and "${c.name}" overlap`
          );
        }
      }
      ranges.push({ name: c.name, min, max });
    }
  }
  #validateSpecialDefault(metatype, def) {
    if (typeof def === "object" && import_common.FieldValue.is(def, import_common.FieldValue.reference)) {
      const reference = def.name;
      const parent = this.model.parent;
      let other = parent?.member(reference);
      if (!other) {
        const cluster = parent?.owner(import_models.ClusterModel);
        other = cluster?.member(reference);
        if (other === void 0) {
          this.error("MEMBER_UNKNOWN", `Default value references unknown property ${reference}`);
        }
      }
      return true;
    }
    if (typeof def === "string") {
      const other = this.model.parent?.member(def);
      if (other) {
        this.model.default = import_common.FieldValue.Reference(other.name);
        return true;
      }
    }
    if (metatype === import_common.Metatype.bitmap && Array.isArray(def)) {
      for (const value of def) {
        if (typeof value !== "string" && typeof value !== "number") {
          this.error("INVALID_BIT_FLAG", `Default bit flag ${def} is not a string or number`);
          continue;
        }
        if (!this.model.member(value)) {
          this.error("UNRESOLVED_BIT_FLAG", `Default bit flag ${def} is not a valid bit value`);
        }
      }
      return true;
    }
  }
  #correctCaseFromShadow() {
    const tag = this.model.tag;
    const name = this.model.name.toLowerCase();
    return false === new import_ModelTraversal.ModelTraversal().visitInheritance(this.model.parent?.base, (owner) => {
      for (const child of owner.children) {
        if (child.tag === tag && child.name.toLowerCase() === name) {
          this.model.name = child.name;
          return false;
        }
      }
    });
  }
}
//# sourceMappingURL=ValueValidator.js.map
