#!/usr/bin/env perl
# command tool like aws s3
use strict;
use warnings;
use FindBin;
use lib $FindBin::Bin . '/../lib';
use Amazon::S3::Thin;
use Config::Tiny;

S3::CLI->new->run(@ARGV);


package S3::CLI;
use strict;
use warnings;
use Getopt::Long;
use Amazon::S3::Thin;
use Data::Dumper;

sub new {
    return bless {}, shift;
}

sub help {
    my ($self, @args) = @_;
    require Pod::Usage;
    Pod::Usage::pod2usage(0);
}

sub run {
    my ($self, @args) = @_;

    our $VERSION = "0.20";

    my $p = Getopt::Long::Parser->new(
        config => [qw(posix_default no_ignore_case bundling)],
        );

    $p->getoptionsfromarray(
        \@args,
        "p|profile=s"   => \(my $profile = "default"),
        "h|help"        => \(my $help),
        "version"     => \(my $version),
        "v|verbose"     => \($self->{verbose}),
    );
    if ($version) {
        printf "s3 %s\n", $VERSION;
        exit 0;
    }
    if ($help) {
        $self->help();
        exit 0;
    }

    # https://docs.aws.amazon.com/cli/latest/userguide/cli-config-files.html
    my $cred_file = $ENV{HOME} . "/.aws/credentials";
    my $config_file = $ENV{HOME} . "/.aws/config";
    my $crd = Config::Tiny->read($cred_file)->{$profile};
    my $config = Config::Tiny->read($config_file)->{$profile};
    my $opt = +{
        aws_access_key_id => $crd->{aws_access_key_id},
        aws_secret_access_key => $crd->{aws_secret_access_key},
        region => $config->{region},
    };
    $self->{thin_client} = Amazon::S3::Thin->new($opt);
    my $subcmd = shift @args;
    if (!$subcmd) {
        $self->help();
        exit 0;
    }
    #warn Dumper $subcmd, $profile , \@args;    n
    if ($subcmd eq "ls") {
        $self->cmd_ls(@args);
    } elsif ($subcmd eq "rm") {
        $self->cmd_rm(@args);
    } elsif ($subcmd eq "cp") {
        $self->cmd_cp(@args);
    } elsif ($subcmd eq "rb") {
        $self->cmd_rb(@args);
    } else {        
        die "invalid sub command";
    }

}

use XML::TreePP;
use JSON;

# aws s3 ls s3://yourbucket/dir

sub _parse_s3_uri {
    my ($self, $uri) = @_;
    my ($bucket, $key);
    $key = "";
    if($uri =~ m|s3://([^/]+)/(.+)$| ){
        ($bucket, $key) = ($1, $2);
        #warn "bucket, key = %s, %s\n", $bucket , $key;
    } elsif ($uri =~ m|s3://([^/]+)/?$| ){
        $bucket = $1;
        #warn "bucket only = %s\n", $bucket;
    } else {
        die "bad url";
    }
}
sub cmd_ls {
    my ($self, $uri) = @_;
    my ($bucket, $key) = $self->_parse_s3_uri($uri);
    $key = '' unless defined $key;
    my $response = $self->{thin_client}->list_objects($bucket,{
        prefix => $key,
        delimiter => "/",
    });

    my $tpp = XML::TreePP->new();
    my $tree = $tpp->parse($response->content);

    if ($response->code() >= 400) {
        die "ERROR\n---\n" . $response->content . "\n---";
    }
    
    if ($self->{verbose}) {
        use JSON;
        print JSON->new->pretty->encode($tree->{ListBucketResult});
    } else {
        my $common_prefixes = $tree->{ListBucketResult}->{CommonPrefixes};

        if (ref $common_prefixes eq "HASH") {
            $common_prefixes = [$common_prefixes];
        }

        print $_->{Prefix}, "\n" for  @$common_prefixes;

        my $contents = $tree->{ListBucketResult}->{Contents};
        if (ref $contents eq "HASH") {
            $contents = [$contents];
        }

        print $_->{Key} , "\n" for @$contents;
    }
}

sub cmd_cp {
    my ($self, $uri) = @_;
    my ($bucket, $key) = $self->_parse_s3_uri($uri);
    my $response = $self->{thin_client}->get_object($bucket, $key);
    print $response->content();
}

sub cmd_rm {
    my ($self, $uri) = @_;
    my ($bucket, $key) = $self->_parse_s3_uri($uri);
    my $response = $self->{thin_client}->delete_object($bucket, $key);
    warn $response->code();
}

sub cmd_rb {
    my ($self, $uri) = @_;
    my ($bucket, $key) = $self->_parse_s3_uri($uri);
    my $response = $self->{thin_client}->delete_bucket($bucket);
    print $response->code();
}
