package Selenium::Remote::ErrorHandler;

use strict;
use warnings;

# ABSTRACT: Error handler for Selenium::Remote::Driver

use Moo;
use Carp qw(croak);

# We're going to handle only codes that are errors.
# http://code.google.com/p/selenium/wiki/JsonWireProtocol
has STATUS_CODE => (
    is      => 'lazy',
    builder => sub {
        return {
            7 => {
                'code' => 'NO_SUCH_ELEMENT',
                'msg' =>
                  'An element could not be located on the page using the given search parameters.',
            },
            8 => {
                'code' => 'NO_SUCH_FRAME',
                'msg' =>
                  'A request to switch to a frame could not be satisfied because the frame could not be found.',
            },
            9 => {
                'code' => 'UNKNOWN_COMMAND',
                'msg' =>
                  'The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource.',
            },
            10 => {
                'code' => 'STALE_ELEMENT_REFERENCE',
                'msg' =>
                  'An element command failed because the referenced element is no longer attached to the DOM.',
            },
            11 => {
                'code' => 'ELEMENT_NOT_VISIBLE',
                'msg' =>
                  'An element command could not be completed because the element is not visible on the page.',
            },
            12 => {
                'code' => 'INVALID_ELEMENT_STATE',
                'msg' =>
                  'An element command could not be completed because the element is in an invalid state (e.g. attempting to click a disabled element).',
            },
            13 => {
                'code' => 'UNKNOWN_ERROR',
                'msg' =>
                  'An unknown server-side error occurred while processing the command.',
            },
            15 => {
                'code' => 'ELEMENT_IS_NOT_SELECTABLE',
                'msg' =>
                  'An attempt was made to select an element that cannot be selected.',
            },
            19 => {
                'code' => 'XPATH_LOOKUP_ERROR',
                'msg' =>
                  'An error occurred while searching for an element by XPath.',
            },
            21 => {
                'code' => 'Timeout',
                'msg' =>
                  'An operation did not complete before its timeout expired.',
            },
            23 => {
                'code' => 'NO_SUCH_WINDOW',
                'msg' =>
                  'A request to switch to a different window could not be satisfied because the window could not be found.',
            },
            24 => {
                'code' => 'INVALID_COOKIE_DOMAIN',
                'msg' =>
                  'An illegal attempt was made to set a cookie under a different domain than the current page.',
            },
            25 => {
                'code' => 'UNABLE_TO_SET_COOKIE',
                'msg' =>
                  'A request to set a cookie\'s value could not be satisfied.',
            },
            26 => {
                'code' => 'UNEXPECTED_ALERT_OPEN',
                'msg'  => 'A modal dialog was open, blocking this operation',
            },
            27 => {
                'code' => 'NO_ALERT_OPEN_ERROR',
                'msg' =>
                  'An attempt was made to operate on a modal dialog when one was not open.',
            },
            28 => {
                'code' => 'SCRIPT_TIMEOUT',
                'msg' =>
                  'A script did not complete before its timeout expired.',
            },
            29 => {
                'code' => 'INVALID_ELEMENT_COORDINATES',
                'msg' =>
                  'The coordinates provided to an interactions operation are invalid.',
            },
            30 => {
                'code' => 'IME_NOT_AVAILABLE',
                'msg'  => 'IME was not available.',
            },
            31 => {
                'code' => 'IME_ENGINE_ACTIVATION_FAILED',
                'msg'  => 'An IME engine could not be started.',
            },
            32 => {
                'code' => 'INVALID_SELECTOR',
                'msg'  => 'Argument was an invalid selector (e.g. XPath/CSS).',
            },
        };
    }
);

=head1 SUBROUTINES

=head2 process_error (Selenium::Remote::Driver $driver, HTTP::Response $response)

Instead of just returning the end user a server returned error code, this returns a more human readable & usable error message.

Used internally in Selenium::Remote::Driver, but overriding this might be useful in some situations.
You could additionally alter the STATUS_CODE parameter of this module to add extra handlers if the situation warrants it.

=cut

sub process_error {
    my ($self, $resp) = @_;
    # TODO: Handle screen if it sent back with the response. Either we could
    # let the end user handle it or we can save it an image file at a temp
    # location & return the path.

    # handle stacktrace-only responses by assuming unknown error
    my $is_stacktrace = !$resp->{status};
    $resp->{status} = 13 unless $resp->{status};

    my $ret;
    #XXX capitalization is inconsistent among geckodriver versions
    $ret->{'stackTrace'} = $resp->{'value'}->{'stacktrace'} // $resp->{'value'}->{'stackTrace'};
    $ret->{'error'}      = $is_stacktrace ? $resp->{value}->{error} : $self->STATUS_CODE->{$resp->{'status'}};
    $ret->{'message'}    = $resp->{'value'}->{'message'};

    return $ret;
}

1;

__END__
