require './core/base/spec_helper'
require './core/data/spec_data'
require 'digest'

module GridConfig
  # Connect to driver
  def register_appium_driver
    url = ""
    if grid_enabled? and saucelabs?
      url = "http://#{SpecData.saucelabs_user}:#{SpecData.saucelabs_key}@ondemand.saucelabs.com:80/wd/hub"
    else
      url = 'http://localhost:4723/wd/hub'
    end

    connection = Capybara.register_driver :appium do |app|
      appium_lib_options = {
        server_url: url
      }
      all_options = {
        appium_lib: appium_lib_options,
        caps: set_capabilities(ENV['BROWSER_NAME'], ENV['PLATFORM_NAME'], ENV['PLATFORM_VERSION'], ENV['DEVICE_NAME'])
      }
      Appium::Capybara::Driver.new(app, all_options)
    end
  end

  # @param browser_name [String] Browser to run the test
  # @param platform_name [String] Mobile Platform to run the test on
  # @param platform_version [String] Version of the Mobile Platform
  # @param device_name [String] Name of the device emulator
  # @return [Hash] Capabilities of the driver
  def set_capabilities(browser_name, platform_name, platform_version, device_name)
    browser_name ||= 'chrome'
    platform_name ||= 'Android'
    # caps = Selenium ::WebDriver::Remote::Capabilities.android
    caps = Hash.new
    if saucelabs? and grid_enabled?
      caps['platformName'] = platform_name
      caps['platformVersion'] = platform_version
      caps['browserName'] = browser_name
      caps['deviceName'] = device_name
      caps['recordScreenshots'] = true
      caps['recordVideo'] = true
      caps['deviceOrientation'] = 'portrait'
    else
      caps['platformName'] = platform_name
      caps['platformVersion'] = platform_version
      caps['browserName'] = browser_name
      caps['deviceName'] = device_name
      caps['avd'] = DriverConfig.capabilities[:caps][:AVD]
    end
    caps['rotatable'] = true
    return caps
  end

  # @return [boolean] If test to be run on saucelabs
  def saucelabs?
    return false if ENV['GRID_HOST'].nil?
    DriverConfig.capabilities[:caps][:GRID_HOST].downcase == 'saucelabs' || ENV['GRID_HOST'].downcase == 'saucelabs'
  end

  # @return [boolean] If the test to be run on a grid
  def grid_enabled?
    return false if ENV['GRID'].nil?
    DriverConfig.capabilities[:caps][:GRID].downcase == 'true' || ENV['GRID'].downcase == 'true'
  end

  # @return [boolean] Connects to new instance and returns result
  def reconnect_to_grid
    Capybara.current_session.driver.quit
    connection = register_appium_driver
    connection_established?(connection)
    connection
  end

  # @param exception [ExceptionClass] Exception occurring during test run
  # @return [boolean] If the grid connection has been terminated from one of the reasons in #grid_connection_exceptions
  def grid_connection_terminated?(exception)
    return true if grid_connection_exceptions.include?(exception.class.to_s)
  end

  # @return [Hash] List of exceptions
  def grid_connection_exceptions
    return [
      'TypeError', # Occurs when webdriver connectivity with browser is loose
      'ETIMEDOUT', # Grid Host is completely unavailable
      'ECONNREFUSED', # Host is online, but Grid is not running
      'EOFError', # End-of-file error
      'Selenium::WebDriver::Error::UnknownError', # Unknown, various conditions
      'RSpec::Core::MultipleExceptionError', # Multiple Exceptions in execution
      'Selenium::WebDriver::Error::WebDriverError' # WebDriver Error
    ]
  end

  # @param connection [WebDriver] WebDriver connection
  # @return [boolean] If the connection has been established
  def connection_established?(connection)
    start_time = Time.now
    begin
      Capybara.current_session.driver.browser
    rescue *grid_connection_exceptions => e
      sleep(5)
      if retry_connection?(start_time, 120)
        retry
      else
        raise "[ERROR] Connection failed to grid, Error: #{e}"
      end
    end
    return true
  end

  # @param start_time [Time] Start Time of when retrying to establish connection
  # @param timeout [Fixnum] Timeout per retry
  # @return [boolean] Whether to retry connection
  def retry_connection?(start_time, timeout)
    time_since_initiated = (Time.now - start_time)
    if time_since_initiated < timeout
      puts "[INFO] Trying to connect to Grid again (will timeout in roughly #{(timeout - time_since_initiated).round} seconds)"
      return true
    else
      puts "[ERROR] Unable to connect to Grid after #{time_since_initiated.round} seconds."
      return false
    end
  end

  def build_sauce_url(sauce_user_key, session_id)
    auth_token = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('md5'), sauce_user_key, session_id)
    url = "https://saucelabs.com/jobs/#{session_id}?auth=#{auth_token}"
  end
end
