# This is the universal page class from which OA::BasePage and
# Workstation::BasePage, and thus ALL PAGE CLASSES inherit.
# It should only contain methods that we want available to all page classes.
# rubocop:disable Metrics/ClassLength

class UniversalPage
  # it's OK if this class is extra long as this is the "page class" BasePage superclass
  include PageObject
  include RSpec::Matchers

  # Public: Scrolls to a Watir element or some area of the page using the watir-scroll gem
  # See https://github.com/p0deje/watir-scroll for more usage examples.
  #
  # where - The element or area of the page to scroll to.
  #
  # Examples
  #
  #   scroll_to(submit_element.element)
  #   scroll_to :bottom
  #
  # Returns nothing
  def scroll_to(where)
    browser.scroll.to(where)
  end

  def clear_cookies
    browser.cookies.clear
  end

  def check_table_for_data?(table, row, column)
    table[row][column].exists?
  rescue
    return false
  end

  def get_data_from_table(table, row, column)
    table[row][column] if check_table_for_data?(table, row, column)
  end

  # Public: Clicks a page object element with JavaScript instead of WebDriver.
  # Useful for 'other element would receive the click' errors and bypassing
  # add_checkers triggered on non-existing pages that were closed by WebDriver
  # clicks.
  #
  # page_object_element - The element to click using JavaScript.
  #
  # Examples
  #
  #   click_with_javascript(logout_link_element)
  #
  # Returns nothing
  def click_with_javascript(page_object_element)
    execute_script("arguments[0].click();", page_object_element)
  end

  # Public: Gets the current URL and parses the value of the specified query
  #
  # query - The field we want to get the value of
  #
  # Example
  #
  #   http://oa.qaorch.com/cont_mgmt/view_artist.php?artist_id=693221
  #   query_value_from_current_url('artist_id')
  #   => "693221"
  #
  # Returns value string
  def query_value_from_current_url(query)
    Addressable::URI.parse(current_url).query_values[query]
  end

  # Click on an element, offset from center by x pixels to the right, and y pixels to the top
  def click_with_offset(element, x, y)
    driver = browser.driver # Using Selenium::WebDriver::Driver
    driver.action.move_to(element.wd, x, y).perform
    driver.action.click.perform
  end

  def inject_mockjax_library
    execute_script <<-JS
      var elem = document.createElement("script");
      elem.setAttribute("src","//cdnjs.cloudflare.com/ajax/libs/jquery-mockjax/1.5.3/jquery.mockjax.min.js");
      document.body.appendChild(elem);
    JS
    Watir::Wait.until { browser.scripts.any? { |s| s.html.include? "mockjax" } }
  end

  def inject_jquery
    execute_script <<-JS
      var elem = document.createElement("script");
      elem.setAttribute("src","//ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js");
      document.body.appendChild(elem);
    JS
    Watir::Wait.until { browser.scripts.any? { |s| s.html.include? "jquery" } }
    puts "jquery installed"
  end

  def expect_to_be_on_page
    expect(current_url).to eql page_url_value # see InjectPageURLs
  end

  # Increased on 3/22/2018 while systems does stuff for their 3/30 deadline
  def wait_for_ajax_scripts_to_finish(default = 300)
    PageObject.javascript_framework = :jquery
    wait_for_ajax(default)
  end

  # Used by some OA and Workstation pages whereupon clicking a link, the anchor tag
  # gains a new attribute data-public-s3-url,
  # e.g. <a href="whatever" data-public-s3-url="https://s3.amazonaws.com/bucket/file.ext">
  def grab_s3_public_url(link_element)
    link_element.click
    wait_until do
      link_element.attribute("data-public-s3-url").is_a?(String) &&
        # Sometimes this attrib is an empty string momentarily
        link_element.attribute("data-public-s3-url").strip != ""
    end
    link_element.attribute "data-public-s3-url"
  end

  def currency_to_bigdecimal(currency)
    currency.gsub(/[^\d-]/, "").to_d / 100
  end

  # Loaders will sometimes appear and disappear too quickly to be
  # found by the grid. Instead, just sleep for a small time and wait for
  # the loader to disappear if it exists.
  def loader_wait(element:, initial_wait: 2, timeout: 60)
    sleep initial_wait
    element.when_not_present(timeout) if element.element.present?
  end

  # Ows-search has a throttle of 400ms for searches.
  # If typing occurs too quickly we won't get an accurate search.
  def throttled_input(locator, search_string, timeout = 0.5, enable_verification = true)
    locator.send_keys(search_string[0..-2])
    sleep(timeout)
    send_backspace = proc do |exception, try|
      Kernel.puts "#{exception.class}: '#{exception.message}' - Attempt: #{try}"
      locator.send_keys :backspace
    end
    Retriable.retriable(
      on: [RSpec::Expectations::ExpectationNotMetError, Watir::Wait::TimeoutError],
      tries: 5, on_retry: send_backspace
    ) do
      sleep(timeout)
      wait_until { locator.value == search_string[0..-2] }
      locator.send_keys(search_string[-1])
      expect_in_first_result(locator, search_string) if enable_verification
    end
  end

  # If RB encoder is overloaded we will see common failures with uploads in tests
  # This is useful for debug purposes.
  def check_rb_encoder_load(message = "for the current step")
    client = Support::DbClient.dd_client
    query = 'SELECT * FROM asset_received_queue WHERE picked_up = "N"'
    queue_load = client.query(query).count
    puts "RB encoder has #{queue_load} jobs in its queue #{message}"
    queue_load + 1
  end

  # Overrides refresh from PageObject, visits current url with session token if token is passed in
  def refresh(session_token = nil)
    if session_token.nil? || current_url.include?("session=#{session_token}")
      super()
    else
      refresh_url = current_url + "&session=#{session_token}" if current_url.include?("?")
      refresh_url ||= current_url + "?session=#{session_token}"
      platform.navigate_to(refresh_url)
    end
  end

  private

  # Watir has a bug where it waits for an element to be visible before sending #send_keys to it.
  # This doesn't work for file_field elements, which tend to be hidden: https://github.com/watir/watir/issues/497
  # This would be better as a monkeypatch, but I couldn't figure out how to do it quickly.
  def upload_workaround(element:, path:)
    element.element.wd.send_keys path
  end

  def click_edit_with_retry(button_to_click)
    Retriable.retriable(on: Selenium::WebDriver::Error::UnknownError, tries: 5) do
      button_to_click
    end
  end

  # If the search input field has the id "inputfetchReleases" then look for a search result link,
  # else look for the search result div.
  # Then expect the search result to include the search string, retrying if it hasn't loaded yet.
  def expect_in_first_result(locator, search_string)
    first_option = if locator.id == "inputfetchReleases"
                     wait_until { link_elements(class: "filter_by").any? { |x| x.present? } }
                     link_elements(class: "filter_by").find { |x| x.present? }
                   else
                     div_element(class: "Select-option")
                   end
    Retriable.retriable(on: RSpec::Expectations::ExpectationNotMetError, tries: 3, base_interval: 1) do
      expect(first_option.when_present(30).text).to include search_string
    end
  end
end
# rubocop:enable Metrics/ClassLength
