use strict;
use warnings;    ## required because I can't work out how to get percritic to use my modern config

package WebService::Google::Client;
$WebService::Google::Client::VERSION = '0.06';

# ABSTRACT: Google API Services Client.


use Data::Dumper;
use Moose;
use WebService::Google::Client::UserAgent;
use WebService::Google::Client::Discovery;
use Carp;


has 'debug' => ( is => 'rw', default => 0, lazy => 1 );
has 'ua' => (
  handles => [qw(access_token user auth_storage do_autorefresh api_query)],
  is      => 'ro',
  default => sub { WebService::Google::Client::UserAgent->new( debug => shift->debug ) },
  lazy    => 1,
);
has 'discovery' => (
  handles => [qw(get_method_meta)],
  is      => 'ro',
  default => sub {
    my $self = shift;
    return WebService::Google::Client::Discovery->new( debug => $self->debug, ua => $self->ua );
  },
  lazy => 1,
);


sub request
{
  my ( $self, $caller, $params ) = @_;

  # my $caller = (caller(0))[3];

  # like WebService::Google::Client::Calendar::Events::list
  carp "WebService::Google::Client->request Caller: " . $caller if ( $self->debug );

  #carp "request PARAMETERS: " . Dumper $params if ( $self->debug );

  my $api_q_data = $self->get_method_meta( $caller );
  $api_q_data->{ options } = $params->{ options };
  delete $params->{ options };

  #carp 'API query data: ' . Dumper $api_q_data if ( $self->debug );

  # get $params from $caller object
  # proxying $self->Service->Resource attributes

  $api_q_data->{ path } = $self->substitute_placeholders( $api_q_data->{ path }, $params );    # previously in util
                                                                                               #carp 'API query data: ' . Dumper $api_q_data if ( $self->debug );
  $self->api_query( $api_q_data );                                                             # path, method
}

## Commented out as pollutes the autogenerated README.md
## previously held in Util.pm until 5/10/18
#method substitute_placeholders
#
#  placeholderS (S)!
#
#  carp $gapi->Calendar->Events->substitute_placeholders('users/{token}/calendarList/{calendarId}/{eventID}', {
#    token =>'12345',
#    calendarId => '54321',
#    eventId => 'abcdef'
#  });  # must be users/12345/calendarList/54321/abcdef
#
#  or
#
#  $gapi->Calendar->Events->token('12345');
#  $gapi->Calendar->Events->calendarId('54321');
#  $gapi->Calendar->Events->eventId('abcdef');
#  # all atributes must be set in class
#  carp $gapi->Calendar->Events->substitute_placeholders('users/{token}/calendarList/{calendarId}/{eventID}');  # must be users/12345/calendarList/54321/abcdef
#
#
# TODO: Consider renaming substitute to more perlish interpolate
#  NB - code smells kludgy - there are some nice idioms around to make this almsot a one liner and behave better
#=cut

sub substitute_placeholders
{
  my ( $self, $string, $parameters ) = @_;

  # find all parameters in string
  my @matches = $string =~ /{[-+]?([a-zA-Z_]+)}/xg;

  carp "substitute_placeholders() matches: " . join( ',', @matches ) if ( $self->debug );    #Dumper \@matches

  for my $prm ( @matches )
  {
    # carp $prm;
    if ( defined $parameters->{ $prm } )
    {
      my $s = $parameters->{ $prm };
      carp "Value of '$prm' taken from passed parameters: " . $s if ( $self->debug );
      $string =~ s/{[+-]?$prm}/$s/xg;
    }
    else
    {
      croak "cant replace '$prm'  placeholder: no source";
    }
  }
  return $string;
}

