#!/usr/bin/perl
use strict;
use warnings;
close DATA;

use Getopt::Long qw(:config gnu_getopt);
use Pod::Coverage::TrustMe ();
use Cwd ();
use File::Find ();
use Pod::Usage;

my %options;
GetOptions(
  'parents!'          => \$options{trust_parents},
  'roles!'            => \$options{trust_roles},
  'pod!'              => \$options{trust_pod},
  'export!'           => \$options{export_only},
  'ignore-imported!'  => \$options{ignore_imported},
  'trust=s@'          => \$options{trust_methods},
  'private=s@'        => \$options{also_private},
  'default-private!'  => sub {
    $options{private} = $_[1] ? undef : [];
  },
  'help'              => sub { pod2usage(-verbose => 2, -exitval => 0) },
) or pod2usage("Invalid options!");

delete $options{$_}
  for grep !defined $options{$_}, keys %options;

my $package_declare_re = qr{
  \A\s*
  package \s+
  ($Pod::Coverage::TrustMe::PACKAGE_RE)\b
  (?: \s+ v?[0-9.]+ )?
  \s*
  (?: ; | \{ | \z )
}x;

my @files;
for my $arg (@ARGV) {
  if (-d $arg) {
    File::Find::find({
      no_chdir => 1,
      wanted => sub {
        my $file = $File::Find::name;
        if (-f $file && $file =~ /\.pm/) {
          push @files, $file;
        }
      },
    }, $arg);
  }
  else {
    push @files, $arg;
  }
}

my $covered = 0;
my $uncovered = 0;

for my $file (@files) {
  my %args = %options;
  local @INC = @INC;
  if (-e $file) {
    $args{pod_from} = $file;

    open my $fh, '<', $file
      or die "Can't read $file: $!\n";
    my @packages;
    my $in_pod = 0;
    while (<$fh>) {
      if (/\A__(?:DATA|END)__\b/) {
        last;
      }
      elsif (/\A=(\w+)/) {
        if (!$in_pod) {
          $in_pod = 1;
        }
        elsif ($1 eq 'cut') {
          $in_pod = 0;
        }
      }
      elsif ($in_pod) { }
      elsif (/$package_declare_re/) {
        push @packages, $1;
      }
    }
    close $fh;

    die "Unable to find any package declarations in $file!\n"
      if !@packages;

    my $full_file = Cwd::abs_path($file);

    my @found =
      map {
        (my $fragment = $_) =~ s{::}{.}g;
        $full_file =~ /\A(.*)\b$fragment\.pm\z/ ? [ $_ => $1 ] : ();
      }
      @packages;

    die "Unable to find any package declarations matching file name in $file!\n"
      if !@found;

    (my $package, my $inc) = @{$found[0]};
    $inc = Cwd::abs_path($inc);

    warn "Found multiple possible packages for $file. Guessing $package."
      if @found > 1;

    if (!grep $inc eq Cwd::abs_path($_), @INC) {
      warn "Adding $inc to \@INC for $file\n";
      unshift @INC, $inc;
    }
    $args{package} = $package;
  }
  elsif ($file =~ $Pod::Coverage::TrustMe::PACKAGE_RE) {
    $args{package} = $file;
  }
  else {
    die "$file does not appear to be a file or a package!\n";
  }

  my $cover = Pod::Coverage::TrustMe->new(%args);
  $cover->print_report;
  print "\n";

  $covered += $cover->covered || 0;
  $uncovered += $cover->uncovered || 0;
}

my $total = $covered + $uncovered;
my $average
  = $total == 0 ? 'unknown'
                : sprintf '%0.2f', $covered / $total * 100;

print "Summary:\n";
print "  subroutines total     : $total\n";
print "  subroutines covered   : $covered\n";
print "  subroutines uncovered : $uncovered\n";
print "  total coverage        : $average%\n\n";

__END__

=head1 NAME

pod-cover-trustme - Report Pod coverage for modules

=head1 SYNOPSIS

  $ pod-cover-trustme lib/My/Module.pm

=head1 OPTIONS

=head2 --no-parents

Disable considering parent class's pod.

=head2 --no-roles

Disable considering composed roles' pod.

=head2 --no-pod

Disable trusting '=for Pod::Coverage' annotations in pod.

=head2 --export

Require documentation only for exported subs.

=head2 --no-ignore-imported

Disable considering imported subs private.

=head2 --trust=<method>

Consider the given method as trusted and not needing documentation.

=head2 --private=<sub>

Consider the given sub name private, and not needing documentation.

=head2 --no-default-private

Disable the default list of private subs.

=head2 --help

Display documentation for the pod-cover-trustme program.
