import { populateSortingNodeGroupsWithDependencies } from '../../utils/populate-sorting-node-groups-with-dependencies.js'
import { validateNewlinesAndPartitionConfiguration } from '../../utils/validate-newlines-and-partition-configuration.js'
import { buildOptionsByGroupIndexComputer } from '../../utils/build-options-by-group-index-computer.js'
import { validateCustomSortConfiguration } from '../../utils/validate-custom-sort-configuration.js'
import { validateGroupsConfiguration } from '../../utils/validate-groups-configuration.js'
import { sortNodesByDependencies } from '../../utils/sort-nodes-by-dependencies.js'
import { getEslintDisabledLines } from '../../utils/get-eslint-disabled-lines.js'
import { doesCustomGroupMatch } from '../../utils/does-custom-group-match.js'
import { isNodeEslintDisabled } from '../../utils/is-node-eslint-disabled.js'
import { sortNodesByGroups } from '../../utils/sort-nodes-by-groups.js'
import { reportAllErrors } from '../../utils/report-all-errors.js'
import { shouldPartition } from '../../utils/should-partition.js'
import { computeGroup } from '../../utils/compute-group.js'
import { rangeToDiff } from '../../utils/range-to-diff.js'
import { getSettings } from '../../utils/get-settings.js'
import { isSortable } from '../../utils/is-sortable.js'
import { complete } from '../../utils/complete.js'
import { getEnumMembers } from '../../utils/get-enum-members.js'
import {
  DEPENDENCY_ORDER_ERROR_ID,
  EXTRA_SPACING_ERROR_ID,
  GROUP_ORDER_ERROR_ID,
  MISSED_SPACING_ERROR_ID,
  ORDER_ERROR_ID,
} from './types.js'
import { computeDependenciesBySortingNode } from './compute-dependencies-by-sorting-node.js'
import { buildComparatorByOptionsComputer } from './build-comparator-by-options-computer.js'
import { computeExpressionNumberValue } from './compute-expression-number-value.js'
import { computeNodeName } from './compute-node-name.js'
import { computeMatchedContextOptions } from './compute-matched-context-options.js'
import { computeDependencies } from './compute-dependencies.js'
import { AST_NODE_TYPES } from '@typescript-eslint/utils'
var defaultOptions = {
  useExperimentalDependencyDetection: true,
  fallbackSort: { type: 'unsorted' },
  newlinesInside: 'newlinesBetween',
  sortByValue: 'ifNumericEnum',
  partitionByComment: false,
  partitionByNewLine: false,
  specialCharacters: 'keep',
  newlinesBetween: 'ignore',
  useConfigurationIf: {},
  type: 'alphabetical',
  ignoreCase: true,
  locales: 'en-US',
  customGroups: [],
  alphabet: '',
  order: 'asc',
  groups: [],
}
function sortEnum({ matchedAstSelectors, context, node }) {
  let members = getEnumMembers(node)
  if (
    !isSortable(members) ||
    !members.every(({ initializer }) => initializer)
  ) {
    return
  }
  let settings = getSettings(context.settings)
  let options = complete(
    computeMatchedContextOptions({
      enumMembers: members,
      matchedAstSelectors,
      context,
    }),
    settings,
    defaultOptions,
  )
  validateCustomSortConfiguration(options)
  validateGroupsConfiguration({
    selectors: [],
    modifiers: [],
    options,
  })
  validateNewlinesAndPartitionConfiguration(options)
  let { sourceCode, id } = context
  let eslintDisabledLines = getEslintDisabledLines({
    ruleName: id,
    sourceCode,
  })
  let optionsByGroupIndexComputer = buildOptionsByGroupIndexComputer(options)
  let sortingNodeGroups = members.reduce(
    (accumulator, member) => {
      let name = computeNodeName({
        node: member,
        sourceCode,
      })
      let group = computeGroup({
        customGroupMatcher: customGroup =>
          doesCustomGroupMatch({
            elementValue: sourceCode.getText(member.initializer),
            elementName: name,
            selectors: [],
            modifiers: [],
            customGroup,
          }),
        predefinedGroups: [],
        options,
      })
      let lastSortingNode = accumulator.at(-1)?.at(-1)
      let sortingNode = {
        dependencies:
          options.useExperimentalDependencyDetection ?
            []
          : computeDependencies(member.initializer, node.id.name),
        value:
          member.initializer?.type === AST_NODE_TYPES.Literal ?
            (member.initializer.value?.toString() ?? null)
          : null,
        isEslintDisabled: isNodeEslintDisabled(member, eslintDisabledLines),
        numericValue: computeExpressionNumberValue(member.initializer),
        size: rangeToDiff(member, sourceCode),
        dependencyNames: [name],
        node: member,
        group,
        name,
      }
      if (
        shouldPartition({
          lastSortingNode,
          sortingNode,
          sourceCode,
          options,
        })
      ) {
        accumulator.push([])
      }
      accumulator.at(-1).push({
        ...sortingNode,
        partitionId: accumulator.length,
      })
      return accumulator
    },
    [[]],
  )
  if (options.useExperimentalDependencyDetection) {
    sortingNodeGroups = populateSortingNodeGroupsWithDependencies({
      dependenciesBySortingNode: computeDependenciesBySortingNode({
        sortingNodes: sortingNodeGroups.flat(),
        enumName: node.id.name,
        sourceCode,
      }),
      sortingNodeGroups,
    })
  }
  let sortingNodes = sortingNodeGroups.flat()
  let isNumericEnum = sortingNodes.every(
    sortingNode => sortingNode.numericValue !== null,
  )
  reportAllErrors({
    availableMessageIds: {
      missedSpacingBetweenMembers: MISSED_SPACING_ERROR_ID,
      unexpectedDependencyOrder: DEPENDENCY_ORDER_ERROR_ID,
      extraSpacingBetweenMembers: EXTRA_SPACING_ERROR_ID,
      unexpectedGroupOrder: GROUP_ORDER_ERROR_ID,
      unexpectedOrder: ORDER_ERROR_ID,
    },
    sortNodesExcludingEslintDisabled,
    nodes: sortingNodes,
    options,
    context,
  })
  function sortNodesExcludingEslintDisabled(ignoreEslintDisabledNodes) {
    return sortNodesByDependencies(
      sortingNodeGroups.flatMap(sortingNodeGroup =>
        sortNodesByGroups({
          comparatorByOptionsComputer:
            buildComparatorByOptionsComputer(isNumericEnum),
          optionsByGroupIndexComputer,
          ignoreEslintDisabledNodes,
          nodes: sortingNodeGroup,
          groups: options.groups,
        }),
      ),
      { ignoreEslintDisabledNodes },
    )
  }
}
export { defaultOptions, sortEnum }
