#!/usr/bin/env perl
use warnings;
use strict;

use Jifty::Test::Dist tests => 104;
use Jifty::Test::WWW::Mechanize;

my $server  = Jifty::Test->make_server;
isa_ok($server, 'Jifty::TestServer');

my $URL     = $server->started_ok;
my $mech    = Jifty::Test::WWW::Mechanize->new();
$mech->requests_redirectable([]);

ok(1, 'Loaded the test script');

my $g1 = TestApp::Plugin::REST::Model::Group->new(
    current_user => TestApp::Plugin::REST::CurrentUser->superuser );
$g1->create( name => 'test group' );
ok( $g1->id );

my $u1 = TestApp::Plugin::REST::Model::User->new(
    current_user => TestApp::Plugin::REST::CurrentUser->superuser,
);
$u1->create(name => 'test', email => 'test@example.com', group_id => $g1->id);
ok($u1->id);

our $FORMAT_NUMBER;

sub result_of {
    local $Test::Builder::Level = $Test::Builder::Level + 1;
    my $request = shift;
    my $test    = shift;

    if (!ref($request)) {
        $request = {
            mech_method => 'get',
            url         => $request,
        };
    }

    my %loaders = (
        yml  => \&Jifty::YAML::Load,
        json => \&Jifty::JSON::decode_json,
        js   => sub {
            my $js = shift;
            $js =~ s/.*? = //; # variable assignment
            return Jifty::JSON::decode_json($js);
        },
    );

    local $FORMAT_NUMBER = 0;
    for my $format (keys %loaders) {
        $FORMAT_NUMBER++;

        my $url = $URL . $request->{url} . '.' . $format;

        my $method = $request->{mech_method};
        my $response = $mech->$method($url, @{ $request->{mech_args} || [] });

        ok($response->is_success || $response->is_redirect, "$method successful");
        my @contents = $response->content;

        if (my $location = $response->header('Location')) {
            $response = $mech->get($location);
            ok($response->is_success, "redirect successful");
            push @contents, $response->content;
        }

        local $Test::Builder::Level = $Test::Builder::Level + 1;

        eval {
            @contents = map { scalar $loaders{$format}->($_) } @contents;
        };
        fail($@) if $@;

        $test->(@contents);
    }
}

sub result_of_post {
    local $Test::Builder::Level = $Test::Builder::Level + 1;
    my $request    = shift;
    my $parameters = shift;
    my $test       = shift;

    if (!ref($request)) {
        $request = {
            mech_method => 'post',
            mech_args   => [$parameters],
            url         => $request,
        };
    }

    result_of($request, $test);
}

result_of '/=/model' => sub {
    is_deeply($_[0], [
        'TestApp.Plugin.REST.Model.Group',
        'TestApp.Plugin.REST.Model.User',
    ]);
};

result_of '/=/model/User' => sub {
    is(scalar keys %{ $_[0] }, 5, '5 keys in the user record');
};

result_of '/=/model/user/id' => sub {
    is(@{ $_[0] }, 1, "one user");
};

result_of '/=/model/user/id/1' => sub {
    is_deeply($_[0], {
        name  => 'test',
        email => 'test@example.com',
        id    => 1,
        tasty => undef,
        group_id => 1,
        group => { name => 'test group', id => 1 },
    });
};

result_of '/=/model/user/id/1/email' => sub {
    is($_[0], 'test@example.com');
};

result_of_post '/=/model/user' => {
    name => 'moose',
    email => 'moose@example.com',
} => sub {
    my ($action_result, $record) = @_;

    my $id = 2 + ($FORMAT_NUMBER - 1);

    is_deeply($action_result, {
        action_class   => 'TestApp::Plugin::REST::Action::CreateUser',
        content        => { id => $id },
        error          => undef,
        failure        => 0,
        field_errors   => {},
        field_warnings => {},
        message        => 'Created',
        success        => 1,
    });

    is_deeply($record, {
        email => 'moose@example.com',
        id    => $id,
        name  => 'moose',
        tasty => undef,
        group_id => undef,
        group => undef,
    });
};


# on PUT    '/=/model/*/*/*' => \&replace_item;
# on DELETE '/=/model/*/*/*' => \&delete_item;

# on POST   '/=/model/*'     => \&create_item;
$mech->post( $URL . '/=/model/User', { name => "moose", email => 'moose@example.com' } );
is($mech->status, 302, "create via POST to model worked");

$mech->post( $URL . '/=/model/Group', { name => "moose" } );
is($mech->status, 403, "create via POST to model with disallowed create action failed with 403");

