# frozen_string_literal: true

module Capybara
  module Poltergeist
    class Error < StandardError; end
    class NoSuchWindowError < Error; end

    class ClientError < Error
      attr_reader :response

      def initialize(response)
        @response = response
      end
    end

    class JSErrorItem
      attr_reader :message, :stack

      def initialize(message, stack)
        @message = message
        @stack   = stack
      end

      def to_s
        [message, stack].join("\n")
      end
    end

    class BrowserError < ClientError
      def name
        response['name']
      end

      def error_parameters
        response['args'].join("\n")
      end

      def message
        'There was an error inside the PhantomJS portion of Poltergeist. ' \
          'If this is the error returned, and not the cause of a more detailed error response, ' \
          'this is probably a bug, so please report it. ' \
          "\n\n#{name}: #{error_parameters}"
      end
    end

    class JavascriptError < ClientError
      def javascript_errors
        response['args'].first.map { |data| JSErrorItem.new(data['message'], data['stack']) }
      end

      def message
        'One or more errors were raised in the Javascript code on the page. ' \
          "If you don't care about these errors, you can ignore them by " \
          'setting js_errors: false in your Poltergeist configuration (see ' \
          'documentation for details).' \
          "\n\n#{javascript_errors.map(&:to_s).join("\n")}"
      end
    end

    class StatusFailError < ClientError
      def url
        response['args'].first
      end

      def details
        response['args'][1]
      end

      def message
        msg = "Request to '#{url}' failed to reach server, check DNS and/or server status"
        msg += " - #{details}" if details
        msg
      end
    end

    class FrameNotFound < ClientError
      def name
        response['args'].first
      end

      def message
        "The frame '#{name}' was not found."
      end
    end

    class InvalidSelector < ClientError
      def method
        response['args'][0]
      end

      def selector
        response['args'][1]
      end

      def message
        'The browser raised a syntax error while trying to evaluate ' \
          "#{method} selector #{selector.inspect}"
      end
    end

    class NodeError < ClientError
      attr_reader :node

      def initialize(node, response)
        @node = node
        super(response)
      end
    end

    class ObsoleteNode < NodeError
      def message
        'The element you are trying to interact with is either not part of the DOM, or is ' \
          'not currently visible on the page (perhaps display: none is set). ' \
          "It's possible the element has been replaced by another element and you meant to interact with " \
          "the new element. If so you need to do a new 'find' in order to get a reference to the " \
          'new element.'
      end
    end

    class UnsupportedFeature < ClientError
      def name
        response['name']
      end

      def unsupported_message
        response['args'][0]
      end

      def version
        response['args'][1].values_at('major', 'minor', 'patch').join '.'
      end

      def message
        "Running version of PhantomJS #{version} does not support some feature: #{unsupported_message}"
      end
    end

    class MouseEventFailed < NodeError
      def name
        response['args'][0]
      end

      def selector
        response['args'][1]
      end

      def position
        [response['args'][2]['x'], response['args'][2]['y']]
      end

      def message
        "Firing a #{name} at co-ordinates [#{position.join(', ')}] failed. Poltergeist detected " \
          "another element with CSS selector '#{selector}' at this position. " \
          'It may be overlapping the element you are trying to interact with. ' \
          "If you don't care about overlapping elements, try using node.trigger('#{name}')."
      end
    end

    class KeyError < ::ArgumentError
      def initialize(response)
        super(response['args'].first)
      end
    end

    class TimeoutError < Error
      def initialize(message)
        @message = message
      end

      def message
        "Timed out waiting for response to #{@message}. It's possible that this happened " \
          'because something took a very long time (for example a page load was slow). ' \
          'If so, setting the Poltergeist :timeout option to a higher value will help ' \
          '(see the docs for details). If increasing the timeout does not help, this is ' \
          'probably a bug in Poltergeist - please report it to the issue tracker.'
      end
    end

    class ScriptTimeoutError < Error
      def message
        'Timed out waiting for evaluated script to resturn a value'
      end
    end

    class DeadClient < Error
      def initialize(message)
        @message = message
      end

      def message
        "PhantomJS client died while processing #{@message}"
      end
    end

    class PhantomJSTooOld < Error
      def self.===(other)
        if other.is_a? Cliver::Dependency::VersionMismatch
          warn "#{name} exception has been deprecated in favor of using the " \
               'cliver gem for command-line dependency detection. Please ' \
               'handle Cliver::Dependency::VersionMismatch instead.'
          true
        else
          super
        end
      end
    end

    class PhantomJSFailed < Error
      def self.===(other)
        if other.is_a? Cliver::Dependency::NotMet
          warn "#{name} exception has been deprecated in favor of using the " \
               'cliver gem for command-line dependency detection. Please ' \
               'handle Cliver::Dependency::NotMet instead.'
          true
        else
          super
        end
      end
    end
  end
end
