package GitLab::API::v4::WWWClient;
our $VERSION = '0.27';

=encoding utf8

=head1 NAME

GitLab::API::v4::WWWClient - A client that works against the GitLab web site.

=head1 SYNOPSIS

    use GitLab::API::v4::WWWClient;
    
    my $client = GitLab::API::v4::WWWClient->new(
        url => 'https://git.example.com/',
    );
    
    $client->sign_in( $username, $password );
    
    my $res = $client->get( $path );

=head1 DESCRIPTION

This class makes it possible to interact with the GitLab web site.

=cut

use Carp qw( croak );
use HTTP::Tiny;
use List::Util qw( first );
use Types::Common::String qw( NonEmptySimpleStr );

use Moo;
use strictures 2;
use namespace::clean;

has _session => (
    is       => 'rw',
    init_arg => undef,
);

sub _croak_res {
    my ($verb, $url, $res) = @_;
    
    return if $res->{status} !~ m{^5};

    local $Carp::Internal{ 'GitLab::API::v4::WWWClient' } = 1;

    croak sprintf(
        'Error %sing %s (HTTP %s): %s',
        uc($verb), $url,
        $res->{status}, ($res->{reason} || 'Unknown'),
    );
}

=head1 REQUIRED ARGUMENTS

=head2 url

This is the base URL to your GitLab web site.

=cut

has url => (
    is       => 'ro',
    isa      => NonEmptySimpleStr,
    required => 1,
);

=head1 METHODS

=head2 sign_in

    $client->sign_in( $username, $password );

Signs in the client given the username and password.

=cut

sub sign_in {
    my ($self, $username, $password) = @_;

    my $tiny = HTTP::Tiny->new(
        verify_SSL => 1,
        max_redirect => 0,
    );

    my $base_url = $self->url();
    $base_url =~ s{/$}{};
    my $sign_in_url = "$base_url/users/sign_in";

    my $load_res = $tiny->get( $sign_in_url );

    _croak_res( 'get', $sign_in_url, $load_res );

    my $token = (
        $load_res->{content} =~
        m{name="authenticity_token" value="(.+?)"}
    )[0];

    
    my ($first_session) = do{
        my $set_cookie_headers = $load_res->{headers}->{ 'set-cookie' };
        $set_cookie_headers = [ $set_cookie_headers ] if !ref $set_cookie_headers;
        my $value = first { $_ =~ m{^_gitlab_session=(.*)} } @$set_cookie_headers;
        $value =~ s{^_gitlab_session=}{}r;
    };

    my $submit_res = $tiny->post_form(
        $sign_in_url,
        {
            'utf8'               => '✓',
            'authenticity_token' => $token,
            'user[login]'        => $username,
            'user[password]'     => $password,
            'user[remember_me]'  => 0,
        },
        {
            headers => {
                'Referer' => $sign_in_url,
                'Cookie'  => "_gitlab_session=$first_session",
                'Cookie2' => '$Version="1"',
            },
        },
    );

    _croak_res( 'post', $sign_in_url, $submit_res );

    my ($second_session) = do{
        my $set_cookie_headers = $submit_res->{headers}->{ 'set-cookie' };
        $set_cookie_headers = [ $set_cookie_headers ] if !ref $set_cookie_headers;
        my $value = first { $_ =~ m{^_gitlab_session=(.*)} } @$set_cookie_headers;
        $value =~ s{^_gitlab_session=}{}r;
    };

    my $home_res = $tiny->get(
        $base_url,
        {
            headers => {
                'Referer' => $sign_in_url,
                'Cookie'  => "_gitlab_session=$second_session",
                'Cookie2' => '$Version="1"',
            },
        },
    );

    _croak_res( 'get', $base_url, $home_res );

    my $ok = ( $home_res->{content} =~ m{sign-out-link} ) ? 1 : 0;
    croak 'Failed to sign in' if !$ok;

    $self->_session( $second_session );

    return;
}

=head2 get

    my $res = $client->get( $path );

Gets the path and returns the L<HTTP::Tiny> response hash.

=cut

sub get {
    my ($self, $path) = @_;

    my $tiny = HTTP::Tiny->new(
        verify_SSL => 1,
        max_redirect => 0,
    );

    my $base_url = $self->url();
    $base_url =~ s{/$}{};
    $path =~ s{^/}{};
    my $url = "$base_url/$path";

    my $session = $self->_session();
    my $headers = $session ? {
        'Cookie'  => "_gitlab_session=$session",
        'Cookie2' => '$Version="1"',
    } : {};

    my $res = $tiny->get(
        $url,
        { headers=>$headers },
    );

    _croak_res( 'get', $url, $res );

    return $res;
}

1;
__END__

=head1 SUPPORT

See L<GitLab::API::v4/SUPPORT>.

=head1 AUTHORS

See L<GitLab::API::v4/AUTHORS>.

=head1 LICENSE

See L<GitLab::API::v4/LICENSE>.

=cut