/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { isDeepEqual, NotImplementedError } from "@matter/general";
import { AttributeModel } from "../../models/index.js";
function InferredComponents(cluster) {
  const result = [];
  cluster.allAces.forEach((child) => addElement(result, child));
  return result;
}
function pattern(...parts) {
  parts = parts.map((p) => {
    switch (p) {
      case "[":
      case "]":
      case "(":
      case ")":
        return `\\${p}`;
      default:
        return p;
    }
  });
  return new RegExp(`^${parts.join("")}$`);
}
const FEATURE_NAME = "[A-Z_][A-Z_0-9]+";
const FEATURE = `(${FEATURE_NAME})`;
const CONJUNCT_FEATURES = `(${FEATURE_NAME}(?: & ${FEATURE_NAME})*)`;
const DISJUNCT_FEATURES = CONJUNCT_FEATURES.replace(/&/g, "[|,]");
const FIELD = "[A-Z][A-Za-z_$]*[a-z][A-Za-z_$]*";
const CONJUNCT_FIELDS = `(${FIELD}(?: & ${FIELD})*)`;
const DISJUNCT_FIELDS = CONJUNCT_FIELDS.replace(/&/g, "[|,]");
const AND = " & ";
const NOT = "!";
function splitConjunction(conjunction) {
  return conjunction.split(" & ");
}
function splitDisjunction(disjunction) {
  return disjunction.split(" | ");
}
const VarianceMatchers = [
  // Mandatory, unconditional
  {
    pattern: pattern("(?:|M)"),
    processor(add) {
      add();
    }
  },
  // Optional, unconditional
  {
    pattern: pattern("(?:O|desc)"),
    processor(add) {
      add(true);
    }
  },
  // fieldName (optional, unconditional).  Ignores field reference which can only be enforced at runtime
  {
    pattern: pattern(FIELD),
    processor(add) {
      add(true);
    }
  },
  // [fieldName] (optional, unconditional).  Ignores field reference which can only be enforced at runtime
  {
    pattern: pattern("[", FIELD, "]"),
    processor(add) {
      add(true);
    }
  },
  // fieldName > num (optional, unconditional).  Ignores field expression
  {
    pattern: pattern(FIELD, " > ", "\\d+"),
    processor(add) {
      add(true);
    }
  },
  // fieldName, O (optional, unconditional).  Ignores field reference
  {
    pattern: pattern(FIELD, ", ", "O"),
    processor(add) {
      add(true);
    }
  },
  // FOO & fieldName (field ignored as must be runtime enforced)
  {
    pattern: pattern(FEATURE, AND, FIELD),
    processor(add, match) {
      add(true, { allOf: match });
    }
  },
  // FOO<& BAR>*
  {
    pattern: pattern(CONJUNCT_FEATURES),
    processor(add, match) {
      add(false, { allOf: splitConjunction(match[0]) });
    }
  },
  // [FOO<& BAR>*]
  {
    pattern: pattern("[", CONJUNCT_FEATURES, "]"),
    processor(add, match) {
      add(true, { allOf: splitConjunction(match[0]) });
    }
  },
  // [FOO<& BAR>*].a+ (e.g. [LF & PA_LF].a+)
  {
    pattern: pattern("[", CONJUNCT_FEATURES, "]", "\\.[a-z][0-9-]*[+\\-]?"),
    processor(add, match) {
      add(true, { allOf: splitConjunction(match[0]) });
    }
  },
  // [FOO & !BAR]
  {
    pattern: pattern("[", FEATURE, AND, NOT, FEATURE, "]"),
    processor(add, match) {
      add(true, { allOf: [match[0]], not: match[1] });
    }
  },
  // [FOO<| BAR>+]
  {
    pattern: pattern("[", DISJUNCT_FEATURES, "]"),
    processor(add, match) {
      add(true, { anyOf: splitDisjunction(match[0]) });
    }
  },
  // FOO<| BAR>* or FOO<, BAR>*
  {
    pattern: pattern(DISJUNCT_FEATURES),
    processor(add, match) {
      add(false, { anyOf: splitDisjunction(match[0]) });
    }
  },
  // Field<| Field>* or Field<, Field>* (optional, unconditional).  Ignores field references
  {
    pattern: pattern(DISJUNCT_FIELDS),
    processor(add) {
      add(true);
    }
  },
  // [Field<| Field>*] or [Field<, Field>*] (optional, unconditional.  Ignores field references)
  {
    pattern: pattern("[", DISJUNCT_FIELDS, "]"),
    processor(add) {
      add(true);
    }
  },
  // FOO, [BAR]
  {
    pattern: pattern(FEATURE, ", ", "[", FEATURE, "]"),
    processor(add, match) {
      add(false, { allOf: [match[0]] });
      add(true, { allOf: [match[1]] });
    }
  },
  // FOO & (BAR | BIZ | ...)
  {
    pattern: pattern(FEATURE, AND, "\\(", DISJUNCT_FEATURES, "\\)"),
    processor(add, match) {
      add(false, { allOf: [match[0]], anyOf: splitDisjunction(match[1]) });
    }
  },
  // FOO & !BAR
  {
    pattern: pattern(FEATURE, AND, NOT, FEATURE),
    processor(add, match) {
      add(false, { allOf: [match[0]], not: match[1] });
    }
  },
  // !FOO & BAR
  {
    pattern: pattern(NOT, FEATURE, AND, FEATURE),
    processor(add, match) {
      add(false, { allOf: [match[1]], not: match[0] });
    }
  },
  // !FOO & [BAR]
  {
    pattern: pattern(NOT, FEATURE, AND, "[", FEATURE, "]"),
    processor(add, match) {
      add(true, { allOf: [match[1]], not: match[0] });
    }
  },
  // !FOO & (BAR<| BIZ>*)
  {
    pattern: pattern(NOT, FEATURE, AND, "(", DISJUNCT_FEATURES, ")"),
    processor(add, match) {
      add(false, { anyOf: splitDisjunction(match[1]), not: match[0] });
    }
  },
  // !FOO
  {
    pattern: pattern(NOT, FEATURE),
    processor(add, match) {
      add(false, { not: match[0] });
    }
  },
  // [!FOO]
  {
    pattern: pattern("[", NOT, FEATURE, "]"),
    processor(add, match) {
      add(true, { not: match[0] });
    }
  },
  // [!FOO & BAR]
  {
    pattern: pattern("[", NOT, FEATURE, AND, FEATURE, "]"),
    processor(add, match) {
      add(true, { not: match[0], allOf: [match[1]] });
    }
  },
  // FOO & BAR, [BIZ]
  {
    pattern: pattern(FEATURE, AND, FEATURE, ", ", "[", FEATURE, "]"),
    processor(add, match) {
      add(false, { allOf: match.slice(0, 2) });
      add(true, { allOf: [match[2]] });
    }
  },
  // FOO, O
  {
    pattern: pattern(FEATURE, ", ", "O"),
    processor(add, match) {
      add(false, { allOf: [match[0]] });
      add(true);
    }
  },
  // Handles a bunch of super ugly conformances in deprecated occupancy sensing attributes.  They all effectively
  // become optional if certain features are enabled.  We use the presence of the "HoldTime" field to reliably detect
  // these and ensure these rules aren't too greedy
  {
    pattern: pattern(".*HoldTime", AND, "\\(?", FEATURE, ".*"),
    processor(add, match) {
      add(true, { allOf: [match[0]] });
    }
  }
];
function addElement(components, element) {
  if (AttributeModel.isGlobal(element)) {
    return;
  }
  let text = element.conformance.toString();
  if (text === "X") {
    return;
  }
  while (true) {
    if (text.match(/^[DP], /)) {
      text = text.substring(3);
    } else {
      break;
    }
  }
  if (text === "D") {
    text = "O";
  } else if (text === "M, D" || text === "P") {
    text = "M";
  }
  let hasRevision = false;
  let hasBareRevision = false;
  const stripped = text.replace(/(\[?)Rev >= v\d+]?(?:, )?/g, (_, bracket) => {
    hasRevision = true;
    if (!bracket) hasBareRevision = true;
    return "";
  }).replace(/,\s*$/, "").trim();
  if (hasRevision) {
    text = stripped || (hasBareRevision ? "M" : "O");
  }
  for (const matcher of VarianceMatchers) {
    const match = text.match(matcher.pattern);
    if (match) {
      matcher.processor((optional, condition) => {
        addVariance(components, element, optional, condition);
      }, match.slice(1));
      return;
    }
  }
  throw new NotImplementedError(`New rule needed for conformance "${element.conformance}" (element ${element.path})`);
}
function addVariance(components, element, optional, condition) {
  let into;
  for (const existing of components) {
    if (isDeepEqual(existing.condition, condition)) {
      into = existing;
      break;
    }
  }
  if (!into) {
    into = {
      condition,
      optional: [],
      mandatory: []
    };
    if (condition) {
      components.push(into);
    } else {
      components.unshift(into);
    }
  }
  if (optional) {
    into.optional.push(element);
  } else {
    into.mandatory.push(element);
  }
}
export {
  InferredComponents
};
//# sourceMappingURL=InferredComponents.js.map
