package App::CPANSQLiteUtils;

our $DATE = '2015-01-10'; # DATE
our $VERSION = '0.01'; # VERSION

use 5.010001;
use strict;
use warnings;
use Log::Any '$log';

use Exporter;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(
                       list_local_cpan_packages
                       list_local_cpan_modules
                       list_local_cpan_dists
                       list_local_cpan_authors
               );
# list_local_cpan_deps
# list_local_cpan_revdeps

sub _connect_db {
    require DBI;

    my %args = @_;

    my $cpan    = $args{cpan};
    my $db_dir  = $args{db_dir} // $cpan;
    my $db_name = $args{db_name} // 'cpandb.sql';

    my $db_path = "$db_dir/$db_name";
    $log->tracef("Connecting to SQLite database at %s ...", $db_path);
    DBI->connect("dbi:SQLite:dbname=$db_path", undef, undef,
                 {RaiseError=>1});
}

our %SPEC;

$SPEC{':package'} = {
    v => 1.1,
    summary => 'Some utilities that query local CPAN::SQLite database',
};

# XXX actually we only need the database
my %common_args = (
    cpan => {
        summary => 'Path to your local CPAN directory',
        schema  => 'str*',
        description => <<'_',

The CPAN home directory must contain `cpandb.sql`.

_
    },
);

my %query_args = (
    query => {
        summary => 'Search query',
        schema => 'str*',
        cmdline_aliases => {q=>{}},
        pos => 0,
    },
    detail => {
        schema => 'bool',
    },
);

$SPEC{list_local_cpan_authors} = {
    v => 1.1,
    summary => 'List authors in local CPAN::SQLite database',
    args => {
        %common_args,
        %query_args,
    },
    result_naked => 1,
    result => {
        description => <<'_',

By default will return an array of CPAN ID's. If you set `detail` to true, will
return array of records.

_
    },
    examples => [
        {
            summary => 'List all authors',
            argv    => [],
            test    => 0,
        },
        {
            summary => 'Find CPAN IDs which start with something',
            argv    => ['--cpan', '/cpan', 'MICHAEL%'],
            result  => ['MICHAEL', 'MICHAELW'],
            test    => 0,
        },
    ],
};
# XXX filter cpanid
sub list_local_cpan_authors {
    my %args = @_;

    my $detail = $args{detail};
    my $q = $args{query} // ''; # sqlite is case-insensitive by default, yay
    $q = '%'.$q.'%' unless $q =~ /%/;

    my $dbh = _connect_db(%args);

    my @bind;
    my @where;
    if (length($q)) {
        push @where, "(cpanid LIKE ? OR fullname LIKE ? OR email like ?)";
        push @bind, $q, $q, $q;
    }
    my $sql = "SELECT
  cpanid id,
  fullname name,
  email
FROM auths".
        (@where ? " WHERE ".join(" AND ", @where) : "").
            " ORDER BY id";

    my @res;
    my $sth = $dbh->prepare($sql);
    $sth->execute(@bind);
    while (my $row = $sth->fetchrow_hashref) {
        push @res, $detail ? $row : $row->{id};
    }
    \@res;
}

$SPEC{list_local_cpan_packages} = {
    v => 1.1,
    summary => 'List packages in locale CPAN::SQLite database',
    args => {
        %common_args,
        %query_args,
        author => {
            summary => 'Filter by author',
            schema => 'str*',
            cmdline_aliases => {a=>{}},
        },
        dist => {
            summary => 'Filter by distribution',
            schema => 'str*',
            cmdline_aliases => {d=>{}},
        },
    },
    result_naked => 1,
    result => {
        description => <<'_',

By default will return an array of package names. If you set `detail` to true,
will return array of records.

_
    },
};
sub list_local_cpan_packages {
    my %args = @_;

    my $detail = $args{detail};
    my $q = $args{query} // ''; # sqlite is case-insensitive by default, yay
    $q = '%'.$q.'%' unless $q =~ /%/;

    my $dbh = _connect_db(%args);

    my @bind;
    my @where;
    if (length($q)) {
        push @where, "(mod_name LIKE ?)";
        push @bind, $q;
    }
    if ($args{author}) {
        #push @where, "(dist_id IN (SELECT dist_id FROM dists WHERE auth_id IN (SELECT auth_id FROM auths WHERE cpanid=?)))";
        push @where, "(author=?)";
        push @bind, $args{author};
    }
    if ($args{dist}) {
        #push @where, "(dist_id=(SELECT dist_id FROM dists WHERE dist_name=?))";
        push @where, "(dist=?)";
        push @bind, $args{dist};
    }
    my $sql = "SELECT
  mod_name name,
  mod_vers version,
  (SELECT dist_name FROM dists WHERE dist_id=mods.dist_id) dist,
  (SELECT cpanid FROM auths WHERE auth_id=(SELECT auth_id FROM dists WHERE dist_id=mods.dist_id)) author
FROM mods".
        (@where ? " WHERE ".join(" AND ", @where) : "").
            " ORDER BY name";

    my @res;
    my $sth = $dbh->prepare($sql);
    $sth->execute(@bind);
    while (my $row = $sth->fetchrow_hashref) {
        push @res, $detail ? $row : $row->{name};
    }
    \@res;
}

$SPEC{list_local_cpan_modules} = $SPEC{list_local_cpan_packages};
sub list_local_cpan_modules {
    goto &list_local_cpan_packages;
}

