#!/usr/bin/env perl

# Daniel Bowling <dbowling@akamai.com>
# Feb 2020

use strict;
use warnings;
use Getopt::Long qw(:config no_ignore_case);
use Akamai::Edgegrid;
use Mozilla::CA;         # Lest ye return SSL trust errors
use JSON;
use open qw(:std :utf8); # Fix "Wide character in print" warning
#use Data::Dumper;        # For debugging (uncomment as needed)

# Parse options
GetOptions(
    'account-switch-key|a=s' => \my $acctSwKey,
    'contract-id|c=s'        => \my $ctrId,
    'dry-run|D'              => \my $dryRun,
    'delimiter|d=s'          => \my $sep,
    'group-id|g=s'           => \my $grpId,
    'header|H'               => \my $head,
    'help|h'                 => \my $help,
    'property-version|P=i'   => \my $prpVer,
    'property-id|p=s'        => \my $prpId,
    'version|V'              => \my $ver,
    'verbose|v'              => \my $verb
    );

## Let's set some variables... ##

# Set delimiter default if none specified
$sep = "\t" if ! $sep;

# Query strings
my $acctSwKeyQS = "accountSwitchKey=$acctSwKey" if $acctSwKey;
my $ctrIdQS     = "contractId=$ctrId"           if $ctrId;
my $grpIdQS     = "groupId=$grpId"              if $grpId;
my $prpIdQS     = "propertyId=$prpId"           if $prpId;
my $dryRunQS    = "dryRun=false";
   $dryRunQS    = "dryRun=true"                 if $dryRun;

# For request method
my $method;

# For input file
my @input;

## Print some info and exit since this won't require API calls... ##

# Version info option
if ($ver) {
    $ver = '0.09';
    print "$ver\n";
    exit;
}

# Print help if no request method specified
if (! $ARGV[0]) {
    print "Usage: $0 GET|PUT|HEAD|PATCH <OPTIONS> <FILE(S)>\n\n";
    die <<'OPTIONS';
Print or commit delivery configuration JSON rule tree(s) via PAPI.
    -a, --account-switch-key=KEY    Specify account switch key
    -c, --contract-id=ID            Specify contract ID
    -D, --dry-run                   Dry run (make no real changes)
    -d, --delimiter=DELIM           Change delimiting character (default is TAB)
    -g, --group-id=ID               Specify group ID
    -H, --header                    Print header with output
    -h, --help                      Print this help message
    -P, --property-version=NUM      Specify property version
    -p, --property-id=ID            Specify property ID
    -V, --version                   Print version
    -v, --verbose                   Print more verbose output
OPTIONS
} else {
    $method = lc($ARGV[0]) # Make lower-case
}

# Set input file
if ($ARGV[1]) {
    # Declare variables for this bit
    my ($n, $fh, $line);

    # In a loop to handle multiple files
    $n = 1;
    until (! $ARGV[$n]) {
        open($fh, '<:encoding(UTF-8)', $ARGV[$n]) || die;
        while ($line = <$fh>) {
            push(@input, $line)
        }
        ++$n;
    }
} elsif (! -t STDIN) {
    chomp(@input = <STDIN>);
}

## Set some things up for the request... ##

# Set options for request
my $agent = Akamai::Edgegrid->new(
    config_file => "$ENV{HOME}/.edgerc",
    section     => "default",
    max_body    => "131072" # Defaults to 2048 unless specified
    );

# Set base URL for request
my $baseurl = "https://" . $agent->{host};

## Functions defined... ##

# Variables declared for the functions
my ($response, $status, $rules, $body, $headers);

