#!/opt/local/bin/perl5.34

use strict;
use warnings;

use Cwd;
use YAML qw(LoadFile);
use LWP::UserAgent;
use HTTP::Request::Common;
use IPC::Run qw(start pump finish);
use File::Basename qw(dirname);
use Getopt::Long::Descriptive;

use constant VERSION => '0.2.0';
use constant CONFIG_FILE => '.pearl.yml';
use constant DEFAULT_PORTFILE => 'Portfile';
use constant MACPORTS_SVN => 'http://svn.macports.org/repository/macports';

#
# signal an error and exit
#
sub error
{
    my ($message) = @_;

    print "Error: $message\n";
    exit 1;
}

#
# returns path to local Portfile
#
sub portfile
{
    my ($config) = @_;

    return $config->{portfile}
        ? $config->{portfile}
        : DEFAULT_PORTFILE;
}

#
# returns the project name
#
sub project_name
{
    my ($config) = @_;

    return $config->{project_name}
        ? $config->{project_name}
        : basename(getcwd);
}

#
# returns the project 'type' (the macports port category)
#
sub project_type
{
    my ($config) = @_;
    my $project_type = $config->{project_type};

    if (!$project_type) {
        error('You need to specify "project_type" in ' . CONFIG_FILE);
    }

    return $config->{project_type};
}

#
# returns the url to the projects Portfile
#
sub portfile_url
{
    my ($project_type, $project_name) = @_;
    my $url = MACPORTS_SVN . '/trunk/dports/%s/%s/Portfile';

    return sprintf($url, $project_type, $project_name);
}

#
# Fetch the source for the latest Portfile from Macports SVN
#
sub fetch_portfile
{
    my ($portfile_url) = @_;
    my $ua = LWP::UserAgent->new;
    my $res = $ua->request(GET $portfile_url);
    my $content = $res->decoded_content;

    if ($res->is_error) {
        error($content);
    }

    return $content;
}

#
# Create the diff between our portfile and the latest one
#
sub diff_portfile
{
    my ($local_portfile, $latest_portfile) = @_;
    my @cmd = ('diff', '-u', '-', $local_portfile);
    my $ph = start(\@cmd, \my $proc_in, \my $proc_out);

    $proc_in .= $latest_portfile;

    pump $ph;
    finish $ph;

    return $proc_out;
}

#
# return the filename for the diff output
#
sub diff_filename
{
    my ($project_name) = @_;

    return sprintf('Portfile-%s.diff', $project_name);
}

#
# write content to specified path
#

sub write_to
{
    my ($path, $content) = @_;

    open(my $fh, '>', $path)
        or die("Could not open '$path' for writing");

    print $fh $content;
    close $fh;
}

#
# main
#

my ($opt, $usage) = describe_options(
    "Version " . VERSION . "\nusage: pearl %o",
    ['stdout', 'print diff to stdout instead of file'],
    ['help', 'print usage information'],
    ['version', 'version information'],
    ['sync', 'sync local portfile with Macports (warning: will overwrite it)']
);

print($usage->text), exit if $opt->help;

my $config = LoadFile(CONFIG_FILE);
my $project_name = project_name($config);
my $project_type = project_type($config);
my $local_portfile = portfile($config);
my $portfile_url = portfile_url($project_type, $project_name);
my $latest_portfile = fetch_portfile($portfile_url);

# sync with macports version

if ($opt->sync) {
    write_to($local_portfile, $latest_portfile);
    print "'$local_portfile' synced with Macports!\n";
}

elsif($opt->version) {
    print "Pearl v" . VERSION . "\n";
}

else {
    my $diff = diff_portfile($local_portfile, $latest_portfile);
    if ($opt->stdout) {
        print $diff;
    }
    else {
        my $diff_filename = diff_filename($project_name);
        write_to($diff_filename, $diff);
        print "Diff written to '$diff_filename'\n";
    }
}

