/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { Conformance } from "@matter/model";
function resolveInterElementConformance(elements, schema, nameDependentElements) {
  const { interdependents, conditionals } = nameDependentElements;
  if (!interdependents.size) {
    applyConditionalResults(elements, nameDependentElements);
    return;
  }
  const elementToGroup = /* @__PURE__ */ new Map();
  const groups = [];
  for (const [name, entry] of interdependents) {
    const group = { members: [entry], depGroups: /* @__PURE__ */ new Set() };
    groups.push(group);
    elementToGroup.set(name, group);
  }
  let DfsState;
  ((DfsState2) => {
    DfsState2[DfsState2["Unvisited"] = 0] = "Unvisited";
    DfsState2[DfsState2["InProgress"] = 1] = "InProgress";
    DfsState2[DfsState2["Done"] = 2] = "Done";
  })(DfsState || (DfsState = {}));
  const dfsState = /* @__PURE__ */ new Map();
  const dfsStack = [];
  function dfs(name) {
    dfsState.set(name, 1 /* InProgress */);
    dfsStack.push(name);
    const info = interdependents.get(name);
    if (info) {
      for (const dep of info.dependencies) {
        if (!interdependents.has(dep)) {
          continue;
        }
        const state = dfsState.get(dep);
        if (state === 1 /* InProgress */) {
          const cycleStart = dfsStack.indexOf(dep);
          mergeGroups(dfsStack.slice(cycleStart), elementToGroup, groups);
        } else if (state === void 0) {
          dfs(dep);
        }
      }
    }
    dfsStack.pop();
    dfsState.set(name, 2 /* Done */);
  }
  for (const name of interdependents.keys()) {
    if (!dfsState.has(name)) {
      dfs(name);
    }
  }
  for (const [name, info] of interdependents) {
    const myGroup = elementToGroup.get(name);
    for (const dep of info.dependencies) {
      const depGroup = elementToGroup.get(dep);
      if (depGroup && depGroup !== myGroup) {
        myGroup.depGroups.add(depGroup);
      }
    }
  }
  const uniqueGroups = [...new Set(groups)];
  const sorted = topoSort(uniqueGroups);
  for (const group of sorted) {
    let anyDepPresent = false;
    for (const depGroup of group.depGroups) {
      if (depGroup.presence) {
        anyDepPresent = true;
        break;
      }
    }
    if (anyDepPresent) {
      group.presence = true;
    } else {
      if (group.members.length === 1) {
        const member = group.members[0];
        let resolvedDepPresent = false;
        for (const dep of member.dependencies) {
          if (!interdependents.has(dep)) {
            if (isResolvedElementPresent(elements, dep, schema) || conditionals.get(dep)?.presence) {
              resolvedDepPresent = true;
              break;
            }
          }
        }
        if (resolvedDepPresent) {
          group.presence = true;
        } else {
          switch (member.fallback) {
            case Conformance.Applicability.Mandatory:
              group.presence = true;
              break;
            case Conformance.Applicability.None:
              group.presence = false;
              break;
            case Conformance.Applicability.Optional:
              group.presence = member.isImplemented;
              break;
          }
        }
      } else {
        group.presence = group.members.every((m) => m.isImplemented);
      }
    }
    for (const member of group.members) {
      applyPresence(elements, member, group.presence);
    }
  }
  applyConditionalResults(elements, nameDependentElements);
}
function isResolvedElementPresent(elements, depName, schema) {
  const conformant = schema.conformant;
  const model = conformant.attributes(depName) ?? conformant.commands(depName) ?? conformant.events(depName);
  if (!model) {
    return false;
  }
  const propName = model.propertyName;
  return elements.attributes.has(propName) || elements.commands.has(propName) || elements.events.has(propName);
}
function applyConditionalResults(elements, nameDependentElements) {
  for (const [, conditional] of nameDependentElements.conditionals) {
    applyPresence(elements, conditional, conditional.presence);
  }
}
function applyPresence(elements, entry, presence) {
  const propName = entry.model.propertyName;
  if (presence && !entry.isImplemented) {
    const target = entry.elementType === "attribute" ? `State.${propName}` : entry.elementType === "command" ? propName : `cluster.events.${propName}`;
    if (entry.elementType === "command") {
      const isPresent = elements.presentCommands.has(propName);
      elements.error(target, isPresent ? "Throws unimplemented exception" : "Implementation missing", !isPresent);
    } else {
      elements.error(target, "Mandatory element unsupported", false);
    }
  } else if (!presence && entry.isImplemented) {
    const target = entry.elementType === "attribute" ? `State.${propName}` : entry.elementType === "command" ? propName : `cluster.events.${propName}`;
    elements.error(target, "Element should not be present", false);
    switch (entry.elementType) {
      case "attribute":
        elements.attributes.delete(propName);
        elements.attributeIds.delete(propName);
        break;
      case "command":
        elements.commands.delete(propName);
        break;
      case "event":
        elements.events.delete(propName);
        break;
    }
  }
}
function mergeGroups(names, elementToGroup, groups) {
  if (names.length < 2) {
    return;
  }
  const target = elementToGroup.get(names[0]);
  for (let i = 1; i < names.length; i++) {
    const other = elementToGroup.get(names[i]);
    if (other === target) {
      continue;
    }
    for (const member of other.members) {
      if (!target.members.includes(member)) {
        target.members.push(member);
      }
      elementToGroup.set(member.model.name, target);
    }
    for (const dep of other.depGroups) {
      if (dep !== target) {
        target.depGroups.add(dep);
      }
    }
    const idx = groups.indexOf(other);
    if (idx !== -1) {
      groups.splice(idx, 1);
    }
  }
  target.depGroups.delete(target);
}
function topoSort(groups) {
  const inDegree = /* @__PURE__ */ new Map();
  const dependents = /* @__PURE__ */ new Map();
  for (const g of groups) {
    for (const dep of g.depGroups) {
      let list = dependents.get(dep);
      if (!list) {
        list = [];
        dependents.set(dep, list);
      }
      list.push(g);
    }
    inDegree.set(g, g.depGroups.size);
  }
  const queue = [];
  for (const g of groups) {
    if (inDegree.get(g) === 0) {
      queue.push(g);
    }
  }
  const result = [];
  while (queue.length) {
    const g = queue.shift();
    result.push(g);
    const deps = dependents.get(g);
    if (deps) {
      for (const dependent of deps) {
        const newDegree = inDegree.get(dependent) - 1;
        inDegree.set(dependent, newDegree);
        if (newDegree === 0) {
          queue.push(dependent);
        }
      }
    }
  }
  return result;
}
export {
  resolveInterElementConformance
};
//# sourceMappingURL=inter-element-conformance.js.map
