package Business::YQL;
use Moo;

our $VERSION = '0.0002'; # VERSION

use HTTP::Request::Common qw(GET POST);
use URI;
use URI::QueryParam;
use LWP::UserAgent;
use JSON::Any;
use Carp qw(croak);
use Log::Any qw($log);
use Try::Tiny;

has scheme  => (is => 'ro', default => 'http'                           );
has domain  => (is => 'ro', default => 'query.yahooapis.com'            );
has version => (is => 'ro', default => 'v1'                             );
has timeout => (is => 'ro', default => 10                               );
has retries => (is => 'ro', default => 3                                );

has json    => (is => 'ro', lazy => 1, default => sub { JSON::Any->new });

has uri     => (
    is      => 'ro',
    lazy    => 1,
    default => sub {
        my $self = shift;
        return sprintf '%s://%s/%s/public/yql',
            $self->scheme, $self->domain, $self->version;
    },
);

has ua      => (
    is      => 'ro',
    lazy    => 1,
    default => sub {
        my $self = shift;
        my $ua = LWP::UserAgent->new;
        $ua->timeout($self->timeout);
        return $ua
    },
);

sub q {
    my ($self, $query) = @_;
    croak "Invalid query string"
        unless $query;

    my %params = (
        q           => $query,
        format      => 'json',
        env         => 'store://datatables.org/alltableswithkeys',
        jsonCompat  => 'new',
    );

    my $uri = URI->new($self->uri);
    $uri->query_form(%params);

    if ($query =~ m/^insert/i) {
        return $self->_req(POST $uri);
    } else {
        return $self->_req(GET $uri);
    }
}

sub _req {
    my ($self, $req) = @_;
    $self->_log_request($req);
    my $res = $self->ua->request($req);
    $self->_log_response($res);
    my $retries = $self->retries;
    while ($res->code =~ /^5/x and $retries--) {
        sleep 1;
        $res = $self->ua->request($req);
    }
    return $self->json->decode($res->content)
        if $res->code =~ /^4/x;
    return $res->content ?
        $self->json->decode($res->content)->{query}{results} : 1;
}

sub _log_content {
    my ($self, $content) = @_;
    if (length $content) {
        try {
            $content = $self->json->encode($self->json->decode($content));
            $log->trace($content);
        } catch {
            $log->error('Invalid JSON: ' . $content);
        };
    }
}

sub _log_request {
    my ($self, $req) = @_;
    $log->trace($req->method . ' => ' . $req->uri);
    _log_content $req->content;
}

sub _log_response {
    my ($self, $res) = @_;
    $log->trace($res->status_line);
    my $content = $res->content;
    _log_content $res->content;
}


# ABSTRACT: YQL Perl interface for the Y! Query API


1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Business::YQL - YQL Perl interface for the Y! Query API

=head1 VERSION

version 0.0002

=head1 SYNOPSIS

    use Business::YQL;

    my $yql = Business::YQL->new;
    my $data = $yql->q('show tables');

    $data = $yql->query("insert into yahoo.y.ahoo.it (url) values ('http://google.com')");

=head1 DESCRIPTION

This module provides a simple Perl interface (via JSON) to perform YQL queries.  You can test queries in Yahoo's developer console here: L<http://developer.yahoo.com/yql/console|http://developer.yahoo.com/yql/console>.

=head1 METHODS

=head2 new

Instantiates a new Business::YQL client object.  All parameters are optional.

    my $yql = Business::YQL->new(
        scheme => 'http',
        domain => 'query.yahooapis.com',
        version => 'v1',
        timeout => 10,
        retries => 3,
    );

B<Parameters>

=over 4

=item - C<scheme>

I<Optional>E<10> E<8>

The valid HTTP scheme for the URI builder.  Defaults to C<http>.

=item - C<domain>

I<Optional>E<10> E<8>

The Yahoo API top-level domain to make API calls against.  Defaults to L<query.yahooapis.com|http:/query.yahooapis.com>.

=item - C<version>

I<Optional>E<10> E<8>

The Yahoo API version to use.  Defaults to C<v1>.

=item - C<timeout>

I<Optional>E<10> E<8>

The maximum number of seconds to wait after submitting an HTTP request before timing out the response.  Defaults to C<10> seconds.

=item - C<retries>

I<Optional>E<10> E<8>

The number of times to retry requests in cases when Yahoo returns a 5xx response.  Defaults to C<3> attempts.

=back

=head2 q

Submits the YQL query, this method simply takes a string to send to Y! API, and returns the decoded JSON response n the form of a Perl object if the request was valid.

    q("SELECT * from geo.places WHERE text='SFO'")

=head1 AUTHOR

Ali Anari <ali@anari.me>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2014 by Ali Anari.

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
