"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 conformance_compiler_exports = {};
__export(conformance_compiler_exports, {
  astToFunction: () => astToFunction
});
module.exports = __toCommonJS(conformance_compiler_exports);
var import_general = require("@matter/general");
var import_model = require("@matter/model");
var import_protocol = require("@matter/protocol");
var import_NameResolver = require("../managed/NameResolver.js");
var import_conformance_util = require("./conformance-util.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const enumValueOk = () => {
};
function astToFunction(schema, supervisor) {
  const ast = schema.conformance.ast;
  const { featuresAvailable, featuresSupported } = import_model.FeatureSet.normalize(
    supervisor.featureMap,
    supervisor.supportedFeatures
  );
  let createNameReference = (name) => {
    const camelized = (0, import_general.camelize)(name);
    const resolver = (0, import_NameResolver.NameResolver)(supervisor, schema.parent, camelized);
    if (resolver) {
      return {
        code: import_conformance_util.Code.Evaluate,
        evaluate: (_value, options) => {
          return {
            code: import_conformance_util.Code.Value,
            value: resolver(options?.siblings)
          };
        }
      };
    }
    return {
      code: import_conformance_util.Code.Evaluate,
      evaluate: (_value, options) => {
        return {
          code: import_conformance_util.Code.Value,
          value: options?.outerResolve?.(camelized)
        };
      }
    };
  };
  const isNullable = schema.quality.nullable;
  const compiledNode = compile(ast);
  let validator;
  switch (compiledNode.code) {
    case import_conformance_util.Code.Conformant:
      if (isNullable) {
        break;
      }
      validator = requireValue;
      break;
    case import_conformance_util.Code.Nonconformant:
    case import_conformance_util.Code.Disallowed:
      validator = disallowValue;
      break;
    case import_conformance_util.Code.Value:
      validator = compiledNode.value === void 0 ? disallowValue : isNullable ? void 0 : requireValue;
      break;
    case import_conformance_util.Code.Optional:
      break;
    case import_conformance_util.Code.Evaluate:
      const { evaluate } = compiledNode;
      validator = (value, session, location) => {
        const staticNode = evaluate(value, location);
        switch (staticNode.code) {
          case import_conformance_util.Code.Conformant:
            if (!isNullable) {
              requireValue(value, session, location);
            }
            break;
          case import_conformance_util.Code.Nonconformant:
          case import_conformance_util.Code.Disallowed:
            disallowValue(value, session, location);
            break;
          case import_conformance_util.Code.Optional:
            break;
          case import_conformance_util.Code.Value:
            if (staticNode.value === void 0) {
              disallowValue(value, session, location);
            } else if (!isNullable) {
              requireValue(value, session, location);
            }
            break;
          default:
            throw new import_conformance_util.UnsupportedConformanceNodeError(schema, compiledNode);
        }
      };
      break;
    default:
      throw new import_conformance_util.UnsupportedConformanceNodeError(schema, compiledNode);
  }
  if (validator !== disallowValue && schema.effectiveMetatype === import_model.Metatype.enum) {
    validator = addEnumMemberValidation(validator);
  }
  return validator;
  function compile(ast2) {
    switch (ast2?.type) {
      case import_model.Conformance.Special.Empty:
      case import_model.Conformance.Special.Desc:
      case import_model.Conformance.Flag.Provisional:
      case import_model.Conformance.Flag.Deprecated:
      case import_model.Conformance.Flag.Optional:
        return { code: import_conformance_util.Code.Optional };
      case import_model.Conformance.Special.Choice:
        return createChoice(ast2.param);
      case import_model.Conformance.Special.Otherwise:
        return createGroup(ast2.param);
      case import_model.Conformance.Special.Name:
        return createName(ast2.param);
      case import_model.Conformance.Special.Revision: {
        const revision = supervisor.schema.revision ?? 1;
        return revision >= ast2.param ? import_conformance_util.ConformantNode : import_conformance_util.NonconformantNode;
      }
      case import_model.Conformance.Special.OptionalIf:
        return createOptionalIf(ast2.param);
      case import_model.Conformance.Special.Value:
        return createValue(ast2.param);
      case import_model.Conformance.Flag.Disallowed:
        return createDisallowed();
      case import_model.Conformance.Flag.Mandatory:
        return createMandatory();
      case import_model.Conformance.Operator.DOT:
        return createDotAccess(ast2.param);
      case import_model.Conformance.Operator.AND:
      case import_model.Conformance.Operator.OR:
      case import_model.Conformance.Operator.XOR:
        return createLogicalBinaryOp(ast2.type, ast2.param);
      case import_model.Conformance.Operator.EQ:
      case import_model.Conformance.Operator.NE:
      case import_model.Conformance.Operator.GT:
      case import_model.Conformance.Operator.LT:
      case import_model.Conformance.Operator.GTE:
      case import_model.Conformance.Operator.LTE:
        return createComparisonOp(ast2.type, ast2.param);
      case import_model.Conformance.Operator.NOT:
        return createNotOp(ast2.param);
      default:
        ast2;
        throw new import_protocol.SchemaImplementationError(
          new import_model.DataModelPath(schema.path),
          `Unsupported conformance AST node type ${ast2.type}`
        );
    }
  }
  function createChoice(param) {
    const compiled = compile(param.expr);
    const name = param.name;
    const template = {
      count: 0,
      target: param.num,
      orMore: !!param.orMore,
      orLess: !!param.orLess
    };
    return {
      code: import_conformance_util.Code.Evaluate,
      evaluate: (value, options) => {
        const choices = options?.choices;
        let choice;
        if (choices) {
          choice = choices[name];
          if (!choice) {
            choice = choices[name] = { ...template };
          }
          if (value !== void 0) {
            choice.count++;
          }
        }
        return (0, import_conformance_util.evaluateNode)(compiled, value, options);
      }
    };
  }
  function createGroup(param) {
    if (!Array.isArray(param)) {
      throw new import_protocol.SchemaImplementationError(
        new import_model.DataModelPath(schema.path),
        "Conformance AST group parameter is not an array"
      );
    }
    if (param[param.length - 1]?.type === import_model.Conformance.Flag.Deprecated) {
      param = param.slice(0, param.length - 1);
    }
    const members = param.map((test) => compile(test));
    const reduced = Array();
    for (const member of members) {
      if (member.code === import_conformance_util.Code.Nonconformant) {
        continue;
      }
      if (member.code === import_conformance_util.Code.Optional || member.code === import_conformance_util.Code.Conformant) {
        if (reduced.length) {
          reduced.push(member);
          break;
        }
        return member;
      }
      reduced.push(member);
    }
    return {
      code: import_conformance_util.Code.Evaluate,
      evaluate: (value, options) => {
        for (const member of reduced) {
          const node = (0, import_conformance_util.evaluateNode)(member, value, options);
          if (node.code !== import_conformance_util.Code.Nonconformant) {
            return node;
          }
        }
        return import_conformance_util.NonconformantNode;
      }
    };
  }
  function createName(param) {
    if (featuresAvailable.has(param)) {
      if (featuresSupported.has(param)) {
        return import_conformance_util.ConformantNode;
      }
      return import_conformance_util.NonconformantNode;
    }
    const resolved = schema.parent?.resolve(param, {
      tags: [import_model.ElementTag.Command, import_model.ElementTag.Event]
    });
    if (resolved) {
      return import_conformance_util.ConformantNode;
    }
    return createNameReference(param);
  }
  function createDotAccess({ lhs, rhs }) {
    let compiledLhs = compile(lhs);
    if (rhs.type !== import_model.Conformance.Special.Name) {
      throw new import_protocol.SchemaImplementationError(
        new import_model.DataModelPath(schema.path),
        `DOT operator RHS must be a name, got ${rhs.type}`
      );
    }
    const propertyName = (0, import_general.camelize)(rhs.param);
    if (compiledLhs.code === import_conformance_util.Code.Conformant && lhs.type === import_model.Conformance.Special.Name) {
      compiledLhs = createNameReference(lhs.param);
    }
    if (compiledLhs.code === import_conformance_util.Code.Value) {
      const lhsVal = compiledLhs.value;
      if (lhsVal === void 0 || lhsVal === null) {
        return { code: import_conformance_util.Code.Value, value: void 0 };
      }
      return { code: import_conformance_util.Code.Value, value: lhsVal[propertyName] };
    }
    if (compiledLhs.code === import_conformance_util.Code.Evaluate) {
      const { evaluate } = compiledLhs;
      return {
        code: import_conformance_util.Code.Evaluate,
        evaluate: (value, options) => {
          const lhsResult = evaluate(value, options);
          if (lhsResult.code !== import_conformance_util.Code.Value || lhsResult.value === void 0 || lhsResult.value === null) {
            return { code: import_conformance_util.Code.Value, value: void 0 };
          }
          return { code: import_conformance_util.Code.Value, value: lhsResult.value[propertyName] };
        }
      };
    }
    return { code: import_conformance_util.Code.Value, value: void 0 };
  }
  function createOptionalIf(param) {
    let node = compile(param) ?? {};
    node = (0, import_conformance_util.asConformance)(node);
    switch (node.code) {
      case import_conformance_util.Code.Conformant:
        return { code: import_conformance_util.Code.Optional };
      case import_conformance_util.Code.Evaluate:
        const evaluate = node.evaluate;
        return {
          code: import_conformance_util.Code.Evaluate,
          evaluate: (value, options) => {
            let staticNode = evaluate(value, options);
            staticNode = (0, import_conformance_util.asConformance)(staticNode);
            if (staticNode.code === import_conformance_util.Code.Conformant) {
              return { code: import_conformance_util.Code.Optional };
            }
            return staticNode;
          }
        };
      default:
        return node;
    }
  }
  function createValue(param) {
    return {
      code: import_conformance_util.Code.Value,
      value: param
    };
  }
  function createDisallowed() {
    return {
      code: import_conformance_util.Code.Disallowed
    };
  }
  function createMandatory() {
    return import_conformance_util.ConformantNode;
  }
  function createLogicalBinaryOp(operator, { lhs, rhs }) {
    const compiledLhs = (0, import_conformance_util.createBooleanTest)(compile(lhs));
    const compiledRhs = (0, import_conformance_util.createBooleanTest)(compile(rhs));
    switch (operator) {
      case import_model.Conformance.Operator.AND:
        if ((0, import_conformance_util.isStatic)(compiledLhs)) {
          if ((0, import_conformance_util.asBoolean)(compiledLhs)) {
            return asNonvalue(compiledRhs);
          } else {
            return import_conformance_util.NonconformantNode;
          }
        }
        if ((0, import_conformance_util.isStatic)(compiledRhs)) {
          if ((0, import_conformance_util.asBoolean)(compiledRhs)) {
            return asNonvalue(compiledLhs);
          } else {
            return import_conformance_util.NonconformantNode;
          }
        }
        return (0, import_conformance_util.createLogicalBinaryEvaluator)(compiledLhs, compiledRhs, (lhs2, rhs2) => lhs2 && rhs2);
      case import_model.Conformance.Operator.OR:
        if ((0, import_conformance_util.isStatic)(compiledLhs)) {
          if ((0, import_conformance_util.asBoolean)(compiledLhs)) {
            return import_conformance_util.ConformantNode;
          } else {
            return asNonvalue(compiledRhs);
          }
        }
        if ((0, import_conformance_util.isStatic)(compiledRhs)) {
          if ((0, import_conformance_util.asBoolean)(compiledRhs)) {
            return import_conformance_util.ConformantNode;
          } else {
            return asNonvalue(compiledLhs);
          }
        }
        return (0, import_conformance_util.createLogicalBinaryEvaluator)(compiledLhs, compiledRhs, (lhs2, rhs2) => lhs2 || rhs2);
      case import_model.Conformance.Operator.XOR:
        if ((0, import_conformance_util.isStatic)(compiledLhs)) {
          if ((0, import_conformance_util.asBoolean)(compiledLhs)) {
            return (0, import_conformance_util.createLogicalInversion)(compiledRhs);
          } else {
            return asNonvalue(compiledRhs);
          }
        }
        if ((0, import_conformance_util.isStatic)(compiledRhs)) {
          if ((0, import_conformance_util.asBoolean)(compiledRhs)) {
            return (0, import_conformance_util.createLogicalInversion)(compiledLhs);
          } else {
            return asNonvalue(compiledLhs);
          }
        }
        return (0, import_conformance_util.createLogicalBinaryEvaluator)(compiledLhs, compiledRhs, (lhs2, rhs2) => lhs2 ? !rhs2 : rhs2);
      default:
        throw new import_protocol.SchemaImplementationError(
          new import_model.DataModelPath(schema.path),
          `Unknown logical binary operator ${operator}`
        );
    }
  }
  function createComparisonOp(operator, { lhs, rhs }) {
    const originalCreateNameReference = createNameReference;
    try {
      if (lhs.type === import_model.Conformance.Special.Name) {
        const name = (0, import_general.camelize)(lhs.param, false);
        const siblingScope = schema.parent instanceof import_model.ValueModel || schema.parent instanceof import_model.ClusterModel ? schema.parent : void 0;
        const field = siblingScope && supervisor.membersOf(siblingScope).find((model) => model.propertyName === name);
        if (field?.effectiveMetatype === import_model.Metatype.enum) {
          let enumValues;
          createNameReference = (name2) => {
            if (enumValues === void 0) {
              enumValues = {};
              for (const member of supervisor.membersOf(field)) {
                enumValues[(0, import_general.camelize)(member.name, true)] = member.id;
              }
            }
            const id = enumValues[(0, import_general.camelize)(name2, true)];
            if (id !== void 0) {
              return {
                code: import_conformance_util.Code.Value,
                value: id
              };
            }
            return originalCreateNameReference(name2);
          };
        }
      }
      return (0, import_conformance_util.createComparison)(operator, compile(lhs), compile(rhs), schema);
    } finally {
      createNameReference = originalCreateNameReference;
    }
  }
  function createNotOp(param) {
    const operand = compile(param);
    return (0, import_conformance_util.createLogicalInversion)(operand);
  }
  function requireValue(value, _session, location) {
    if (value === void 0) {
      throw new import_protocol.ConformanceError(schema, location, "Matter requires you to set this attribute");
    }
  }
  function disallowValue(value, _session, location) {
    if (value !== void 0) {
      throw new import_protocol.ConformanceError(schema, location, "Matter does not allow you to set this attribute");
    }
  }
  function disallowEnumValue(schema2) {
    return (location) => {
      throw new import_protocol.EnumValueConformanceError(
        schema2,
        location,
        `Matter does not allow enum value ${schema2.name} (ID ${schema2.effectiveId}) here`
      );
    };
  }
  function asNonvalue(node) {
    if (node.code === import_conformance_util.Code.Value) {
      return (0, import_conformance_util.asBoolean)(node) ? import_conformance_util.ConformantNode : import_conformance_util.NonconformantNode;
    }
    return node;
  }
  function addEnumMemberValidation(mainValidator) {
    const members = supervisor.membersOf(schema);
    if (!members.length) {
      return mainValidator;
    }
    let memberValidators;
    for (const member of members) {
      const id = member.effectiveId;
      if (id === void 0) {
        continue;
      }
      let ast2 = member.conformance.ast;
      if (ast2.type === import_model.Conformance.Special.Choice) {
        ast2 = ast2.param.expr;
      }
      const compiledNode2 = compile(ast2);
      let memberValidator;
      switch (compiledNode2.code) {
        case import_conformance_util.Code.Conformant:
        case import_conformance_util.Code.Optional:
          memberValidator = enumValueOk;
          break;
        case import_conformance_util.Code.Nonconformant:
        case import_conformance_util.Code.Disallowed:
          memberValidator = disallowEnumValue(member);
          break;
        case import_conformance_util.Code.Evaluate:
          const { evaluate } = compiledNode2;
          const disallow = disallowEnumValue(member);
          memberValidator = (location) => {
            const staticNode = evaluate(true, location);
            switch (staticNode.code) {
              case import_conformance_util.Code.Conformant:
              case import_conformance_util.Code.Optional:
                break;
              case import_conformance_util.Code.Nonconformant:
              case import_conformance_util.Code.Disallowed:
                disallow(location);
                break;
              default:
                throw new import_conformance_util.UnsupportedConformanceNodeError(member, compiledNode2);
            }
          };
          break;
        default:
          throw new import_conformance_util.UnsupportedConformanceNodeError(member, compiledNode2);
      }
      if (!memberValidators) {
        memberValidators = {};
      }
      memberValidators[id] = memberValidator;
    }
    if (memberValidators === void 0) {
      return mainValidator;
    }
    return (value, session, location) => {
      mainValidator?.(value, session, location);
      if (typeof value === "number") {
        if (!memberValidators[value]) {
          throw new import_protocol.UnknownEnumValueError(
            location,
            `Matter does not define the enum value ${value} for ${schema.name}`
          );
        }
        memberValidators[value](location);
      }
    };
  }
}
//# sourceMappingURL=conformance-compiler.js.map
