use strict;
use warnings;

use Test::More;
use Test::Deep;
use Test::Fatal;
use Scalar::Util qw{looks_like_number};

use Selenium::Remote::Driver;
use Selenium::Firefox::Profile;
use Selenium::Remote::Spec;

#So we only modify _request_new_session to get webd3 working.
#As such, we should only test that.
NEWSESS: {

    #TODO cover case where ISA Selenium::Firefox
    my $self = bless({ is_wd3 => 1 },"Selenium::Remote::Driver");
    my $profile = Selenium::Firefox::Profile->new();
    $profile->set_preference(
        'browser.startup.homepage' => 'http://www.google.com',
    );
    my $args = {
        desiredCapabilities => {
            browserName       => 'firefox',
            version            => 666,
            platform           => 'ANY',
            javascript         => 1,
            acceptSslCerts     => 1,
            firefox_profile    => $profile,
            pageLoadStrategy   => 'none',
            proxy => {
                proxyType => 'direct',
                proxyAutoconfigUrl => 'http://localhost',
                ftpProxy           => 'localhost:1234',
                httpProxy          => 'localhost:1234',
                sslProxy           => 'localhost:1234',
                socksProxy         => 'localhost:1234',
                socksVersion       => 2,
                noProxy            => ['http://localhost'],
            },
            extra_capabilities => { #TODO these need to be translated as moz:firefoxOptions => {} automatically, and then to be put in the main hash
                binary  => '/usr/bin/firefox',
                args    => ['-profile', '~/.mozilla/firefox/vbdgri9o.default'], #gotta check this gets overridden
                profile => 'some Base64 string of a zip file. I should really make this a feature',
                log     => 'trace', #trace|debug|config|info|warn|error|fatal
                prefs   => {}, #TODO check that this is auto-set above by the Selenium::Firefox::Profile stuff
                webdriverClick => 0, #This option is OP, *must* be set to false 24/7
            },
        },
    };

    no warnings qw{redefine once};
    local *Selenium::Remote::RemoteConnection::request = sub {return { sessionId => 'zippy', cmd_status => 'OK', cmd_return => {capabilities => 'eee'} }};
    local *File::Temp::Dir::dirname = sub { return '/tmp/zippy' };
    use warnings;

    my ($args_modified,undef) = $self->_request_new_session($args);

    my $expected = {
        'alwaysMatch' => {
            'browserVersion'     => 666,
            'moz:firefoxOptions' => {
                'args' => [
                    '-profile',
                    '/tmp/zippy'
                ],
                'binary'  => '/usr/bin/firefox',
                'log'     => 'trace',
                'prefs'   => {},
                'profile' => 'some Base64 string of a zip file. I should really make this a feature',
                'webdriverClick' => 0
            },
            'platformName' => 'ANY',
            'proxy'        => {
                'ftpProxy'           => 'localhost:1234',
                'httpProxy'          => 'localhost:1234',
                'noProxy'            => [
                    'http://localhost'
                ],
                'proxyAutoconfigUrl' => 'http://localhost',
                'proxyType'          => 'direct',
                'socksProxy'         => 'localhost:1234',
                'socksVersion'       => 2,
                'sslProxy'           => 'localhost:1234'
            },
            'browserName'      => 'firefox',
            'pageLoadStrategy' => 'none',
            acceptInsecureCerts => 1,
        }
    };

    is($self->{capabilities},'eee',"Caps set correctly in wd3 mode");
    is_deeply($args_modified->{capabilities},$expected,"Desired capabilities correctly translated to Firefox (WD3)");

    #$expected->{alwaysMatch}->{'goog:chromeOptions'} = $expected->{alwaysMatch}->{'moz:firefoxOptions'};
    $expected->{alwaysMatch}->{'moz:firefoxOptions'} = {};
    #$expected->{alwaysMatch}->{'goog:chromeOptions'}->{args} = ['-profile', '~/.mozilla/firefox/vbdgri9o.default'];
    $expected->{alwaysMatch}->{browserName} = 'chrome';

    $args->{desiredCapabilities}->{browserName} = 'chrome';
    ($args_modified,undef) = $self->_request_new_session($args);
    is_deeply($args_modified->{capabilities},$expected,"Desired capabilities correctly translated to Krom (WD3)");

}

EXECOMMAND: {

    #_execute_command with payload 'hitting all the right buttons'
    #also check that fallback works w/ the right special missing word
    #also check capability shortcut
    my $self = bless({ is_wd3 => 1, capabilities => 'wakka wakka', browser_name => 'firefox' },"Selenium::Remote::Driver");

    no warnings qw{redefine once};
    local *Selenium::Remote::RemoteConnection::request = sub {return { sessionId => 'zippy', cmd_status => 'OK' }};
    local *Selenium::Remote::Spec::get_params          = sub { my ($self,$ret)       = @_; $ret->{v3} = 1; return $ret; };
    local *Selenium::Remote::Commands::get_params      = sub { die 'whee' };
    local *Selenium::Remote::Spec::parse_response      = sub { my ($self,undef,$ret) = @_; $ret->{rv3} = 1; return $ret; };
    local *Selenium::Remote::Commands::parse_response  = sub { die 'zippy' };
    use warnings;

    my ($input,$params) = ({ command => 'zippy'},{ ms => 1, type=> 1, text => 1, value => 1, using => 1});

    my $ret = $self->_execute_command($input,$params);
    is($ret->{rv3},1,"v3 code walked in _execute_command on happy path");

    $input->{command} = 'getCapabilities';
    $ret = $self->_execute_command($input,$params);
    is($ret,'wakka wakka',"v3 code walked in _execute_command on getCapabilities path");

    $input->{command} = 'HORGLE';

    no warnings qw{redefine once};
    local *Selenium::Remote::Spec::get_params          = sub { return undef; };
    local *Selenium::Remote::Commands::get_params      = sub { die 'whee' };
    local *Selenium::Remote::Spec::parse_response      = sub { my ($self,undef,$ret) = @_; $ret->{rv3} = 1; return $ret; };
    local *Selenium::Remote::Commands::parse_response  = sub { die 'zippy' };
    use warnings;

    $ret = exception { $self->_execute_command($input,$params) };
    like($ret,qr/whee/,"v2 fallback walked in _execute_command on getCapabilities path");

}

