#!/usr/bin/env perl
use strict;
use warnings;

use File::Spec::Functions qw(updir catfile abs2rel canonpath);
use File::Basename qw(dirname);
use Cwd qw(abs_path);
use POSIX qw(strftime);
use MIME::Base64 qw(decode_base64);
use Digest::SHA qw(sha256_hex);

my $dir = dirname(__FILE__);
my $root = abs2rel(abs_path(catfile($dir, updir)));

my $CACERT_PEM_FILE = canonpath("$root/lib/Mozilla/CA/cacert.pem");

binmode STDOUT, ':utf8';

my $before;
my $after;
my @opts
    = @ARGV == 1 ? split /:/, $ARGV[0]
    : @ARGV == 0 ? 'HEAD'
    : @ARGV;
die "Bad arguments"
    if @opts > 2;

push @opts, undef
    while @opts < 2;

my @content;
for my $spec (@opts) {
    my $fh;
    my $pid;
    if (!defined $spec) {
        open $fh, '<:raw', $CACERT_PEM_FILE
            or die "can't read $CACERT_PEM_FILE: $!\n";
    }
    else {
        my @cmd = (qw(git show), $spec.':'.$CACERT_PEM_FILE);
        $pid = open $fh, '-|', @cmd
            or die "can't run @cmd: $!";
    }

    # possibly a better formet to parse:
    # openssl crl2pkcs7 -nocrl -certfile $file | openssl pkcs7 -print_certs

    my $state = 0;
    my $cert_name;
    my $cert_content;
    my %certs;

    while (my $line = readline $fh) {
        $line =~ s/\r?\n\z//;

        next
            if $state < 3 && $line =~ /\A\s*(?:#.*)?\z/;

        if ($state == 0) {
            $cert_name = $line;
            $cert_name =~ s/\\x([a-fA-F0-9]{2})/chr(hex($1))/ge;
            utf8::decode($cert_name);
            $state++;
        }
        elsif ($state == 1) {
            die "unknown pem content:\n$line"
                unless $line =~ /\A=+\z/;
            $state++;
        }
        elsif ($state == 2) {
            die "unknown pem content:\n$line"
                unless $line =~ /\A-+BEGIN CERTIFICATE-+\z/;
            $cert_content = '';
            $state++;
        }
        elsif ($state == 3) {
            if ($line =~ /\A-+END CERTIFICATE-+\z/) {
                my $sha = sha256_hex(decode_base64($cert_content));
                $certs{$cert_name} = $sha;
                $state = 0;
                next;
            }

            $cert_content .= $line;
        }
    }

    if (!keys %certs) {
        die "can't read cert file $CACERT_PEM_FILE" . (defined $spec ? " for $spec" : "") . "!\n";
    }

    push @content, \%certs;
}

my %added =
    map +($_ => $content[1]->{$_}),
    grep !exists $content[0]->{$_},
    keys %{ $content[1] };

my %removed =
    map +($_ => $content[0]->{$_}),
    grep !exists $content[1]->{$_},
    keys %{ $content[0] };

my %updated =
    map +($_ => [ $content[0]->{$_}, $content[1]->{$_} ]),
    grep exists $content[1]->{$_} && $content[0]->{$_} ne $content[1]->{$_},
    keys %{ $content[0] };

for my $change (
    [ 'Added', \%added ],
    [ 'Updated', \%updated ],
    [ 'Removed', \%removed ],
) {
    my ($label, $certs) = @$change;
    next
        if !%$certs;
    print "  - $label certificates:\n";
    for my $cert (sort keys %$certs) {
        print "    - $cert\n";
        my $sha = $certs->{$cert};
        if (ref $sha) {
            print "      old sha256: $sha->[0]\n";
            print "      new sha256: $sha->[1]\n";
        }
        else {
            print "      sha256: $sha\n";
        }
    }
}
