package Dist::Zilla::Plugin::PERLANCAR::AddDeps;

our $DATE = '2015-05-15'; # DATE
our $VERSION = '0.06'; # VERSION

use 5.010001;
use strict;
use warnings;

use Moose;
with (
    'Dist::Zilla::Role::FileGatherer',
    'Dist::Zilla::Role::MetaProvider',
);

use App::lcpan::Call qw(call_lcpan_script);
use File::Slurper qw(read_binary);
use Module::Path::More qw(module_path);

has module => (is=>'rw');
has include_author => (is=>'rw');

sub mvp_multivalue_args { qw(include_author module) }

use namespace::autoclean;

sub gather_files {
    use experimental 'smartmatch';

    my $self = shift;

    my $modules = $self->module or return;

    my $res = call_lcpan_script(argv => ['deps', '-R', @$modules]);

    my %add_mods = (map {$_=>0} @$modules); # to be added in our dist
    my %dep_mods; # to stay as deps
    for my $rec (@$res) {
        my $add = 0;
        my $mod = $rec->{module};
        $mod =~ s/\A\s+//;

        # decide whether we should add this module or not
      DECIDE:
        {
            if ($self->include_author && @{ $self->include_author }) {
                last DECIDE unless $rec->{author} ~~ @{ $self->include_author };
            }
            $add = 1;
        }

        if ($add) {
            $add_mods{$mod} = $rec->{version};
        } else {
            $dep_mods{$mod} = $rec->{version};
        }
    }

    $self->log_debug(["modules to add into dist: %s", \%add_mods]);
    $self->log_debug(["modules to add as deps: %s", \%dep_mods]);

    my $meta_no_index = {};

    $res = call_lcpan_script(argv=>["mods-from-same-dist", keys %add_mods]);
    for my $mod (@{ $res }) {
        my $path = module_path(module => $mod);
        $self->log_fatal(["Can't find path for module %s, make sure the module is installed", $mod])
            unless $path;

        my $mod_pm = $mod;
        $mod_pm =~ s!::!/!g;
        $mod_pm .= ".pm";

        my $ct = read_binary($path);

      MUNGE:
        {
            # adjust dist name
            $ct =~ s/^(=head1 VERSION\s+[^\n]+from Perl distribution )[\w-]+(?: version [^)\s]+)*/
                $1 . $self->zilla->name . " version " . $self->zilla->version/ems;
        }

        my $file_path = "lib/$mod_pm";
        my $file = Dist::Zilla::File::InMemory->new(
            name    => $file_path,
            content => $ct,
        );
        push @{ $meta_no_index->{file} }, $file_path;

        $self->add_file($file);
    }
    $self->{_meta_no_index} = $meta_no_index;

    for my $mod (keys %dep_mods) {
        $self->zilla->register_prereqs($mod => $dep_mods{$mod});
    }
}

sub metadata {
    my $self = shift;

    { no_index => $self->{_meta_no_index} };
}

__PACKAGE__->meta->make_immutable;
1;
# ABSTRACT: Include dependencies into dist

__END__

=pod

=encoding UTF-8

=head1 NAME

Dist::Zilla::Plugin::PERLANCAR::AddDeps - Include dependencies into dist

=head1 VERSION

This document describes version 0.06 of Dist::Zilla::Plugin::PERLANCAR::AddDeps (from Perl distribution Dist-Zilla-Plugin-PERLANCAR-AddDeps), released on 2015-05-15.

=head1 SYNOPSIS

In C<dist.ini>:

 name=Perinci-CmdLine-Any-Bundled
 version=0.01

 [PERLANCAR::AddDeps]
 include_author = PERLANCAR
 module = Perinci::CmdLine::Any

=head1 DESCRIPTION

B<WARNING: EXPERIMENTAL>

This plugin will add module files along with their recursive dependencies into
your dist during building. When done carefully, this can reduce the number of
dists that users need to download and install because they are already included
in your dists.

=head2 How it works

1. Perform "lcpan deps -R" against all the modules specified in C<module>
configuration. This basically queries your local CPAN index and ask for the
recursive dependencies of the modules. You can filter this using
C<include_author> to include only dependencies written by a certain author (for
example, yourself). The result is a list of modules to add into dist (#1a) and a
list of modules to stay as deps (#1b).

2. Perform "lcpan mods-from-same-dist" for all modules found in #1a. The result
is all modules from all dependency distributions.

3. Search all the module files found in #2 in your local installation and
include them to Dist::Zilla for building. Some minor modifications will be done
first:

=over

=item *

If the POD indicates which dist the module is in, will replace it with our dist.
For example if there is a VERSION section with this content:

 This document describes version 0.10 of Perinci::CmdLine::Any (from Perl
 distribution Perinci-CmdLine-Any), released on 2015-04-12.

then the text will be replaced with:

 This document describes version 0.10 of Perinci::CmdLine::Any (from Perl
 distribution Perinci-CmdLine-Any-Bundled version 0.01), released on 2015-04-12.

=back

4. Add all files in #3 into no_index metadata, so they don't clash with the
original dists.

5. Add modules in #1b as dependencies.

=head2 Caveats

=over

=item *

"lcpan" is used to list dependencies and contents of dists. You should have
"lcpan" installed and your local CPAN fairly recent (keep it up-to-date with
"lcpan update").

=item *

Only modules from each dependency distribution are included. This means other
stuffs are not included: scripts/binaries, shared files, PODs. This is because
PAUSE currently only index packages (~ modules). We have C<.packlist> though,
and can use it in the future when needed.

=item *

Your bundle dist (the one you're building which include the deps) should be
built with a minimal set of Dist::Zilla plugins. It should not do POD weaving,
or change/fill version numbers (e.g. OurVersion which looks for C<# VERSION> and
change it), etc. We want the included dependency module files to be as pristine
as possible.

=item *

Currently all the dependency dists must be installed on your local Perl
installation. (This is purely out of my coding laziness though. It could/should
be extracted from the release file in local CPAN index though.)

=back

=for Pod::Coverage .+

=head1 SEE ALSO

L<lcpan>

=head1 HOMEPAGE

Please visit the project's homepage at L<https://metacpan.org/release/Dist-Zilla-Plugin-PERLANCAR-AddDeps>.

=head1 SOURCE

Source repository is at L<https://github.com/perlancar/perl-Dist-Zilla-Plugin-PERLANCAR-AddDeps>.

=head1 BUGS

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

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
