"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 MergedModel_exports = {};
__export(MergedModel_exports, {
  MergedModel: () => MergedModel
});
module.exports = __toCommonJS(MergedModel_exports);
var import_general = require("@matter/general");
var import_aspects = require("../aspects/index.js");
var import_common = require("../common/index.js");
var import_models = require("../models/index.js");
var import_ModelVariantTraversal = require("./ModelVariantTraversal.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
function MergedModel(revision, variants, priorities = MergedModel.DefaultPriorities) {
  const priority = new PriorityHandler(priorities || MergedModel.DefaultPriorities);
  const visitor = new MergeTraversal(revision, priority, (variants2, recurse) => {
    const merged = merge(variants2);
    if (variants2.tag === import_common.ElementTag.Cluster) {
      reparentToCanonicalParent(revision, priority, variants2);
    }
    const manual = priority.get(variants2.tag, "type")[0];
    if (merged.type && variants2.map[manual]?.type === merged.type && !variants2.map[manual].children?.length) {
      return merged;
    }
    if (merged.type) {
      for (const variant of Object.values(variants2.map)) {
        if (variant.type === void 0) {
          variant.type = merged.type;
        }
      }
    }
    merged.children = recurse();
    return merged;
  });
  return visitor.traverse(variants);
  function merge(variants2) {
    const variantValues = Object.fromEntries(
      Object.entries(variants2.map).map(([variantName, variant]) => [variantName, variant.valueOf()])
    );
    const keys = new Set(Object.values(variantValues).flatMap((v) => Object.keys(v)));
    keys.delete("children");
    const properties = Object.fromEntries([...keys].map((k) => [k, visitor.pluck(variants2.tag, k, variantValues)]));
    if (properties.type) {
      const type = visitor.chooseType(variants2);
      if (type?.type !== void 0 && type?.type !== null) {
        properties.type = type.type;
      }
    }
    const constraint = new import_aspects.Constraint(properties.constraint);
    if (constraint.desc) {
      for (const key of priority.get("*", "constraint")) {
        const definition = variants2.map[key]?.constraint;
        const constraint2 = new import_aspects.Constraint(definition);
        if (!constraint2?.isEmpty && !constraint2?.desc) {
          properties.constraint = definition;
          break;
        }
      }
    }
    return import_models.Model.create(properties);
  }
}
class MergeTraversal extends import_ModelVariantTraversal.ModelVariantTraversal {
  constructor(revision, priority, visitor) {
    super(revision, priority.get("*", "type"));
    this.priority = priority;
    this.visitor = visitor;
  }
  priority;
  visitor;
  visit(variants, recurse) {
    return this.visitor(variants, recurse);
  }
  /**
   * Use priority rules to select a single value from available variants.
   */
  pluck(tag, fieldName, variantValues) {
    const variantPriorities = this.priority.get(tag, fieldName);
    for (const variantName of variantPriorities) {
      const variantValue = variantValues[variantName]?.[fieldName];
      if (variantValue !== void 0) {
        return variantValue;
      }
    }
  }
  /**
   * Type selection is more complicated than other fields.
   */
  chooseType(variants) {
    const variantPriorities = this.priority.get(variants.tag, "type");
    let type;
    let metatype;
    for (const sourceName of variantPriorities) {
      const variant = variants.map[sourceName];
      if (!variant) {
        continue;
      }
      if (sourceName === variantPriorities[0]) {
        return variant;
      }
      if (!type) {
        type = variant;
        if (type instanceof import_models.ValueModel) {
          metatype = type.effectiveMetatype;
        }
        continue;
      }
      if (!(type instanceof import_models.ValueModel) || !(variant instanceof import_models.ValueModel)) {
        continue;
      }
      let overridePriority;
      const variantMetatype = variant.effectiveMetatype;
      if (!metatype) {
        if (variantMetatype) {
          overridePriority = true;
        }
      } else if (metatype === import_common.Metatype.integer) {
        const variantMetatype2 = variant.effectiveMetatype;
        if (variantMetatype2 === import_common.Metatype.enum || variantMetatype2 === import_common.Metatype.bitmap) {
          overridePriority = true;
        }
      }
      if (overridePriority) {
        type = variant;
        metatype = variantMetatype;
      }
    }
    return type;
  }
}
class PriorityHandler {
  constructor(priorities) {
    this.priorities = priorities;
  }
  priorities;
  /**
   * Get the priority for a specific tag and field.
   */
  get(typeName, fieldName) {
    const priority = this.priorities[typeName]?.[fieldName] || this.priorities["*"]?.[fieldName] || this.priorities[typeName]?.["*"] || this.priorities["*"]?.["*"];
    if (!priority) {
      throw new import_general.InternalError("No default (*, *) priority available");
    }
    return priority;
  }
}
function reparentToCanonicalParent(revision, priority, variants) {
  const deparented = Array();
  const traversal = new MergeTraversal(revision, priority, (variants2, recurse) => {
    const type = traversal.chooseType(variants2);
    if (!(type instanceof import_models.ValueModel)) {
      recurse();
      return;
    }
    if (type.base?.isSeed && import_common.Metatype.hasChildren(type.effectiveMetatype)) {
      for (const variantName in variants2.map) {
        const variant = variants2.map[variantName];
        if (variant === type || variant.children.length) {
          continue;
        }
        const base = variant.base;
        if (!(base instanceof import_models.ValueModel) || base.parent?.tag !== import_common.ElementTag.Cluster || !base.children.length) {
          continue;
        }
        if (base.parent?.references(base).length > 1) {
          continue;
        }
        deparented.push(base);
        variant.children = base.children;
        variant.type = base.type;
      }
    }
    recurse();
  });
  traversal.traverse(variants.map);
  deparented.forEach((m) => m.parent = void 0);
  return variants;
}
((MergedModel2) => {
  MergedModel2.DefaultPriorities = {
    "*": {
      "*": ["local", "chip", "spec"],
      // Prefer spec for elements that are insufficiently defined in chip
      conformance: ["local", "spec", "chip"],
      constraint: ["local", "spec", "chip"],
      quality: ["local", "spec", "chip"],
      access: ["local", "spec", "chip"],
      // Prefer spec for element names
      name: ["local", "spec", "chip"],
      // Prefer spec for datatype names (must match element names)
      type: ["local", "spec", "chip"],
      // Prefer spec for detailed documentation
      details: ["local", "spec", "chip"],
      // Prefer spec for default values
      default: ["local", "spec", "chip"]
    }
  };
})(MergedModel || (MergedModel = {}));
//# sourceMappingURL=MergedModel.js.map
