include Sorcery::TestHelpers::Rails

def locate(id)
  id = id.gsub(/\s+/, '_')
  e = page.all("[name=\"#{id}\"], [id=\"#{id}\"], [id^=\"#{id}--\"]")
  return e.first[:id]
end

def selector_for(id)
  prefixes = ['body.has-overlay .dlg.open ', 'body #primary-content ']
  selectors = ['[name="{id}"]', '[id="{id}"]', '[id^="{id}--"]']

  selector = []
  prefixes.each do | pre |
    selectors.each do | sel |
      selector << "#{pre}#{sel}".gsub(/\{id\}/, id)
    end
  end

  selector.join(',')
end

def get_language_code(language)
  languages = {
    'english' => 'en',
    'french' => 'fr',
    'spanish' => 'es',
    'german' => 'de'
  }
  languages[language.downcase]
end

def to_filename(filename)
  filename.gsub(/[^\w\s_-]+/, '')
    .gsub(/(^|\b\s)\s+($|\s?\b)/, '\\1\\2')
    .gsub(/\s+/, '_')
end

def log_result(scenario)
  if Capybara.current_session.server.present?
    dir = "log/test-results/#{to_filename(scenario.feature.name)}"
    FileUtils::mkdir_p dir
    filename = "#{to_filename(scenario.name)}.html"
    File.write("#{dir}/#{filename}", capture_html)
  end
end

def capture_html(distance_from_root = 3)
  html = page.html
  host = "http://#{Capybara.current_session.server.host}:#{Capybara.current_session.server.port}"
  public_dir = "#{'../' * distance_from_root}public/"
  
  html.gsub(/(=\"|\(['"]?)(?:#{host})?\/(assets|uploads)/, "\\1#{public_dir}\\2")
end

def attempt_to(refresh_on_fail = false, &block)
  exception = nil
  begin
    retries ||= 0
    timeout ||= 0
    timeout += 1
    yield
  rescue Exception => e
    exception ||= e
    raise exception unless (retries += 1) <= 4
    visit TestState.last_page if TestState.last_page && refresh_on_fail
    sleep(timeout * timeout)
    retry
  end
end

def last_email_html
  if TestState.last_email.parts
    TestState.last_email.parts.last.body.raw_source
  else
    TestState.last_email.body.raw_source
  end
end

def find_in_last_email(selector, attribute = nil)
  html = last_email_html.gsub(/^.*?(<table .*<\/table>).*$/m, '\1').gsub(/\n/, ' ').gsub(/\'/, '\\\'')
  Nokogiri::HTML(html).at(selector).attr(attribute)
end

def emails_to(email_address, subject = nil)
  ActionMailer::Base.deliveries.select do |mail|
    mail.to.include?(email_address) &&
      (subject.nil? || ActionView::Base.full_sanitizer.sanitize(mail.subject).downcase.include?(subject.downcase))
  end
end

def str_to_num(num)
  nums = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten']
  return nums.find_index(num) if nums.include?(num)

  case num
  when 'no', 'none'
    return 0
  when 'a', 'an'
    return 1
  when 'two', 'couple', 'a couple', 'couple of', 'a couple of'
    return 2
  end

  return num.to_i if num =~ /\d+/

  fail "Could not interpret '#{num}' as a number"
end

def compare(expected, actual, negate = false)
  case expected
  when /any|some/
    expectation = (be >= 1)
  else
    expectation = (be == str_to_num(expected))
  end
  expect(actual).send(negate ? :not_to : :to, expectation)
end

def email_address(user)
  case user
  when /(I|me)/
    TestState.my_account.present? ? TestState.my_account.email : TestState.last_email_entered
  when /^(?:the )?site administrator$/i
    'goodgodwin@hotmail.com'
  when /^'(.+)'$/
    get_user($1).email
  else
    user
  end
end

def get_locale(language)
  return ({
    spanish: :es,
    english: :en,
    french:  :fr,
    german:  :de,
    klingon: :tlh
  }[language.strip.downcase.to_sym] or fail "Unable to convert '#{language.trim}' into a locale")
end

def get_field(field)
  field = field.gsub(/^\s*(my|the)\s*(.+)$/, '\2').gsub(/\s/, '_')
  aliases = {'phone_number' => 'phone', 'email_address' => 'email'}
  return aliases[field] || field
end

def get_user(username)
  return create_user unless username.present?

  TestState::Users[username] ||= create_user({
      (username =~ /^[^\s]+@[^\s]+\.[^\s]+$/ ? :email : :firstname) => username
    })

  return TestState::Users[username]
end

def element_with_text(text, parent = page)
  smallest = nil
  parent.all('*', text: text).each do |node|
    smallest = node if smallest.nil? || smallest.native['outerHTML'].length > node.native['outerHTML'].length
  end
  return smallest
end

def parent_element(node)
  node.first(:xpath, './/..')
end

def headers_to_attributes(object)
  new_object = {}
  object.each do |key, value|
    new_object[key.underscore] = value
  end

  return new_object
end

def string_to_time(str)
  h, m = str.split(':')
  h = h.to_i
  m = m.to_i
  h += 12 if h < 6
  return h + (m > 0 ? 0.5 : 0)
end

def string_to_time_length(str)
  parts = str.match(/^(?:(\d+) hour)?\s*(?:(\d+) minutes)?$/)
  return (parts[1] || '0').to_i + ((parts[2] || '0').to_i > 0 ? 0.5 : 0)
end

def str_to_wday(str)
  return [:sun, :mon, :tue, :wed, :thu, :fri, :sat].index(str[0...3].downcase.to_sym)
end