# Define function to get rule tree
sub get_rules {
    # Make request for rule tree
    if ($acctSwKey) {
        $response = $agent->get(
            "$baseurl/papi/v1/properties/$prpId/versions/$prpVer/rules?" .
            "$acctSwKeyQS&$ctrIdQS&$grpIdQS"
            );
    } else {
        $response = $agent->get(
            "$baseurl/papi/v1/properties/$prpId/versions/$prpVer/rules?" .
            "$ctrIdQS&$grpIdQS"
            );
    }

    $status = $response->status_line; # HTTP status code
    $rules  = $response->content;     # Rule tree

    if ($verb) {
        return $rules . "\n" . $status # Verbose output if -v
    } else {
        return $rules                  # Just the rule tree
    }
}

# Define function to head rule tree
sub head_rules {
    # Make request for rule tree
    if ($acctSwKey) {
        $response = $agent->head(
            "$baseurl/papi/v1/properties/$prpId/versions/$prpVer/rules?" .
            "$acctSwKeyQS&$ctrIdQS&$grpIdQS"
            );
    } else {
        $response = $agent->head(
            "$baseurl/papi/v1/properties/$prpId/versions/$prpVer/rules?" .
            "$ctrIdQS&$grpIdQS"
            );
    }

    $headers = $response->headers_as_string;
    return $headers;
}

# Define function to upload rule tree
sub put_rules {
    # Request body
    $body = "@_";

    # Make request for rule tree
    if ($acctSwKey) {
        $response = $agent->put(
            "$baseurl/papi/v1/properties/$prpId/versions/$prpVer/rules?" .
            "$acctSwKeyQS&$ctrIdQS&$grpIdQS&validateRules=true&$dryRunQS",
            "Content-Type" => "application/json",
            "Content"      => $body
            );
    } else {
        $response = $agent->put(
            "$baseurl/papi/v1/properties/$prpId/versions/$prpVer/rules?" .
            "$ctrIdQS&$grpIdQS&validateRules=true&$dryRunQS",
            "Content-Type" => "application/json",
            "Content"      => $body
            );
    }

    $status = $response->status_line; # HTTP status code
    $rules  = $response->content;     # Rule tree

    if ($verb) {
        return $rules . "\n" . $status # Verbose output if -v
    } else {
        return $status                 # Just the status code
    }
}

# Define function to patch rule tree
sub patch_rules {
    # Request body
    $body = @_;

    # Make request for rule tree
    if ($acctSwKey) {
        $response = $agent->patch(
            "$baseurl/papi/v1/properties/$prpId/versions/$prpVer/rules?" .
            "$acctSwKeyQS&$ctrIdQS&$grpIdQS&validateRules=true&$dryRunQS",
            "Content-Type" => "application/json-patch+json",
            "Content"      => $body
            );
    } else {
        $response = $agent->patch(
            "$baseurl/papi/v1/properties/$prpId/versions/$prpVer/rules?" .
            "$ctrIdQS&$grpIdQS&validateRules=true&$dryRunQS",
            "Content-Type" => "application/json-patch+json",
            "Content"      => $body
            );
    }
    
    $status = $response->status_line; # HTTP status code
    $rules  = $response->content;     # Rule tree

    if ($verb) {
        return $rules . "\n" . $status # Verbose output if -v
    } else {
        return $status                 # Just the status code
    }
}

## Begin the information gathering/API calling bits... ##

# Print list of contracts if none specified
if (! $ctrId) {
    # Make request for contracts
    if ($acctSwKey) {
        $response = $agent->get(
            "$baseurl/papi/v1/contracts?$acctSwKeyQS"
            );
    } else {
        $response = $agent->get(
            "$baseurl/papi/v1/contracts"
            );
    }
    my $ctrs = decode_json($response->content); # Parse the returned JSON

    # Print header if -H 
    if ($head) {
        print "contractId";
        print $sep . "contractTypeName" if $verb;
        print "\n";
    }

    # Print contracts
    my $n = 0;
    until (! $ctrs->{contracts}{items}[$n]{contractId}) {
        print $ctrs->{contracts}{items}[$n]{contractId};
        if ($verb) {
            print "$sep" . $ctrs->{contracts}{items}[$n]{contractTypeName}
        }
        print "\n";
        ++$n;
    }

    exit;
}

