#!/usr/bin/env perl
use strict;
use warnings;
use 5.020;
use FindBin qw($RealBin);
use lib "$RealBin/../lib"; # source checkout or build tree
use Getopt::Long qw(GetOptions);
use JSON::PP ();
use Perl::CodeMap;

my %options = (root => '.', output => 'perl-code-map.html', parser => 'auto', embed_source => 1);
my $root_set = 0;
GetOptions(
    'root=s' => sub { $options{root} = $_[1]; $root_set = 1; },
    'o|output=s' => \$options{output},
    'json=s' => \$options{json},
    'from-json=s' => \$options{from_json},
    'parser=s' => \$options{parser},
    'embed-source!' => \$options{embed_source},
    'max-source-bytes=i' => \$options{max_source_bytes},
    'include-tests!' => \$options{include_tests},
    'source-base=s' => \$options{source_base},
    'name=s' => \$options{name},
    'version' => \$options{version},
    'help|h' => \$options{help},
) or exit 2;
if ($options{help}) {
    print <<'HELP';
Perl CodeMap - static Perl dependency maps and browsable source

Usage:
  perl-codemap --root DIR --output code-map.html [--json code-map.json]
  perl-codemap --from-json code-map.json [--root ORIGINAL_SOURCE] --output report.html

Options:
  --root DIR                Source directory (default: current directory)
  -o, --output FILE         Standalone HTML output (default: perl-code-map.html)
  --json FILE               Optional machine-readable JSON
  --from-json FILE          Re-render an earlier JSON export
  --parser auto|ppi|lite    Prefer PPI when installed; lite is best effort
  --[no-]embed-source       Embed syntax-highlightable source (default: yes)
  --max-source-bytes N      Per-file embedding limit (default: 2000000)
  --include-tests           Include test files and test directories
  --source-base URL         Repository link prefix for the scanned directory
  --name TITLE              Set report title
  --version                 Print version
  --help                    Print this help

Tip: install PPI for structural parsing: cpanm PPI
Maintainer: Scott Muller <scott@21h.io>
The generated HTML and JSON may contain source code. Keep private projects private.
HELP
    exit 0;
}
if ($options{version}) { print "Perl CodeMap $Perl::CodeMap::VERSION\n"; exit 0; }
my $cm = eval { Perl::CodeMap->new(%options) };
if (!$cm) { print STDERR "perl-codemap: $@"; exit 2; }
my $data;
if ($options{from_json}) {
    eval {
        open my $fh, '<:raw', $options{from_json} or die "Cannot read $options{from_json}: $!\n";
        local $/;
        $data = JSON::PP->new->utf8(1)->decode(<$fh>);
        close $fh;
        die "Expected a CodeMap JSON object with a pkgs array\n"
            unless ref($data) eq 'HASH' && ref($data->{pkgs}) eq 'ARRAY';
        $data->{sources} ||= {};
        if (!$options{embed_source}) {
            $data->{sources} = {};
        } else {
            my $root = $root_set ? $options{root}
                : ((defined $data->{module} && -d $data->{module}) ? $data->{module} : undef);
            $cm->hydrate_sources($data, $root) if defined $root;
        }
        $data->{source} = $options{source_base} if defined $options{source_base};
        $data->{name} = $options{name} if defined $options{name};
        1;
    } or do { print STDERR "perl-codemap: $@"; exit 2; };
} else {
    $data = eval { $cm->analyze };
    if (!$data) { print STDERR "perl-codemap: $@"; exit 2; }
}
if (!@{$data->{pkgs}}) {
    print STDERR "perl-codemap: no Perl packages found under the selected source root\n";
    exit 1;
}
my $ok = eval { $cm->write_report($data, output => $options{output}, json => $options{json}); 1 };
if (!$ok) { print STDERR "perl-codemap: $@"; exit 2; }
my $fn_count = 0;
$fn_count += @{$_->{funcs}} for map { @{$_->{files}} } @{$data->{pkgs}};
print "Report: $options{output}\n";
print 'Packages: ', scalar(@{$data->{pkgs}}), " | Declarations: $fn_count",
    ' | Embedded sources: ', scalar(keys %{$data->{sources} || {}}), "\n";
print STDERR "Warning: lite parser is heuristic; install PPI for full structural extraction.\n"
    if ($data->{parser} // '') eq 'lite';
print STDERR "Warning: $_\n" for @{$data->{warnings} || []};