# TODO: It has been suggested that there are MOO alternatives to AUTOLOAD - PS not inimate with either - review req'd
## leaving here for now as is really the main advertsiwed benefit but still can't get it to work beyond simplistic cases
## NB - this produces classes using the factory WebService::Google::Client::Services which includes Discovery that
##      will instantiate a new UserAgent for each instance. If insist on using this approach should probably at least
##      pass throughthe UserAgent from this class.
sub AUTOLOAD
{
  my ( $self ) = @_;
  our $AUTOLOAD;
  my $unknown_resource = ( split( /::/x, $AUTOLOAD ) )[-1]
    ; # $unknown_method_name = API .. !PS ?? -1 is not one of the allowed literal values (0, 1, 2). Use the Readonly or Const::Fast module or the "constant" pragma instead at line 167, column 56.  Unnamed numeric literals make code less maintainable.  (Severity: 2)

  carp "unknown_resource - $unknown_resource" if ( $self->debug );
  require WebService::Google::Client::Services;
  my $added = WebService::Google::Client::Services->new;    ## create a new instance of the factory class ?
  $added->debug( $self->debug );                            ## inherit debug
  $added->generate_one( $self, lcfirst $unknown_resource );
  return $self->$unknown_resource;

  #return $self;
}



1;

__END__

=pod

=encoding UTF-8

=head1 NAME

WebService::Google::Client - Google API Services Client.

=head1 VERSION

version 0.06

=head1 SYNOPSIS

    use WebService::Google::Client;
    use Data::Dumper;

    my $gapi = WebService::Google::Client->new(debug => 0); # my $gapi = WebService::Google::Client->new(access_token => '');
    my $user = 'peter@pscott.com.au'; # full gmail or G-Suite email

    $gapi->auth_storage->setup({type => 'jsonfile', path => '/path' }); # by default

    $gapi->user($user);       ## allows to select user configuration by google auth'd email address
    $gapi->do_autorefresh(1); ## refresh auth token with refresh token if auth session has expired

    my $r1 = $gapi->Calendar->Events->list({ calendarId => 'primary' })->json;
    carp scalar @{$r1->{items}};

    print Dumper $gapi_agent->Gmail->Users->getProfile( { userId => 'me'  } )->json;

    print $gapi_agent->discovery->chi->root_dir(); ## provides the directory used to hold cached API discovery specs by CHI

To create or modify authorization file with scope and user tokens in current folder run I<goauth> CLI tool

=head2 TYPICAL USAGE ( WITHOUT AUTO-GENERATED CLASSES )

    use feature 'say';
    use WebService::Google::Client;

    my $gapi = WebService::Google::Client->new(debug => 0);
    $gapi->auth_storage->setup({ type => 'jsonfile', path => './gapi.json' }) || croak('Unable to ');
    my $aref_token_emails = $gapi->auth_storage->storage->get_token_emails_from_storage;
    my $user = $aref_token_emails->[0];
    print "Running tests with default user email = $user\n";
    $gapi->user($user);
    $gapi->do_autorefresh;

    my $cl =   $gapi->api_query({
        method => 'get',
        path       => "https://www.googleapis.com/gmail/v1/users/me/messages?q=newer_than:1d;to:$user", 
    });

    if ($cl->code eq '200') ## Mojo::Message::Response
    {
        foreach my $msg ( @{ $cl->json->{messages} } )
        {
            say $cl->to_string;
        }

    }

See unit test in xt folder for more examples and some use cases for specific APIS in the /examples folder

=head1 KEY FEATURES

=over 1

=item Object-oriented calls by API->Resource->method schema. Like $gapi->Calendar->Events->lists ( Under Review )

=item Classes are generated dynamically using L<Moose::Meta::Class> based on Google API Discovery Service ( Under review )

=item API Discovery with local caching using CHI File

=item OAUTH app credentials (client_id, client_secret, users access_token && refresh_token) storage stored in local gapi.json file

=item Automatic access_token refresh (if user has refresh_token) and saving refreshed token to storage

=item CLI tool (I<goauth>) with lightweight http server to simplify config OAuth2 configuration, sccoping, authorization and obtaining access_ and refresh_ tokens

=back

=head2 TODO: 

=over 1

=item DBI and MongoDB not currently implemented

=item Refactor tests

=item Memoise discovery data

=item Option to Configure Auth Storage on new instance creation 

=back

=head1 SEE ALSO

=over 1

=item *

L<Moo::Google> - The original code base later forked into L<WebService::Google> by Steve Dondley who was unable to collaborate with

=item * 

L<Google Swagger API https://github.com/APIs-guru/google-discovery-to-swagger> 

=back

=head1 AUTHORS

=over 4

=item *

Pavel Serikov <pavelsr@cpan.org>

=item *

Peter Scott <peter@localshop.com.au>

=back

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2017-2018 by Pavel Serikov, Peter Scott and others.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
