require 'capybara/rspec'
require 'capybara-screenshot/rspec'
require './core/formatters/screenshot_failures_formatter'
require 'selenium-webdriver'
require 'rspec/retry'
require './core/formatters/stats_formatter'
require './core/formatters/failures_formatter'
require './core/configs/env_config'
require './core/formatters/parallel_output_formatter'
require './core/configs/grid_config'
require './core/formatters/bmp_formatter'
require './core/formatters/jslog_formatter'
require './core/utils/bmp_utils'
require './core/base/page_helper'
require './core/base/capybara_extensions'
require './core/base/capybara_patch'
require './core/base/rspec_extensions'
require './core/configs/driver_config'
require './core/utils/user_utils'
require './core/utils/logger_utils'
require './core/utils/js_utils.rb'
require './core/extensions/rspec_extensions'
require 'rspec_junit_formatter'
require './core/formatters/user_init_duration_formatter'
require 'smoca_common/configs/s3_config'
require 'smoca_common/utils/screenshot_utils'
require 'smoca_common/utils/twitch_utils'
require './core/utils/js_logger_utils'

include EnvConfig
include StatUtils
include BmpUtils
include GridConfig
include BMPFormatter
include Tracking
include UserUtils # Allow registering users in all specs
include LoggerUtils
include JSUtils
include JSLoggerUtils

# If ENV['PROPERTIES'] is specified properties_config.rb
# will be called to handle the test cofiguration.
# ENV['PROPERTIES'] will only accept a file name.
# IE: 'video_player_config.yaml'
# This must be the first parameter checked via CLI.
# If nil, the environment variable does not exist.
if ENV['PROPERTIES'] != nil
  require './core/utils/properties_config_utils'
  PropertiesConfigUtils.configure_environment
end

initialize_environment # Method determines where Capybara should run.
# This will make it clear in the log as to where the test was running
# Designed to only print once in Parallel by checking if the TEST_ENV_NUMBER is #1
puts "Tests are running against: #{Capybara.app_host}" if ENV['TEST_ENV_NUMBER'] == '' || ENV['PARALLEL'] != 'true'

# Browser Drivers like :edge report elements as hidden unless it's in the viewport
Capybara.ignore_hidden_elements = false

