# frozen_string_literal: true

module Net
  class IMAP

    # An array of sequence numbers returned by Net::IMAP#search, or unique
    # identifiers returned by Net::IMAP#uid_search.
    #
    # For backward compatibility, SearchResult inherits from Array.
    class SearchResult < Array

      # Returns a frozen SearchResult populated with the given +seq_nums+.
      #
      #     Net::IMAP::SearchResult[1, 3, 5, modseq: 9]
      #     # => Net::IMAP::SearchResult[1, 3, 5, modseq: 9]
      def self.[](*seq_nums, modseq: nil)
        new(seq_nums, modseq: modseq)
      end

      # A modification sequence number, as described by the +CONDSTORE+
      # extension in {[RFC7162
      # §3.1.6]}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.1.6].
      attr_reader :modseq

      # Returns a frozen SearchResult populated with the given +seq_nums+.
      #
      #     Net::IMAP::SearchResult.new([1, 3, 5], modseq: 9)
      #     # => Net::IMAP::SearchResult[1, 3, 5, modseq: 9]
      def initialize(seq_nums, modseq: nil)
        super(seq_nums.to_ary.map { Integer _1 })
        @modseq = Integer modseq if modseq
        freeze
      end

      # Returns a frozen copy of +other+.
      def initialize_copy(other); super; freeze end

      # Returns whether +other+ is a SearchResult with the same values and the
      # same #modseq.  The order of numbers is irrelevant.
      #
      #     Net::IMAP::SearchResult[123, 456, modseq: 789] ==
      #       Net::IMAP::SearchResult[123, 456, modseq: 789]
      #     # => true
      #     Net::IMAP::SearchResult[123, 456, modseq: 789] ==
      #       Net::IMAP::SearchResult[456, 123, modseq: 789]
      #     # => true
      #
      #     Net::IMAP::SearchResult[123, 456, modseq: 789] ==
      #       Net::IMAP::SearchResult[987, 654, modseq: 789]
      #     # => false
      #     Net::IMAP::SearchResult[123, 456, modseq: 789] ==
      #       Net::IMAP::SearchResult[1, 2, 3, modseq: 9999]
      #     # => false
      #
      # SearchResult can be compared directly with Array, if #modseq is nil and
      # the array is sorted.
      #
      #     Net::IMAP::SearchResult[9, 8, 6, 4, 1] == [1, 4, 6, 8, 9] # => true
      #     Net::IMAP::SearchResult[3, 5, 7, modseq: 99] == [3, 5, 7] # => false
      #
      # Note that Array#== does require matching order and ignores #modseq.
      #
      #     [9, 8, 6, 4, 1] == Net::IMAP::SearchResult[1, 4, 6, 8, 9] # => false
      #     [3, 5, 7] == Net::IMAP::SearchResult[3, 5, 7, modseq: 99] # => true
      #
      def ==(other)
        (modseq ?
         other.is_a?(self.class) && modseq == other.modseq :
         other.is_a?(Array)) &&
          size == other.size &&
          sort == other.sort
      end

      # Hash equality.  Unlike #==, order will be taken into account.
      def hash
        return super if modseq.nil?
        [super, self.class, modseq].hash
      end

      # Hash equality.  Unlike #==, order will be taken into account.
      def eql?(other)
        return super if modseq.nil?
        self.class == other.class && hash == other.hash
      end

      # Returns a string that represents the SearchResult.
      #
      #    Net::IMAP::SearchResult[123, 456, 789].inspect
      #    # => "[123, 456, 789]"
      #
      #    Net::IMAP::SearchResult[543, 210, 678, modseq: 2048].inspect
      #    # => "Net::IMAP::SearchResult[543, 210, 678, modseq: 2048]"
      #
      def inspect
        return super if modseq.nil?
        "%s[%s, modseq: %p]" % [self.class, join(", "), modseq]
      end

      # Returns a string that follows the formal \IMAP syntax.
      #
      #    data = Net::IMAP::SearchResult[2, 8, 32, 128, 256, 512]
      #    data.to_s           # => "* SEARCH 2 8 32 128 256 512"
      #    data.to_s("SEARCH") # => "* SEARCH 2 8 32 128 256 512"
      #    data.to_s("SORT")   # => "* SORT 2 8 32 128 256 512"
      #    data.to_s(nil)      # => "2 8 32 128 256 512"
      #
      #    data = Net::IMAP::SearchResult[1, 3, 16, 1024, modseq: 2048].to_s
      #    data.to_s           # => "* SEARCH 1 3 16 1024 (MODSEQ 2048)"
      #    data.to_s("SORT")   # => "* SORT 1 3 16 1024 (MODSEQ 2048)"
      #    data.to_s           # => "1 3 16 1024 (MODSEQ 2048)"
      #
      def to_s(type = "SEARCH")
        str = +""
        str << "* %s " % [type.to_str] unless type.nil?
        str << join(" ")
        str << " (MODSEQ %d)" % [modseq] if modseq
        -str
      end

      # Converts the SearchResult into a SequenceSet.
      #
      #     Net::IMAP::SearchResult[9, 1, 2, 4, 10, 12, 3, modseq: 123_456]
      #       .to_sequence_set
      #     # => Net::IMAP::SequenceSet["1:4,9:10,12"]
      def to_sequence_set; SequenceSet[*self] end

      def pretty_print(pp)
        return super if modseq.nil?
        pp.text self.class.name + "["
        pp.group_sub do
          pp.nest(2) do
            pp.breakable ""
            each do |num|
              pp.pp num
              pp.text ","
              pp.fill_breakable
            end
            pp.breakable ""
            pp.text "modseq: "
            pp.pp modseq
          end
          pp.breakable ""
          pp.text "]"
        end
      end

    end

  end
end
