import { matches } from './matches.js'
/**
 * Checks whether an element matches the criteria of a custom group.
 *
 * Supports both single custom groups and "anyOf" groups (where matching any
 * subgroup is sufficient). For single groups, all specified criteria must
 * match. For "anyOf" groups, at least one subgroup must match.
 *
 * @example
 *
 * ```ts
 * // Single custom group
 * doesCustomGroupMatch({
 *   customGroup: {
 *     selector: 'property',
 *     modifiers: ['static'],
 *     elementNamePattern: 'on*',
 *   },
 *   elementName: 'onClick',
 *   selectors: ['property'],
 *   modifiers: ['static', 'readonly'],
 *   elementValue: null,
 *   decorators: [],
 * })
 * // Returns: true
 * ```
 *
 * @example
 *
 * ```ts
 * // AnyOf custom group
 * doesCustomGroupMatch({
 *   customGroup: {
 *     anyOf: [
 *       { selector: 'method' },
 *       { selector: 'property', modifiers: ['static'] },
 *     ],
 *   },
 *   elementName: 'foo',
 *   selectors: ['method'],
 *   modifiers: [],
 *   elementValue: null,
 * })
 * // Returns: true (matches first subgroup)
 * ```
 *
 * @template CustomGroupMatchOptions - Type of custom group match options.
 * @param props - Combined parameters including the custom group and element
 *   properties.
 * @returns True if the element matches the custom group criteria, false
 *   otherwise.
 */
function doesCustomGroupMatch(props) {
  if ('anyOf' in props.customGroup) {
    return props.customGroup.anyOf.some(subgroup =>
      doesSingleCustomGroupMatch({
        ...props,
        customGroup: subgroup,
      }),
    )
  }
  return doesSingleCustomGroupMatch({
    ...props,
    customGroup: props.customGroup,
  })
}
/**
 * Checks whether an element matches a single custom group's criteria.
 *
 * Tests each criterion in sequence, returning false as soon as any criterion
 * fails. All specified criteria must match for the function to return true. The
 * checks are performed in the following order:
 *
 * 1. Selector match (exact)
 * 2. Modifiers match (all required modifiers must be present)
 * 3. Element name pattern match
 * 4. Element value pattern match
 * 5. Decorator name pattern match (at least one decorator must match).
 *
 * @param params - Parameters for matching.
 * @param params.customGroup - Custom group configuration with matching
 *   criteria.
 * @param params.elementName - Name of the element to test.
 * @param params.elementValue - Optional value of the element.
 * @param params.selectors - Element's selectors.
 * @param params.modifiers - Element's modifiers.
 * @param params.decorators - Element's decorators.
 * @returns True if all specified criteria match, false otherwise.
 */
function doesSingleCustomGroupMatch({
  elementValue,
  customGroup,
  elementName,
  decorators,
  selectors,
  modifiers,
}) {
  if (customGroup.selector && !selectors?.includes(customGroup.selector)) {
    return false
  }
  if (customGroup.modifiers) {
    for (let modifier of customGroup.modifiers) {
      if (!modifiers?.includes(modifier)) {
        return false
      }
    }
  }
  if ('elementNamePattern' in customGroup && customGroup.elementNamePattern) {
    if (!matches(elementName, customGroup.elementNamePattern)) {
      return false
    }
  }
  if ('elementValuePattern' in customGroup && customGroup.elementValuePattern) {
    if (!matches(elementValue ?? '', customGroup.elementValuePattern)) {
      return false
    }
  }
  if (
    'decoratorNamePattern' in customGroup &&
    customGroup.decoratorNamePattern
  ) {
    let decoratorPattern = customGroup.decoratorNamePattern
    if (!decorators?.some(decorator => matches(decorator, decoratorPattern))) {
      return false
    }
  }
  return true
}
export { doesCustomGroupMatch }
