package WebService::Strava::Auth;

use v5.010;
use strict;
use warnings;
use Config::Tiny;
use LWP::Authen::OAuth2;
use JSON qw(decode_json encode_json);
use JSON::Parse 'valid_json';
use Carp qw(croak);
use File::Basename;
use File::MimeInfo::Magic;
use Data::Dumper;
use Method::Signatures 20140224;
use Moo;
use experimental 'say';
use namespace::clean;

# ABSTRACT: A Strava Segment Object

our $VERSION = '0.06'; # VERSION: Generated by DZP::OurPkg:Version


# Debugging hooks in case things go weird. (Thanks @pjf)

around BUILDARGS => sub {
  my $orig  = shift;
  my $class = shift;
  
  if ($WebService::Strava::DEBUG) {
    warn "Building task with:\n";
    warn Dumper(\@_), "\n";
  }
  
  return $class->$orig(@_);
};

has 'api_base'      => (is => 'ro', default => sub { 'https://www.strava.com/api/v3' });
has 'config_file'   => ( is => 'ro', default  => sub { "$ENV{HOME}/.stravarc" } );
has 'config'        => ( is => 'rw', lazy => 1, builder => 1 );
has 'scope'         => ( is => 'ro', default  => sub { "view_private,write" } );
has 'auth'          => ( is => 'rw', lazy => 1, builder => 1, handles => [ qw( get post ) ] );

# TODO: Potentially allow the config to be passed through instead of loaded.
#has 'client_id'     => ( is => 'ro' );
#has 'client_secret' => ( is => 'ro' );
#has 'token_string'  => ( is => 'rw' );


method setup() {
  # Request Client details if non existent
  if (! $self->config->{auth}{client_id} ) {
    $self->config->{auth}{client_id} = $self->prompt("Paste enter your client_id");
  }
  
  if (! $self->config->{auth}{client_secret} ) {
    $self->config->{auth}{client_secret} = $self->prompt("Paste enter your client_secret");
  }
  
  # Build auth object - TODO: Write a strava authentication provider! Issue #1
  my $oauth2 = LWP::Authen::OAuth2->new(
    client_id => $self->{config}{auth}{client_id},
    client_secret => $self->{config}{auth}{client_secret},
    service_provider => "Strava",
    redirect_uri => "http://127.0.0.1",
    scope => $self->{scope},
  );

  # Get authentican token string
  say "Log into the Strava account and browse the following url";
  my $url = $oauth2->authorization_url();
  say $url;
  my $code = $self->prompt("Paste code result here");
  $oauth2->request_tokens(code => $code);
  $self->config->{auth}{token_string} = $oauth2->token_string;
  $self->config->write($self->{config_file});
}

method _build_config() {
  my $config;
  if ( -e $self->{config_file} ) {
    $config = Config::Tiny->read( $self->{config_file} );
    unless ($config->{auth}{client_id} 
            && $config->{auth}{client_secret}) {
      die <<"END_DIE";
Cannot find user credentials in $self->{config_file}

You'll need to have a $self->{config_file} file that looks like 
the following:

    [auth]
    client_id     = xxxxx
    client_secret = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

You can get these values by going to https://www.strava.com/settings/api

Running 'strava --setup' or \$strava->auth->setup will run you through
setting up Oauth2.

END_DIE
}
  } else {
    $config = Config::Tiny->new();
  }
  return $config;
}

method _build_auth() {
  $self->config;
  croak "Missing token, please re-run setup" unless $self->config->{auth}{token_string};

  my $oauth2 = LWP::Authen::OAuth2->new(
    client_id => $self->{config}{auth}{client_id},
    client_secret => $self->{config}{auth}{client_secret},
    service_provider => "Strava",
    token_string => $self->config->{auth}{token_string},
  );
  return $oauth2;
}


