module Vmail

  module ShowingHeaders

    def get_message_headers(message_ids)
      messages = message_ids.map {|message_id|
        m = Message[message_id]
        if m.nil?
          raise "Message #{ message_id } not found"
        end
        m
      }
      res = messages.map {|m| format_header_for_list(m)}.join("\n")
      res
    end

    def fetch_and_cache_headers(id_set)
      log "Fetching headers for #{ id_set.size } messages"
      results = reconnect_if_necessary do
        @imap.fetch(id_set, ["FLAGS", "ENVELOPE", "RFC822.SIZE", "UID"])
      end
      results.reverse.map do |x|
        envelope = x.attr["ENVELOPE"]
        message_id = envelope.message_id
        begin
          subject = Mail::Encodings.unquote_and_convert_to((envelope.subject || ''), 'UTF-8')
        rescue Encoding::UndefinedConversionError
          log "Encoding::UndefinedConversionError:\n#{[$!.message, $!.backtrace].join("\n")}"
          subject = "[encoding could not be converted to UTF-8]"
        end
        recipients = ((envelope.to || []) + (envelope.cc || [])).map {|a| extract_address(a)}.join(', ')
        sender = extract_address envelope.from.first
        uid = x.attr["UID"]
        message = Message[message_id]
        unless message
          message = Message.new
          message.message_id = message_id
          message.save
        end
        params = {
          subject: (subject || ''),
          flags: x.attr['FLAGS'].join(','),
          date: Time.parse(envelope.date).localtime.to_s,
          size: x.attr['RFC822.SIZE'],
          sender: sender,
          recipients: recipients
        }
        # We really just need to update the flags, buy let's update everything
        message.update params

        unless Vmail::Labeling[message_id: message_id, label_id: @label.label_id]
          params = { message_id: message.message_id, uid: uid, label_id: @label.label_id }

          Labeling.create params
        end
        message_id
      end
    end

    def extract_address(address_struct)
      address = if address_struct.nil?
                  "Unknown"
                else
                  email = [ (address_struct.mailbox ? Mail::Encodings.unquote_and_convert_to(address_struct.mailbox, 'UTF-8') : ""),
                      (address_struct.host ?  Mail::Encodings.unquote_and_convert_to(address_struct.host, 'UTF-8'): "")
                    ].join('@')
                  if address_struct.name
                   "#{ Mail::Encodings.unquote_and_convert_to((address_struct.name || ''), 'UTF-8') } <#{ email }>"
                  else
                    email
                  end
                end

    end

    def format_header_for_list(message)
      date = DateTime.parse(message.date)
      formatted_date = if date.year != Time.now.year
                         date.strftime @date_formatter_prev_years
                       else
                         date.strftime @date_formatter_this_year
                       end
      address = if @mailbox == mailbox_aliases['sent']
                  message.recipients
                else
                  message.sender
                end

      mid_width = [@width - 38, 10].max  # force a minimum width of 10
      address_col_width = (mid_width * 0.3).ceil
      subject_col_width = (mid_width * 0.7).floor
      row_text = [ format_flags(message.flags).col(2),
                   (formatted_date || '').col(@date_width),
                   address.col(address_col_width),
                   message.subject.col(subject_col_width),
                   number_to_human_size(message.size).rcol(7),
                   message.message_id ].join(' | ')
    end

    FLAGMAP = {'Flagged' => '*', 'Seen' => 'Seen'}

    def format_flags(flags)
      flags = flags.split(',').map {|flag| FLAGMAP[flag]}.compact
      if flags.delete('Seen').nil?
        flags << '+' # unread
      end
      flags.join('')
    end

    def with_more_message_line(res)
      remaining = @start_index
      if remaining <= 1
        return res
      end
      res + "\n>  Load #{[100, remaining].min} more messages. #{ remaining } remaining."
    end

  end
end
