package Dezi::Admin::API::Stats;
use strict;
use warnings;
use Carp;
use base qw( Plack::Component );
use Data::Dump qw( dump );
use Plack::Util::Accessor qw(
    debug
    conn
    table_name
    searcher
);
use JSON;
use Plack::Middleware::REST::Util;
use Dezi::Admin::Utils;
use Dezi::Admin::API::Response;
use Try::Tiny;

our $VERSION = '0.006';

our @FIELDS = (
    {   name => 'id',
        type => 'int',
    },
    {   name       => 'tstamp',
        type       => 'date',
        dateFormat => 'timestamp',
    },
    {   name => 'q',
        type => 'string',
    },
    {   name => 'build_time',
        type => 'float',
    },
    {   name => 'remote_user',
        type => 'string',
    },
    {   name => 'search_time',
        type => 'float',
    },
    { name => 'path',  type => 'string', },
    { name => 'total', type => 'int' },
    { name => 's',     type => 'string' },
    { name => 'o',     type => 'int' },
    { name => 'p',     type => 'int' },
    { name => 'h',     type => 'boolean' },
    { name => 'c',     type => 'boolean' },
    { name => 'L',     type => 'string' },
    { name => 'f',     type => 'boolean' },
    { name => 'r',     type => 'boolean' },
    { name => 't',     type => 'string' },
    { name => 'b',     type => 'string' },
);

sub get_list {
    my ( $self, $req ) = @_;
    my $list  = [];
    my $total = 0;
    my %sql   = Dezi::Admin::Utils::params_to_sql( $req, $self->table_name,
        [ 'q', 'remote_user', 'path' ] );

    #dump \%sql;

    $self->conn->run(
        sub {
            my $dbh = $_;
            my $sth = $dbh->prepare( $sql{sql} );
            $sql{args} ? $sth->execute( @{ $sql{args} } ) : $sth->execute();
            while ( my $row = $sth->fetchrow_hashref ) {
                push @$list, $row;
            }
            $sth = $dbh->prepare( $sql{count} );
            $sql{args} ? $sth->execute( @{ $sql{args} } ) : $sth->execute();
            $total = $sth->fetch->[0];
        }
    );

    my $resp = Dezi::Admin::API::Response->new(
        total   => $total,
        results => $list,

    );
    $resp->metaData->{fields}   = [@FIELDS];
    $resp->metaData->{sortInfo} = {
        direction => $sql{direction},
        field     => $sql{sort},
    };
    $resp->metaData->{limit} = $sql{limit};
    $resp->metaData->{start} = $sql{offset};

    return $resp;
}

sub get_stat {
    my ( $self, $req ) = @_;
    my $id = request_id( $req->env );

    # TODO

    return Dezi::Admin::API::Response->new();
}

sub get_terms {
    my ( $self, $req ) = @_;

    my %all_terms;
    my $total = 0;
    my %sql   = Dezi::Admin::Utils::params_to_sql( $req, $self->table_name,
        [ 'q', 'tstamp', ] );

    #dump \%sql;

    my $query_parser = $self->searcher->engine->searcher->qp;

    $self->conn->run(
        sub {
            my $dbh = $_;
            my $sth = $dbh->prepare( $sql{sql} );
            $sql{args} ? $sth->execute( @{ $sql{args} } ) : $sth->execute();
            while ( my $row = $sth->fetchrow_hashref ) {
                my $q = $row->{q} or next;
                my $err;
                my $query = try {
                    $query_parser->parse($q);
                }
                catch {
                    $err = $_;
                    return;
                };
                if ( !$err and $query ) {
                    $query->walk(
                        sub {
                            my ( $clause, $dialect, $code, $prefix ) = @_;
                            if ( $clause->is_tree ) {
                                $clause->value->walk($code);
                            }
                            else {
                                my $value
                                    = ref( $clause->value )
                                    ? sprintf( '(%s..%s)',
                                    @{ $clause->value } )
                                    : $clause->value;
                                $all_terms{$value}->{count}++;
                                $all_terms{$value}->{recent}
                                    ||= $row->{tstamp};
                                $all_terms{$value}->{recent} = $row->{tstamp}
                                    if $row->{tstamp}
                                    > $all_terms{$value}->{recent};
                            }
                        }
                    );
                }
            }
        }
    );

    my $list = [];
    for my $term (
        sort { $all_terms{$b}->{count} <=> $all_terms{$a}->{count} }
        keys %all_terms
        )
    {
        push @$list,
            {
            term   => $term,
            count  => $all_terms{$term}->{count},
            recent => $all_terms{$term}->{recent},
            };
    }
    $total = scalar @$list;

    my $resp = Dezi::Admin::API::Response->new(
        total   => $total,
        results => $list,

    );
    $resp->metaData->{fields} = [
        { name => 'term',  type => 'string', },
        { name => 'count', type => 'int', },
        {   name       => 'recent',
            type       => 'date',
            dateFormat => 'timestamp',
        },
    ];
    $resp->metaData->{sortInfo} = {
        direction => 'DESC',
        field     => 'count',
    };
    $resp->metaData->{limit} = $sql{limit};
    $resp->metaData->{start} = $sql{offset};

    return $resp;

}

my %dispatch = (
    '/'      => 'get_list',
    '/terms' => 'get_terms',
);

sub call {
    my ( $self, $env ) = @_;
    my $req    = Plack::Request->new($env);
    my $resp   = $req->new_response;
    my $path   = $req->path;
    my $method = $dispatch{$path} || undef;
    if ( !$method ) {
        $resp->status(404);
        $resp->body(
            encode_json(
                {   code    => 404,
                    success => 0,
                    error   => 'Resource not found',
                }
            )
        );
    }
    else {
        my $api_resp = $self->$method($req);
        $resp->body("$api_resp");
    }

    $resp->status(200) unless $resp->status;
    $resp->content_type(Dezi::Admin::Utils::json_mime_type)
        unless $resp->content_type;

    return $resp->finalize;
}

1;

__END__

=head1 NAME

Dezi::Admin::API::Stats - Dezi administration API to Dezi::Stats data

=head1 SYNOPSIS

 /api/stats?q=foo&sort=name&dir=asc&limit=10&offset=0

=head1 DESCRIPTION

Dezi::Admin::API::Stats isa L<Plack::Component>.

=head1 METHODS

=head2 get_list

Returns L<Dezi::Admin::API::Response> object representing metadata for
one or more statistics matching GET params.

=head2 get_stat

Returns L<Dezi::Admin::API::Response> object for a single statistic.

=head2 get_terms

Returns L<Dezi::Admin::API::Response> object representing
the top I<N> search terms.

=head2 call( I<env> )

Required method that dispatches request.

=head1 AUTHOR

Peter Karman, C<< <karman at cpan.org> >>

=head1 BUGS

Please report any bugs or feature requests to C<bug-dezi-admin at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Dezi-Admin>.  I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.

=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Dezi::Admin


You can also look for information at:

=over 4

=item * RT: CPAN's request tracker

L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Dezi-Admin>

=item * AnnoCPAN: Annotated CPAN documentation

L<http://annocpan.org/dist/Dezi-Admin>

=item * CPAN Ratings

L<http://cpanratings.perl.org/d/Dezi-Admin>

=item * Search CPAN

L<http://search.cpan.org/dist/Dezi-Admin/>

=back

=head1 COPYRIGHT & LICENSE

Copyright 2013 Peter Karman.

This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.


=cut