method get_api($api_path) {
  my $response = $self->auth->get($self->{api_base}.$api_path);
  my $json = $response->decoded_content;
  if (! valid_json($json) ) {
    croak("Something went wrong, a JSON string wasn't returned");
  }
  if ($ENV{STRAVA_DEBUG}) {
    say Dumper($json);
  }
  return decode_json($json);
}


method delete_api($api_path) {
  my $response = $self->auth->delete($self->{api_base}.$api_path);
  if ($ENV{STRAVA_DEBUG}) {
    say Dumper($response);
  }
  return $response->code == 204 ? 1 : 0;
}


method post_api($api_path,$content) {
  my $response = $self->auth->post(
    $self->{api_base}.$api_path,
    Content => encode_json($content),
  );

  my $json = $response->decoded_content;
  if (! valid_json($json) ) {
    croak("Something went wrong, a JSON string wasn't returned");
  }
  if ($ENV{STRAVA_DEBUG}) {
    say Dumper($json);
  }
  return decode_json($json);
}


method uploads_api(
  :$file,
  :$type,
  :$activity_type?,
  :$name?,
  :$description?,
  :$private?,
  :$trainer?,
  :$external_id?,
) {
  my $filename = basename($file);
  my $mimetype = mimetype($file);

  my $content = { 
    file =>  [ 
      $file, 
      $filename, 
      Content_Type => $mimetype, 
      'Content-Transfer-Encoding' => 'binary',
    ], 
    data_type => lc($type),
  };

  $content->{activity_type} = lc($activity_type) if $activity_type;
  $content->{name} = $name if $name;
  $content->{description} = $description if $description;
  $content->{private} = $private if $private;
  $content->{trainer} = $trainer if $trainer;
  $content->{external_id} = $external_id if $external_id;

  my $response = $self->auth->post(
    $self->{api_base}.'/uploads',
    Content_Type => 'multipart/form-data',
    Content => $content,
  );

  my $json = $response->decoded_content;
  if (! valid_json($json) ) {
    croak("Something went wrong, a JSON string wasn't returned");
  }
  if ($ENV{STRAVA_DEBUG}) {
    say Dumper($json);
  }
  return decode_json($json);
}

method prompt($question,:$default) { # inspired from here: http://alvinalexander.com/perl/edu/articles/pl010005
  if ($default) {
    say $question, "[", $default, "]: ";
  } else {
    say $question, ": ";
    $default = "";
  }

  $| = 1;               # flush
  $_ = <STDIN>;         # get input

  chomp;
  if ("$default") {
    return $_ ? $_ : $default;    # return $_ if it has a value
  } else {
    return $_;
  }
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

WebService::Strava::Auth - A Strava Segment Object

=head1 VERSION

version 0.06

=head1 SYNOPSIS

  my $auth = WebService::Strava::Auth->new(
    ['config_file' => '/path/to/file'], 
    ['scope' => 'read']
  );

=head1 DESCRIPTION

  A thin wrapper around LWP::Authen::OAuth2 to provide a pre-authenticated Oauth2 object
  as a helper for the rest of WebService::Strava.

=head1 METHODS

=head2 setup()

  $auth->setup();

Runs through configuring Oauth2 authentication with the Strava API. You
will need your client_id and client_secret available here:

https://www.strava.com/settings/api

=head2 get_api

  $strava->auth->get_api($url);

Mainly used for an internal shortcut, but will return a parsed
perl data structure of what the api returns.

=head2 delete_api

  $strava->auth->delete_api($url);

Mainly used for an internal shortcut, but will return true on 
success or false on failure. 

=head2 post_api

  $strava->auth->post_api($url,$content);

Mainly used for an internal shortcut, but will return a parsed
perl data structure of what the api returns. '$content' is expected 
to be a plain perl data structure. The method will encode it to json.

=head2 uploads_api

  $strava->auth->uploads_api(file => 'sample.gpx', type => 'gpx');

Mainly used for an internal shortcut, but will return a parsed
perl data structure of what the api returns.

=head1 AUTHOR

Leon Wright < techman@cpan.org >

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2014 by Leon Wright.

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