class Shoulda::Matchers::ActiveRecord::HaveDbIndexMatcher

@private

def correct_unique?

def correct_unique?
  if qualifiers.include?(:unique)
    if qualifiers[:unique] && !matched_index.unique
      @reason = 'it is not unique'
      false
    elsif !qualifiers[:unique] && matched_index.unique
      @reason = 'it is unique'
      false
    else
      true
    end
  else
    true
  end
end

def described_table_name

def described_table_name
  if model
    "the #{table_name} table"
  else
    'a table'
  end
end

def description

def description
  String.new('have ').tap do |description|
    description <<
      if qualifiers.include?(:unique)
        "#{Shoulda::Matchers::Util.a_or_an(index_type)} "
      else
        'an '
      end
    description << 'index on '
    description << inspected_expected_columns
  end
end

def failure_message

def failure_message
  message =
    "Expected #{described_table_name} to #{positive_expectation}"
  message <<
    if index_exists?
      ". The index does exist, but #{reason}."
    elsif reason
      ", but #{reason}."
    else
      ', but it does not.'
    end
  Shoulda::Matchers.word_wrap(message)
end

def failure_message_when_negated

def failure_message_when_negated
  Shoulda::Matchers.word_wrap(
    "Expected #{described_table_name} not to " +
    "#{negative_expectation}, but it does.",
  )
end

def formatted_expected_columns

def formatted_expected_columns
  expected_columns.map do |column|
    if column.match?(/^\w+$/)
      column.to_sym
    else
      column
    end
  end
end

def index_exists?

def index_exists?
  !matched_index.nil?
end

def index_type

def index_type
  if qualifiers[:unique]
    'unique'
  else
    'non-unique'
  end
end

def initialize(columns)

def initialize(columns)
  @expected_columns = normalize_columns_to_array(columns)
  @qualifiers = {}
end

def inspected_expected_columns

def inspected_expected_columns
  if formatted_expected_columns.one?
    formatted_expected_columns.first.inspect
  else
    formatted_expected_columns.inspect
  end
end

def matched_index

def matched_index
  @_matched_index ||=
    if expected_columns.one?
      sorted_indexes.detect do |index|
        Array.wrap(index.columns) == expected_columns
      end
    else
      sorted_indexes.detect do |index|
        index.columns == expected_columns
      end
    end
end

def matches?(subject)

def matches?(subject)
  @subject = subject
  index_exists? && correct_unique?
end

def model

def model
  subject&.class
end

def negative_expectation

def negative_expectation
  description
end

def normalize_columns_to_array(columns)

def normalize_columns_to_array(columns)
  Array.wrap(columns).map(&:to_s)
end

def positive_expectation

def positive_expectation
  if index_exists?
    expectation = "have an index on #{inspected_expected_columns}"
    if qualifiers.include?(:unique)
      expectation << " and for it to be #{index_type}"
    end
    expectation
  else
    description
  end
end

def sorted_indexes

def sorted_indexes
  if qualifiers.include?(:unique)
    # return indexes with unique matching the qualifier first
    unsorted_indexes.sort_by do |index|
      index.unique == qualifiers[:unique] ? 0 : 1
    end
  else
    unsorted_indexes
  end
end

def table_name

def table_name
  model.table_name
end

def unique(unique = true)

def unique(unique = true)
  @qualifiers[:unique] = unique
  self
end

def unsorted_indexes

def unsorted_indexes
  model.connection.indexes(table_name)
end