# Print list of groups if none specified
if (! $grpId) {
    # Make request for groups
    if ($acctSwKey) {
        $response = $agent->get(
            "$baseurl/papi/v1/groups?$acctSwKeyQS"
            );
    } else {
        $response = $agent->get(
            "$baseurl/papi/v1/groups"
            );
    }
    my $grps = decode_json($response->content); # Parse the returned JSON

    # Print header if -H
    if ($head) {
        print "groupId" . $sep . "groupName";
        print $sep . "parentGroupId" if $verb;
        print "\n";
    }

    # Print groups
    my $n = 0;
    until (! $grps->{groups}{items}[$n]{groupId}) {
        print $grps->{groups}{items}[$n]{groupId}   . "$sep" .
              $grps->{groups}{items}[$n]{groupName};
        # Groups may not have parents; print if they do AND verbose flag set
        if ($grps->{groups}{items}[$n]{parentGroupId} && $verb) {
            print $sep . $grps->{groups}{items}[$n]{parentGroupId}
        }
        print "\n";
        ++$n;
    }

    exit;
}

# Print list of properties if none specified
if (! $prpId) {
    # Make request for properties
    if ($acctSwKey) {
        $response = $agent->get(
            "$baseurl/papi/v1/properties?$acctSwKeyQS&$ctrIdQS&$grpIdQS"
            );
    } else {
        $response = $agent->get(
            "$baseurl/papi/v1/properties?$ctrIdQS&$grpIdQS"
            );
    }
    my $prps = decode_json($response->content); # Parse the returned JSON

    # Print header if -H
    if ($head) {
        print "propertyId" . $sep . "propertyName";
        print $sep . "latestVersion" if $verb;
        print "\n";
    }

    # Print properties
    my $n = 0;
    until (! $prps->{properties}{items}[$n]{propertyId}) {
        print $prps->{properties}{items}[$n]{propertyId}   . "$sep" .
              $prps->{properties}{items}[$n]{propertyName};
        if ($verb) {
            print $sep . $prps->{properties}{items}[$n]{latestVersion};
        }
        print "\n";
        ++$n;
    }

    exit;
}

# Print list of property versions if none specified
if (! $prpVer) {
    # Make request for property versions
    if ($acctSwKey) {
        $response = $agent->get(
            "$baseurl/papi/v1/properties/$prpId/versions?$acctSwKeyQS&" .
            "$ctrIdQS&$grpIdQS"
            );
    } else {
        $response = $agent->get(
            "$baseurl/papi/v1/properties/$prpId/versions?$ctrIdQS&$grpIdQS"
            );
    }
    my $versions = decode_json($response->content); # Parse the returned JSON

    # Print header if -H
    if ($head) {
        print "propertyVersion" . $sep . "updatedDate" . $sep. "updatedByUser";
        print $sep . "note" if $verb;
        print "\n";
    }

    # Print property versions
    my $n = 0;
    until (! $versions->{versions}{items}[$n]{propertyVersion}) {
        print $versions->{versions}{items}[$n]{propertyVersion} . "$sep" .
              $versions->{versions}{items}[$n]{updatedDate}     . "$sep" .
              $versions->{versions}{items}[$n]{updatedByUser};
        # Notes don't always exist; print if they do AND verbose flag set
        if ($versions->{versions}{items}[$n]{note} && $verb) {
            print $sep . $versions->{versions}{items}[$n]{note}
        }
        print "\n";
        ++$n;
    }

    exit;
}

## If we've made it this far then we have enough info to dig in... ##

# Begin request for rule tree
print get_rules()                if $method eq "get";
print head_rules()               if $method eq "head";
print put_rules(@input)   . "\n" if $method eq "put";
print patch_rules(@input) . "\n" if $method eq "patch";
