# frozen_string_literal: true

class HTTPSender < BaseSender

  def initialize(endpoint, options = {})
    super()
    @endpoint = endpoint
    @options = options
    @log_id = SecureRandom.alphanumeric(8).upcase
  end

  def send_message(message)
    start_time = Time.now
    result = SendResult.new
    result.log_id = @log_id

    request_options = {}
    request_options[:sign] = true
    request_options[:timeout] = @endpoint.timeout || 5
    case @endpoint.encoding
    when "BodyAsJSON"
      request_options[:json] = parameters(message, flat: false).to_json
    when "FormData"
      request_options[:params] = parameters(message, flat: true)
    end

    log "Sending request to #{@endpoint.url}"
    response = Postal::HTTP.post(@endpoint.url, request_options)
    result.secure = !!response[:secure] # rubocop:disable Style/DoubleNegation
    result.details = "Received a #{response[:code]} from #{@endpoint.url}"
    log "  -> Received: #{response[:code]}"
    if response[:body]
      log "  -> Body: #{response[:body][0, 255]}"
      result.output = response[:body].to_s[0, 500].strip
    end
    if response[:code] >= 200 && response[:code] < 300
      # This is considered a success
      result.type = "Sent"
    elsif response[:code] >= 500 && response[:code] < 600
      # This is temporary. They might fix their server so it should soft fail.
      result.type = "SoftFail"
      result.retry = true
    elsif response[:code].negative?
      # Connection/SSL etc... errors
      result.type = "SoftFail"
      result.retry = true
      result.connect_error = true
    elsif response[:code] == 429
      # Rate limit exceeded, treat as a hard fail and don't send bounces
      result.type = "HardFail"
      result.suppress_bounce = true
    else
      # This is permanent. Any other error isn't cool with us.
      result.type = "HardFail"
    end
    result.time = (Time.now - start_time).to_f.round(2)
    result
  end

  private

  def log(text)
    Postal.logger.info text, id: @log_id, component: "http-sender"
  end

  def parameters(message, options = {})
    case @endpoint.format
    when "Hash"
      hash = {
        id: message.id,
        rcpt_to: message.rcpt_to,
        mail_from: message.mail_from,
        token: message.token,
        subject: message.subject,
        message_id: message.message_id,
        timestamp: message.timestamp.to_f,
        size: message.size,
        spam_status: message.spam_status,
        bounce: message.bounce,
        received_with_ssl: message.received_with_ssl,
        to: message.headers["to"]&.last,
        cc: message.headers["cc"]&.last,
        from: message.headers["from"]&.last,
        date: message.headers["date"]&.last,
        in_reply_to: message.headers["in-reply-to"]&.last,
        references: message.headers["references"]&.last,
        html_body: message.html_body,
        attachment_quantity: message.attachments.size,
        auto_submitted: message.headers["auto-submitted"]&.last,
        reply_to: message.headers["reply-to"]
      }

      if @endpoint.strip_replies
        hash[:plain_body], hash[:replies_from_plain_body] = ReplySeparator.separate(message.plain_body)
      else
        hash[:plain_body] = message.plain_body
      end

      if @endpoint.include_attachments?
        if options[:flat]
          message.attachments.each_with_index do |a, i|
            hash["attachments[#{i}][filename]"] = a.filename
            hash["attachments[#{i}][content_type]"] = a.content_type
            hash["attachments[#{i}][size]"] = a.body.to_s.bytesize.to_s
            hash["attachments[#{i}][data]"] = Base64.encode64(a.body.to_s)
          end
        else
          hash[:attachments] = message.attachments.map do |a|
            {
              filename: a.filename,
              content_type: a.mime_type,
              size: a.body.to_s.bytesize,
              data: Base64.encode64(a.body.to_s)
            }
          end
        end
      end

      hash
    when "RawMessage"
      {
        id: message.id,
        rcpt_to: message.rcpt_to,
        mail_from: message.mail_from,
        message: Base64.encode64(message.raw_message),
        base64: true,
        size: message.size.to_i
      }
    else
      {}
    end
  end

end