REMOTECONN: {
    my $self = bless({},'Selenium::Remote::RemoteConnection');
    $self->remote_server_addr('eee');
    $self->port(666);
    no warnings qw{redefine once};
    local *LWP::UserAgent::request = sub { my ($self,$req) = @_; return $req };
    use warnings;

    my $res = $self->request({ payload => { zippy => 1}, url => 'grid', method => 'eee' },{},1);
    is($res->content,'{"zippy":1}',"RemoteConnection payload shim works");
}

#get_cmds, get_params, parse_response
#get_caps and get_caps map have already been checked above in the _request_new_session code

SPEC: {
    my $obj = Selenium::Remote::Spec->new();
    my $cmds = $obj->get_cmds();
    subtest "parsing of spec blob done correctly" => sub {
        foreach my $key (keys(%$cmds)) {
            like($cmds->{$key}->{url},qr/^session|status/,"url parsed for $key correctly");
            is($cmds->{$key}->{url},$obj->get_url($key),"get_url accessor works for $key");
            like($cmds->{$key}->{method},qr/^GET|POST|DELETE|PUT$/,"method parsed for $key correctly");
            is($cmds->{$key}->{method},$obj->get_method($key),"get_method accessor works for $key");
            ok($cmds->{$key}->{description},"description parsed for $key correctly");
            ok(looks_like_number($cmds->{$key}->{no_content_success}),"no_content_success parsed for $key correctly");
            is($cmds->{$key}->{no_content_success},$obj->get_no_content_success($key),"get_no_content_success accessor works for $key");
        }
    };
}

SPEC_PARAMS: {
    no warnings qw{redefine once};
    local *Selenium::Remote::Spec::get_url = sub { return ':sessionId/:id/:name/:propertyName/:other/:windowHandle/timeouts' };
    use warnings;

    my $obj = Selenium::Remote::Spec->new();
    my $args = {
        session_id    => 'a',
        id            => 'man',
        name          => 'a',
        property_name => 'plan',
        other         => 'a canal',
        window_handle => 'panama',
        command       => 'fullscreenWindow',
        ms            => 666,
        type          => 'page load',
        using         => 'id',
        value         => 'whee',
        text          => 'zippy',
    };
    my $expected = {
        'method'             => 'POST',
        'no_content_success' => 1,
        'url'                => 'a/man/a/plan/a canal/panama/timeouts',
        'payload'            => {
            'handle'   => 'panama',
            'pageLoad' => 666,
            'using'    => 'css selector',
            'value'    => 'zippy',
        },
    };

    is_deeply($obj->get_params($args),$expected,"get_params: var substitution works, payload construction works (mostly)");

    $args->{type} = 'implicit';
    $expected->{payload}{implicit} = 666;
    delete $expected->{payload}{pageLoad};
    is_deeply($obj->get_params($args),$expected,"get_params: timeout payload mongling (implicit) works");

    $args->{type} = 'script';
    $expected->{payload}{script} = 666;
    delete $expected->{payload}{implicit};
    is_deeply($obj->get_params($args),$expected,"get_params: timeout payload mongling (script) works");

    no warnings qw{redefine once};
    local *Selenium::Remote::Spec::get_url = sub { return ':sessionId/:id/:name/:propertyName/:other/:windowHandle/timeouts/async_script' };
    use warnings;

    $args->{type} = 'page load';
    delete $expected->{payload}{pageLoad};
    $expected->{payload}{script} = 666;
    $expected->{payload}{type} = 'script';
    is_deeply($obj->get_params($args),$expected,"get_params: async_script substitution works");

    no warnings qw{redefine once};
    local *Selenium::Remote::Spec::get_url = sub { return ':sessionId/:id/:name/:propertyName/:other/:windowHandle/timeouts/implicit_wait' };
    use warnings;

    delete $expected->{payload}{script};
    $expected->{payload}{implicit} = 666;
    $expected->{payload}{type} = 'implicit';
    is_deeply($obj->get_params($args),$expected,"get_params: implicit_wait substitution works");

    delete $args->{text};
    $expected->{payload}{value} = "[id='whee']";
    is_deeply($obj->get_params($args),$expected,"get_params: id css substitution works");

    $args->{using} = 'class name';
    $expected->{payload}{value} = ".whee";
    is_deeply($obj->get_params($args),$expected,"get_params: class name css substitution works");

    $args->{using} = 'name';
    $expected->{payload}{value} = "[name='whee']";
    is_deeply($obj->get_params($args),$expected,"get_params: name css substitution works");
}

PARSE_RESP: {
    my $obj = Selenium::Remote::Spec->new();
    my $expected = { error => 'ID10T', 'message' => 'Please insert another quarter'};
    my $args = {
        cmd_status => 'OK',
        cmd_return => $expected,
    };
    is_deeply($obj->parse_response(undef,$args),$expected,"parse_response works");
    $args->{cmd_status} = 'NOT OK';
    like(exception { $obj->parse_response(undef,$args) },qr/insert another quarter/i,"parse_response throws on failure");
}

done_testing();