"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("#general");
var import_model = require("#model");
var import_errors = require("../../errors.js");
var import_conformance_util = require("./conformance-util.js");
/**
 * @license
 * Copyright 2022-2024 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
function astToFunction(schema, featureMap, supportedFeatures) {
  const ast = schema.conformance.ast;
  const { featuresAvailable, featuresSupported } = import_model.FeatureSet.normalize(featureMap, supportedFeatures);
  const compiledNode = compile(ast);
  let validator;
  switch (compiledNode.code) {
    case import_conformance_util.Code.Conformant:
      validator = requireValue;
      break;
    case import_conformance_util.Code.Nonconformant:
    case import_conformance_util.Code.Disallowed:
      validator = disallowValue;
      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:
            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 {
              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.Group:
        return createGroup(ast2.param);
      case import_model.Conformance.Special.Name:
        return createName(ast2.param);
      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.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_errors.SchemaImplementationError(
          (0, 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
    };
    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_errors.SchemaImplementationError(
        (0, import_model.DataModelPath)(schema.path),
        "Conformance AST group parameter is not an array"
      );
    }
    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;
      } else {
        return import_conformance_util.NonconformantNode;
      }
    } else {
      param = (0, import_general.camelize)(param);
      return {
        code: import_conformance_util.Code.Evaluate,
        evaluate: (_value, options) => {
          return {
            code: import_conformance_util.Code.Value,
            value: options?.siblings?.[param]
          };
        }
      };
    }
  }
  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 compiledRhs;
          } else {
            return import_conformance_util.NonconformantNode;
          }
        }
        if ((0, import_conformance_util.isStatic)(compiledRhs)) {
          if ((0, import_conformance_util.asBoolean)(compiledRhs)) {
            return 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 compiledRhs;
          }
        }
        if ((0, import_conformance_util.isStatic)(compiledRhs)) {
          if ((0, import_conformance_util.asBoolean)(compiledRhs)) {
            return import_conformance_util.ConformantNode;
          } else {
            return 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 compiledRhs;
          }
        }
        if ((0, import_conformance_util.isStatic)(compiledRhs)) {
          if ((0, import_conformance_util.asBoolean)(compiledRhs)) {
            return (0, import_conformance_util.createLogicalInversion)(compiledLhs);
          } else {
            return compiledLhs;
          }
        }
        return (0, import_conformance_util.createLogicalBinaryEvaluator)(compiledLhs, compiledRhs, (lhs2, rhs2) => lhs2 ? !rhs2 : rhs2);
      default:
        throw new import_errors.SchemaImplementationError(
          (0, import_model.DataModelPath)(schema.path),
          `Unknown logical binary operator ${operator}`
        );
    }
  }
  function createComparisonOp(operator, { lhs, rhs }) {
    return (0, import_conformance_util.createComparison)(operator, compile(lhs), compile(rhs), schema);
  }
  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_errors.ConformanceError(schema, location, "Matter requires you to set this attribute");
    }
  }
  function disallowValue(value, _session, location) {
    if (value !== void 0) {
      throw new import_errors.ConformanceError(schema, location, "Matter does not allow you to set this attribute");
    }
  }
  function disallowEnumValue(schema2) {
    return (location) => {
      throw new import_errors.ConformanceError(
        schema2,
        location,
        `Matter does not allow enum value ${schema2.name} (ID ${schema2.effectiveId}) here`
      );
    };
  }
  function addEnumMemberValidation(mainValidator) {
    const members = schema.activeMembers;
    if (!members.length) {
      return mainValidator;
    }
    let memberValidators;
    for (const member of members) {
      const id = member.effectiveId;
      if (id === void 0) {
        continue;
      }
      const compiledNode2 = compile(member.conformance.ast);
      let memberValidator;
      switch (compiledNode2.code) {
        case import_conformance_util.Code.Conformant:
        case import_conformance_util.Code.Optional:
          continue;
        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") {
        memberValidators[value]?.(location);
      }
    };
  }
}
//# sourceMappingURL=conformance-compiler.js.map