TODO: {
    local $TODO = 'Missing mandatory field fails with error 500 on debian unstable (nov 2009), test is commented out to keep time';
#$mech->post( $URL . '/=/model/Group', { } );
#is($mech->status, 403, "create via POST with missing mandatory field");
    ok(0, $TODO);
}

# on GET    '/=/search/*/**' => \&search_items;
$mech->get_ok('/=/search/user/id/1.yml');
my $content = get_content();
is_deeply($content, [{ name => 'test', email => 'test@example.com', id => 1, tasty => undef, group_id => 1, group => { name => 'test group', id => 1 } }]);

$mech->get_ok('/=/search/user/id/1/name/test.yml');
$content = get_content();
is_deeply($content, [{ name => 'test', email => 'test@example.com', id => 1, tasty => undef, group_id => 1, group => { name => 'test group', id => 1  } }]);

$mech->get_ok('/=/search/user/id/1');
$content = get_content();
unlike($content, qr/HASH/);
like($content, qr/test\@example.com/);

$mech->get_ok('/=/search/user/id/1.html');
$content = get_content();
like($content, qr/test\@example.com/);

$mech->get_ok('/=/search/user/id/1/name/test/email.yml');
$content = get_content();
is_deeply($content, ['test@example.com']);

$mech->get('/=/search/Usery/id/1.yml');
is($mech->status,'404');

$mech->get('/=/search/user/id/1/name/foo.yml');
is($mech->status,'200');
$content = get_content();
is_deeply($content, []);

# on GET    '/=/action'      => \&list_actions;

my @actions = qw(
                 TestApp.Plugin.REST.Action.CreateGroup
                 TestApp.Plugin.REST.Action.UpdateGroup
                 TestApp.Plugin.REST.Action.SearchGroup
                 TestApp.Plugin.REST.Action.ExecuteGroup
                 TestApp.Plugin.REST.Action.CreateUser
                 TestApp.Plugin.REST.Action.UpdateUser
                 TestApp.Plugin.REST.Action.DeleteUser
                 TestApp.Plugin.REST.Action.SearchUser
                 TestApp.Plugin.REST.Action.ExecuteUser
                 TestApp.Plugin.REST.Action.DoSomething
                 Jifty.Action.AboutMe
                 Jifty.Action.Autocomplete
                 Jifty.Action.Redirect);

$mech->get_ok('/=/action/');
is($mech->status, 200);
for (@actions) {
    $mech->content_contains($_);
}
$mech->get_ok('/=/action.yml');
my @got = @{get_content()};

is(
    join(",", sort @got ),
    join(",",sort @actions), 
, "Got all the actions as YAML");


# on GET    '/=/action/*'    => \&list_action_params;

$mech->get_ok('/=/action/DoSomething');
is($mech->status, 200);
$mech->get_ok('/=/action/TestApp::Plugin::REST::Action::DoSomething');
is($mech->status, 200);
$mech->get_ok('/=/action/TestApp.Plugin.REST.Action.DoSomething');
is($mech->status, 200);

# Parameter name
$mech->content_contains('email');
# Parameter label
$mech->content_contains('Email');
# Default value
$mech->content_contains('example@email.com');

$mech->get_ok('/=/action/DoSomething.yml');
is($mech->status, 200);

my %args = %{get_content()};
ok($args{email}, "Action has an email parameter");
is($args{email}{label}, 'Email', 'email has the correct label');
is($args{email}{default_value}, 'example@email.com', 'email has the correct default');


# on POST   '/=/action/*'    => \&run_action;
# 

$mech->post( $URL . '/=/action/DoSomething', { email => 'good@email.com' } );

$mech->content_contains('Something happened!');

$mech->post( $URL . '/=/action/DoSomething', { email => 'bad@email.com' } );

$mech->content_contains('Bad looking email');
$mech->content_lacks('Something happened!');

$mech->post( $URL . '/=/action/DoSomething', { email => 'warn@email.com' } );
    
$mech->content_contains('Warning for email');
$mech->content_contains('Something happened!');

# Test YAML posts
$mech->post ( $URL . '/=/action/DoSomething.yml', { email => 'good@email.com' } );

my %content;
eval {
    %content = %{get_content()};
};

ok($content{success});
is($content{message}, 'Something happened!');

    
$mech->post ( $URL . '/=/action/DoSomething.yaml', { email => 'bad@email.com' } );

eval {
    %content = %{get_content()};
};

ok(!$content{success}, "Action that doesn't validate fails");
is($content{field_errors}{email}, 'Bad looking email');


sub get_content { return Jifty::YAML::Load($mech->content)}

1;

