Before do
  ActionMailer::Base.deliveries.clear
end

Then /^(an|no) e?mail should have been sent((?: |and|with|from "[^"]+"|to "[^"]+"|the subject "[^"]+"|the body "[^"]+"|the attachments "[^"]+")+)$/ do |mode, query|
  conditions = {}
  conditions[:to] = $1 if query =~ /to "([^"]+)"/
  conditions[:cc] = $1 if query =~ / cc "([^"]+)"/
  conditions[:bcc] = $1 if query =~ /bcc "([^"]+)"/
  conditions[:from] = $1 if query =~ /from "([^"]+)"/
  conditions[:subject] = $1 if query =~ /the subject "([^"]+)"/
  conditions[:body] = $1 if query =~ /the body "([^"]+)"/
  conditions[:attachments] = $1 if query =~ /the attachments "([^"]+)"/
  @mail = TestMails.find(conditions)
  expectation = mode == 'no' ? 'should_not' : 'should'
  @mail.send(expectation, be_present)
end

When /^I follow the (first|second|third)? ?link in the e?mail$/ do |index_in_words|
  mail = @mail || ActionMailer::Base.deliveries.last
  index = { nil => 0, 'first' => 0, 'second' => 1, 'third' => 2 }[index_in_words]
  visit mail.body.scan(Patterns::URL)[index][2]
end

Then /^no e?mail should have been sent$/ do
  ActionMailer::Base.deliveries.should be_empty
end

Then /^I should see "([^\"]*)" in the e?mail$/ do |text|
  ActionMailer::Base.deliveries.last.body.should include(text)
end

Then /^show me the e?mails$/ do
  ActionMailer::Base.deliveries.each do |mail|
    p [mail.from, mail.to, mail.subject]
  end
end

Then /^(an|no) e?mail should have been sent with:$/ do |mode, raw_data|
  raw_data.strip!
  conditions = {}.tap do |hash|
    raw_data.split("\n").each do |row|
      if row.match(/^[a-z]+: /i)
        key, value = row.split(": ", 2)
        hash[key.downcase.to_sym] = value
      end
    end
  end
  @mail = TestMails.find(conditions)
  expectation = mode == 'no' ? 'should_not' : 'should'
  @mail.send(expectation, be_present)
end

Then /^that e?mail should have the following lines in the body:$/ do |body|
  body.each do |line|
    @mail.body.should include(line.strip)
  end
end

Then /^that e?mail should have the following body:$/ do |body|
  @mail.body.should include(body.strip)
end

class TestMails
  class << self

    attr_accessor :user_identity

    def find(conditions)
      ActionMailer::Base.deliveries.detect do |mail|
        [ conditions[:to].nil? || mail.to.include?(resolve_email conditions[:to]),
          conditions[:cc].nil? || mail.cc.andand.include?(resolve_email conditions[:cc]),
          conditions[:bcc].nil? || mail.bcc.andand.include?(resolve_email conditions[:bcc]),
          conditions[:from].nil? || mail.from.include?(resolve_email conditions[:from]),
          conditions[:subject].nil? || mail.subject.include?(conditions[:subject]),
          conditions[:body].nil? || mail.body.include?(conditions[:body]),
          conditions[:attachments].nil? || conditions[:attachments].split(/\s*,\s*/).sort == Array(mail.attachments).collect(&:original_filename).sort
        ].all?
      end.tap do |mail|
        log(mail)
      end
    end

    def resolve_email(identity)
      if identity =~ /^.+\@.+$/
        identity
      else
        User.send("find_by_#{user_identity || 'email'}!", identity).email
      end
    end

    def log(mail)
      if mail.present?
        File.open("log/test_mails.log", "a") do |file|
          file << "From: #{mail.from}\n"
          file << "To: #{mail.to.join(', ')}\n"
          file << "Subject: #{mail.subject}\n\n"
          file << mail.body
          file << "\n-------------------------\n\n"
        end
      end
    end
    
  end
end