$SPEC{list_local_cpan_dists} = {
    v => 1.1,
    summary => 'List distributions in local CPAN::SQLite database',
    args => {
        %common_args,
        %query_args,
        author => {
            summary => 'Filter by author',
            schema => 'str*',
            cmdline_aliases => {a=>{}},
        },
    },
    result_naked => 1,
    result => {
        description => <<'_',

By default will return an array of distribution names. If you set `detail` to
true, will return array of records.

_
    },
    examples => [
        {
            summary => 'List all distributions',
            argv    => ['--cpan', '/cpan'],
            test    => 0,
        },
        {
            summary => 'Grep by distribution name, return detailed record',
            argv    => ['--cpan', '/cpan', 'data-table'],
            test    => 0,
        },
        {
            summary   => 'Filter by author, return JSON',
            src       => 'list-local-cpan-dists --cpan /cpan --author perlancar --json',
            src_plang => 'bash',
            test      => 0,
        },
    ],
};
sub list_local_cpan_dists {
    my %args = @_;

    my $detail = $args{detail};
    my $q = $args{query} // '';
    $q = '%'.$q.'%' unless $q =~ /%/;

    my $dbh = _connect_db(%args);

    my @bind;
    my @where;
    if (length($q)) {
        push @where, "(name LIKE ?)";
        push @bind, $q;
    }
    if ($args{author}) {
        #push @where, "(dist_id IN (SELECT dist_id FROM dists WHERE auth_id IN (SELECT auth_id FROM auths WHERE cpanid=?)))";
        push @where, "(author=?)";
        push @bind, $args{author};
    }
    my $sql = "SELECT
  dist_name name,
  dist_vers version,
  dist_file file,
  (SELECT cpanid FROM auths WHERE auth_id=dists.auth_id) author
FROM dists".
        (@where ? " WHERE ".join(" AND ", @where) : "").
            " ORDER BY name";

    my @res;
    my $sth = $dbh->prepare($sql);
    $sth->execute(@bind);
    while (my $row = $sth->fetchrow_hashref) {
        push @res, $detail ? $row : $row->{name};
    }
    \@res;
}

1;
# ABSTRACT: Some utilities that query local CPAN::SQLite database

__END__

=pod

=encoding UTF-8

=head1 NAME

App::CPANSQLiteUtils - Some utilities that query local CPAN::SQLite database

=head1 VERSION

This document describes version 0.01 of App::CPANSQLiteUtils (from Perl distribution App-CPANSQLiteUtils), released on 2015-01-10.

=head1 SYNOPSIS

See the CLI scripts.

=head1 FUNCTIONS


=head2 list_local_cpan_authors(%args) -> any

List authors in local CPAN::SQLite database.

Examples:

 list_local_cpan_authors();


List all authors.


 list_local_cpan_authors( cpan => "/cpan", query => "MICHAEL%"); # -> ["MICHAEL", "MICHAELW"]


Find CPAN IDs which start with something.


Arguments ('*' denotes required arguments):

=over 4

=item * B<cpan> => I<str>

Path to your local CPAN directory.

The CPAN home directory must contain C<cpandb.sql>.

=item * B<detail> => I<bool>

=item * B<query> => I<str>

Search query.

=back

Return value:  (any)


By default will return an array of CPAN ID's. If you set C<detail> to true, will
return array of records.


=head2 list_local_cpan_dists(%args) -> any

List distributions in local CPAN::SQLite database.

Examples:

 list_local_cpan_dists( cpan => "/cpan");


List all distributions.


 list_local_cpan_dists( cpan => "/cpan", query => "data-table");


Grep by distribution name, return detailed record.


 list_local_cpan_dists();


Filter by author, return JSON.


Arguments ('*' denotes required arguments):

=over 4

=item * B<author> => I<str>

Filter by author.

=item * B<cpan> => I<str>

Path to your local CPAN directory.

The CPAN home directory must contain C<cpandb.sql>.

=item * B<detail> => I<bool>

=item * B<query> => I<str>

Search query.

=back

Return value:  (any)


By default will return an array of distribution names. If you set C<detail> to
true, will return array of records.


=head2 list_local_cpan_modules(%args) -> any

List packages in locale CPAN::SQLite database.

Arguments ('*' denotes required arguments):

=over 4

=item * B<author> => I<str>

Filter by author.

=item * B<cpan> => I<str>

Path to your local CPAN directory.

The CPAN home directory must contain C<cpandb.sql>.

=item * B<detail> => I<bool>

=item * B<dist> => I<str>

Filter by distribution.

=item * B<query> => I<str>

Search query.

=back

Return value:  (any)


By default will return an array of package names. If you set C<detail> to true,
will return array of records.


=head2 list_local_cpan_packages(%args) -> any

List packages in locale CPAN::SQLite database.

Arguments ('*' denotes required arguments):

=over 4

=item * B<author> => I<str>

Filter by author.

=item * B<cpan> => I<str>

Path to your local CPAN directory.

The CPAN home directory must contain C<cpandb.sql>.

=item * B<detail> => I<bool>

=item * B<dist> => I<str>

Filter by distribution.

=item * B<query> => I<str>

Search query.

=back

Return value:  (any)


By default will return an array of package names. If you set C<detail> to true,
will return array of records.

=head1 SEE ALSO

L<CPAN::SQLite> (with its front-end CLI L<cpandb>) and C<CPAN::SQLite::CPANMeta>
(with its front-end CLI C<cpandb-cpanmeta>) which generates the index database
of your local CPAN mirror.

=head1 HOMEPAGE

Please visit the project's homepage at L<https://metacpan.org/release/App-CPANSQLiteUtils>.

=head1 SOURCE

Source repository is at L<https://github.com/perlancar/perl-App-CPANSQLiteUtils>.

=head1 BUGS

Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=App-CPANSQLiteUtils>

When submitting a bug or request, please include a test-file or a
patch to an existing test-file that illustrates the bug or desired
feature.

=head1 AUTHOR

perlancar <perlancar@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by perlancar@cpan.org.

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
