#!/usr/bin/env perl
use strictures 2;

use Test2::Require::AuthorTesting;
use Test2::V0;

use Log::Any::Adapter 'TAP';
use MIME::Base64 qw( decode_base64 );
use Path::Tiny;

use GitLab::API::v4::Config;
use GitLab::API::v4::WWWClient;
use GitLab::API::v4;

my $config = GitLab::API::v4::Config->new();
my $api = GitLab::API::v4->new( $config->args() );

subtest projects => sub{
    my $stamp = time();
    my $project_name = "gitlab-api-v4-test-$stamp";

    my $created_project = $api->create_project(
        { name=>$project_name },
    );
    ok( $created_project, 'project created' );

    my $project_id = $created_project->{id};
    my $project = $api->project( $project_id );
    ok( $project, 'project found' );

    subtest upload_file_to_project => sub{
        my $file = Path::Tiny->tempfile( SUFFIX => '.txt' );
        my $file_content = 'Hello GitLab, this is a test of ' . ref($api) . '.';
        $file->spew( $file_content );

        my $upload = $api->upload_file_to_project(
            $project_id,
            { file=>"$file" },
        );
        ok( $upload->{url}, 'got an upload response' );

        my $www_base_url = $api->url();
        $www_base_url =~ s{/api/v4.*$}{};

        my $www_client = GitLab::API::v4::WWWClient->new(
            url => $www_base_url,
        );

        $www_client->sign_in(
            'root',
            $ENV{GITLAB_API_V4_ROOT_PASSWORD},
        );

        my $project_path = $project->{path_with_namespace};
        my $upload_path = $upload->{url};
        my $download_path = "$project_path/$upload_path";

        my $res = $www_client->get( $download_path );
        is( $res->{content}, $file_content, 'upload_file_to_project worked' );
    };

    subtest 'file methods' => sub{
        $api->create_file(
            $project_id,
            'foo/bar.txt',
            {
                branch => 'master',
                content => 'Test of create file.',
                commit_message => 'This is a commit.',
            },
        );

        my $file = $api->file(
            $project_id,
            'foo/bar.txt',
            { ref=>'master' },
        );
        is(
            decode_base64( $file->{content} ),
            'Test of create file.',
            'created file is there; and looks right',
        );

        my $content = $api->raw_file(
            $project_id,
            'foo/bar.txt',
            { ref=>'master' },
        );
        is(
            $content,
            'Test of create file.',
            'able to retrieve the file raw',
        );

        $api->edit_file(
            $project_id,
            'foo/bar.txt',
            {
                branch => 'master',
                content => 'Test of edit file.',
                commit_message => 'This is the next commit.',
            },
        );
        my $edited_file = $api->file(
            $project_id,
            'foo/bar.txt',
            { ref=>'master' },
        );
        is(
            decode_base64( $edited_file->{content} ),
            'Test of edit file.',
            'editing a file worked',
        );

        $api->delete_file(
            $project_id,
            'foo/bar.txt',
            {
                branch => 'master',
                commit_message => 'This is the last commit.',
            },
        );
        $file = $api->file(
            $project_id,
            'foo/bar.txt',
            { ref=>'master' },
        );
        is(
            $file, undef,
            'file was deleted',
        );
    };

    subtest hooks => sub{
        my $hook = $api->create_project_hook(
            $project->{id},
            { url=>'http://example.com/gitlab-hook-1' },
        );
        ok( $hook, 'create_project_hook returned the hook' );

        $hook = $api->edit_project_hook(
            $project->{id}, $hook->{id},
            { url=>'http://example.com/gitlab-hook-2' },
        );
        ok( $hook, 'edit_project_hook returned the hook' );
        my $hook_id = $hook->{id};

        $hook = $api->project_hook( $project->{id}, $hook_id );
        ok( $hook, 'project_hook returned the hook' );
        is( $hook->{url}, 'http://example.com/gitlab-hook-2', 'hook looks right' );

        $api->delete_project_hook( $project->{id}, $hook_id );
        $hook = $api->project_hook( $project->{id}, $hook_id );
        ok( (!$hook), 'delete_project_hook seems to have worked' );

        like(
            dies { $api->delete_project_hook( $project->{id}, $hook_id ) },
            qr{\b404\b},
            'a subsequent delete_project_hook throws',
        );
    };

    $api->delete_project( $project_id );
    pass 'project deleted';
};

subtest users => sub{
    my $stamp = time();
    my $username = "gitlab-api-v4-test-$stamp";
    $api->create_user({
        username   => $username,
        email      => "$username\@example.com",
        password   => 'd5fzHF7tfgh',
        name       => 'GitLabAPIv4 Test',
    });
    pass 'user created';

    my $users = $api->users({ username => $username });
    is( @$users+0, 1, 'one user found' );

    my $user = shift @$users;
    is( $user->{username}, $username, 'user has correct username' );
    die 'Incorrect user found' if $user->{username} ne $username;

    my $user_id = $user->{id};
    ok( $api->block_user($user_id), 'user blocked' );
    ok( (!$api->block_user($user_id)), 'user cannot be blocked again' );
    ok( $api->unblock_user($user_id), 'user unblocked' );
    ok( (!$api->unblock_user($user_id)), 'user cannot be unblocked again' );

    $api->delete_user($user_id);
    pass 'user deleted';
};

subtest failures => sub{
    is( $api->user( 12345678 ), undef, 'GETing an unknown entity returns undef' );
    my $err_re = qr{^Error PUTing \S+/users/12345678 \(HTTP 404\): Not Found \{"message":"404 User Not Found"\}};
    like( dies{ $api->edit_user( 12345678, {} ) }, $err_re, 'POSTing an unknown entity throws a specific exception' );
};

done_testing;

sub join_paths {
    my @paths = @_;

    return() if !@paths;
    return @paths if @paths==1;

    my $first = shift @paths;
    $first =~ s{/$}{};

    my $last = pop @paths;
    $last =~ s{^/}{};

    @paths = (
        map { $_ =~ s{^/?(.*?)/?$}{$1}; $_ }
        @paths
    );

    return join('/', $first, @paths, $last);
}