#!/usr/bin/env perl

use lib 'auto-lib', 'lib';

use 5.010;
use strict;
use warnings;

use Data::Dumper;
use Paws;
use Moose::Util;
use ARGV::Struct;
use Term::ANSIColor 2.00 qw();
use Hash::Flatten qw//;
use List::Util qw(reduce);
use Module::Find;

use Getopt::Long qw/GetOptionsFromArray/;

my @args = @ARGV;

my $aws = Paws->new;

my $service_name = shift @args;
if (not defined $service_name) {
  say "A list of the services you have available";
  say $_ for (sort $aws->available_services);
  exit 1;  
}

my $service = $aws->class_for_service($service_name);

my $opts = {};
my @opts = (
  'caller=s',
  'region=s',
  'debug',
  'help'
);
#if ($service->meta->find_attribute_by_name('region')) {

GetOptionsFromArray(\@args, $opts, @opts);

my $method_name = shift @args;

if (exists $opts->{caller}) {
  Paws->load_class($opts->{ caller });
  $opts->{ caller } = $opts->{ caller }->new;
}

$service = $aws->service($service_name, %$opts);

my $call;

if (not defined $method_name or not $call = $service->meta->find_method_by_name($method_name)){
  if (defined $method_name) {
    print "Don't know method $method_name on $service_name\n\n";
  } else {
    print "Please specify a method for $service_name\n\n"
  }
  
  print "Maybe you ment:\n";

  map  { say $_ }
    sort 
    map  { $_->name } 
    grep { $_->package_name =~ m/^Paws\:\:/ and 
           $_->name =~ m/^[A-Z]/ 
         } 
    $service->meta->get_all_methods;
  exit 1;
}

die "invalid method" if ($call->package_name !~ m/^Paws\:\:/);

my $help_with_method_call = grep { $_ =~ m/^help$/i } @args;

die "HELP!!!" if ($help_with_method_call);

my %args = %{ ARGV::Struct->new(argv => [ '{', @args, '}' ])->parse };

if ($opts->{ debug }) {
  print "Arguments to the call:\n";
  print Dumper(\%args);
} 

# $call is a Meta Method Object. It can be called via execute
my $result = eval {$call->execute($service, %args) };
if ($@) {
  print $@ if ($opts->{debug});
  if (ref($@) and $@->isa('Moose::Exception::AttributeIsRequired')) {
    print "Call to $method_name requires argument " . $@->attribute_name . "\n";
    my $call_class = Moose::Util::find_meta($@->class_name);
    my @atts = sort map { $_->name } $call_class->get_all_attributes;
    foreach my $att_name (@atts) {
      my $attribute = $call_class->find_attribute_by_name($att_name);
      say sprintf "%s: %s %s", $attribute->name, $attribute->type_constraint->name, ($attribute->is_required ? 'REQUIRED' : '');
    }
  } else {
    die $@;
  }

  exit 1;
}

if (ref($result)) {
  my $h = $service->to_hash($result);
  $h = Hash::Flatten::flatten($h, { HashDelimiter => '.', ArrayDelimiter => '.' });

  my $max_chars = reduce { length($a) > length($b) ? $a : $b } keys %$h;
  $max_chars = length($max_chars);
  #TODO: colorize
  say "$_: " . (' ' x ($max_chars - length($_))) .  $h->{ $_ } for (sort keys %$h);
}