RSpec.configure do |config|
  Capybara.run_server = false
  Capybara.default_driver = :selenium
  Capybara.javascript_driver = :selenium
  Capybara.server_port = 9887 + ENV['TEST_ENV_NUMBER'].to_i

  # Selenium Grid Configuration.
  # Point to Selenium Grid if needed
  if grid_enabled?
    point_to_grid
  else
    DriverConfig.register_local_driver
  end

  # Logging
  if ENV['SELENIUM_LOGGING'] == 'true'
    log_level = ENV['SELENIUM_LOG_LEVEL'] || 'info'
    DriverConfig.enable_logging(log_level.to_sym)
  end

  if ENV['SMOCA_LOG_LEVEL']
    LoggerUtils::SmocaLogger.level = ENV['SMOCA_LOG_LEVEL'].to_sym
  else
    LoggerUtils::SmocaLogger.level = :error
  end

  ## RSpec-Retry
  config.verbose_retry = true # show retry status in spec process
  config.display_try_failure_messages = true # displays the failure message upon retry
  config.default_retry_count = ENV['RETRY_COUNT'] # The default amount rspec will retry
  config.exceptions_to_hard_fail = [HeimdallExceptions::UserNotAvailable]

  config.tty = true # Output the color to Jenkins
  # Capybara Failure Screenshot Config
  Capybara.save_path = "#{ScreenshotUtils.failure_location_path}/screenshots" # Output failure screenshots to the following path
  Capybara::Screenshot.prune_strategy = :keep_last_run # Auto delete upon new run

  # Adds a selector to Capybara for iframes.
  # Will be able to do expect(page).to have_selector(:iframe, 'name')
  Capybara.add_selector(:iframe) do
    css { |locator| "iframe[name=#{locator}]" }
  end

  # Adds a selector to Capybara for name attribute of tags
  # Will be able to find element using selector as name
  Capybara.add_selector(:name) do
    xpath { |name| XPath.descendant[XPath.attr(:name).contains(name)] }
  end

  config.before(:each) do |example|
    if unsupported_browser?(example)
      skip('Browser is not supported for this scenario.')
    end
  end

  config.after(:each) do
    PageHelper.reset_browser_window_size
  end

  config.before(:suite) do
    $login_rate = StatsCounter.new # Initializes to track the login success/failures throughout the suite
    # Set Browser Window Sizes
    # Grid does this within its point_to_grid method
    # This is to cover Headless and Local environments
    PageHelper.set_browser_window_to_default_size unless browser_formatter(SpecData.browser.to_s).eql?('MicrosoftEdge')
  end

  # Configuration around each example ran within the suite
  config.around(:each) do |example|
    example.run

    # Destroy Users after each run
    if example.metadata.has_key?(:users)
      example.metadata[:users].each { |user_type| user_type.destroy if user_type.user_initialized? && !user_type.persist? }
    end

    if example.exception
      track_exception(example.exception) unless ENV['TRACKING'] != 'true'

      # Reconnect to Grid if needed
      if grid_connection_terminated?(example.exception) && grid_enabled?
        logger.warn "Grid connection was terminated due to [#{example.exception.class} #{example.exception.message}]."\
          "\n\nReconnecting..."
        connection = reconnect_to_grid
        example.run_with_retry if connection
      end
    end
  end

  config.after(:suite) do
    measure_login_ratio($login_rate) # Once complete, analyze the login success/failure numbers
    if DriverConfig.log_filepath
      log_url = S3Config.upload_file(DriverConfig.log_filepath)
      puts "Uploaded Logs for Session \##{Capybara.page.driver.browser.session_id}. #{log_url}"
    end
  end

  # Exclude TIER2 examples when passing TIER2 = false
  config.filter_run_excluding :tier2 => true if ENV['TIER2'] == 'false'

  # Exclude BETA scenarios unless passing BETA = true
  config.filter_run_excluding :BETA => true unless ENV['BETA'] == 'true'

  # Exclude TIER1_BUFFER_EVENTS unless passing TIER1_BUFFER_EVENTS = true. See SMOC-318.
  config.filter_run_excluding :tier1_buffer_events => true unless ENV['TIER1_BUFFER_EVENTS'] == 'true'

  # Excluded from all runs unless PLAYER_TYPE=mediaplayer to an issue
  # where flash backend incorrectly fires off pause event when autoplay=false
  config.filter_run_excluding :mediaplayer => true unless VideoPlayerData.player_type == 'mediaplayer'

  # Order Matters! Place this before ParallelOutput formatter. That way HAR data can be added to metadata
  # before ParallelOutput builds the failure string
  config.add_formatter(BMPFormatter::Formatter) if bmp_enabled?

  # Place before Gnosis Reporting so that screenshot data is added to metadata before sending to Gnosis
  # Place before PARALLEL so that dump failure already has the screenshot data
  if ScreenshotUtils.screenshots_enabled?
    config.add_formatter(ScreenshotFailuresFormatter)
  end

  # Load Formatters based on if the tests are running in Parallel or not
  # output test result as rspec.xml if test is not run in parallel, rspec*.xml if it is
  if ENV['PARALLEL'] == 'true'
    config.add_formatter(RSpec::Core::Formatters::ParallelOutput) # Load a specialized output for Parallel
    config.add_formatter(RspecJunitFormatter, "resources/output/rspec#{TwitchUtils.thread_number}.xml")
  else
    config.add_formatter(RSpec::Core::Formatters::DocumentationFormatter)
    config.add_formatter(RspecJunitFormatter, 'resources/output/rspec.xml')
    config.profile_examples = true
  end

  if ENV['TRACKING'] == 'true'
    config.add_formatter(Stats::Formatter)
  end

  if JSLoggerUtils::JSErrorLogger.supported?
    config.add_formatter(JSLogFormatter)
  end

  config.add_formatter(UserInitDurationFormatter)
end

# @param example [RSpec::Core::Example] The example to check its unsupported browser metadata
# @return [Boolean] Whether the example is not supported on the current browser
def unsupported_browser?(example)
  current_browser = page.driver.browser.browser # Returns as a symbol

  if example.metadata.has_key?(:unsupported_browsers)
    example.metadata[:unsupported_browsers].each do |browser|
      return true if browser_formatter(browser.to_s).to_sym == current_browser
    end
  end

  return false # If it gets here, it didn't find an unsupported browser
end
