#!/usr/bin/perl

=head1 DESCRIPTION


    
=cut

use strict;
use warnings;

use Data::Dumper;
use Getopt::Long;
use JSON;
use LWP::UserAgent;

use utf8;
use open ':std' => ':utf8';

our $VERBOSE;

our %cmd_handlers = (
    cat  => \&cmd_cat,
    help  => \&cmd_help,
    ls   => \&cmd_ls,
    rm   => \&cmd_rm,
    tee  => \&cmd_tee,
);

run() unless caller();

sub run
{
    my $opt = parse_options();

    die "usupported cmd '$opt->{cmd}', stop\n" unless exists $cmd_handlers{$opt->{cmd}};
    $cmd_handlers{$opt->{cmd}}->($opt);

    exit 0;
}

sub parse_options
{
    my %O;

    GetOptions (
        "h|help"       => sub {system("podselect -section SYNOPSIS -section DESCRIPTION $0 "); exit 0;},
        "v|verbose"    => \$O{verbose},
    ) or die "can't parse options, stop";

    die "command missing, stop\nsee ckv -h for help\n" unless @ARGV > 0;
    $O{cmd} = shift @ARGV;

    $O{argv} = [ @ARGV ];

    $VERBOSE = delete $O{verbose};

    return \%O;
}

=head2 help

    справка по командам

=cut
sub cmd_help
{
    my ($opt) = @_;

    for my $topic (@{$opt->{argv}}){
        system("podselect -section '/$topic' $0") == 0 or print "no help for '$topic'";
    }

    if ( @{$opt->{argv}} == 0 ){
        print "available commands:\n".join("", map { "    $_\n" } sort keys %cmd_handlers );
    }
}

=head2 ls

    список существующих ключей

=cut
sub cmd_ls
{
    my ($opt) = @_;

    my $json = _http_get('http://localhost:8500/v1/kv/?keys');
    my $data = from_json($json);

    print join "", map { "$_\n" } @$data;
}


=head2 cat

    читает указанные ключи и пишет контент на stdout

=cut
sub cmd_cat
{
    my ($opt) = @_;

    for my $key ( @{ $opt->{argv} } ){
        my $data = _http_get("http://localhost:8500/v1/kv/$key?raw");
        print $data;
    }
}


=head2 tee

    читает stdin и пишет в указанный ключ

=cut
sub cmd_tee
{
    my ($opt) = @_;
    
    my $key = $opt->{argv}->[0];
    die "empty key to write, stop\n" unless $key;
    my $url = "http://localhost:8500/v1/kv/$key";

    my $data = join "", <STDIN>;

    _http_put($url, $data);
}

=head2 rm 

=cut
sub cmd_rm
{
    my ($opt) = @_;
    my $key = $opt->{argv}->[0];
    die "empty key to remove, stop\n" unless $key;
    my $url = "http://localhost:8500/v1/kv/$key";

    _http_delete($url);
}

sub _http_get
{
    my ($url) = @_;
    die "_http_get: empty url" unless $url;
    print STDERR "...getting data (curl $url)\n" if $VERBOSE;

    my $ua = LWP::UserAgent->new;

    my $response = $ua->get($url);

    if ($response->is_success) {
        return $response->decoded_content;
    } else {
        die $response->status_line;
    }
}

sub _http_put
{
    my ($url, $data) = @_;
    print STDERR "...putting data (curl -X PUT --data-binary \@<filename> $url)\n" if $VERBOSE;

    my $ua = LWP::UserAgent->new;

    my $response = $ua->put($url, Content => $data);

    if ($response->is_success) {
        if ($response->decoded_content ne 'true'){
            die "put failed: ".$response->decoded_content;
        }  
        return '';
    } else {
        die $response->status_line;
    }
}

sub _http_delete
{
    my ($url) = @_;
    die "_http_delete: empty url" unless $url;
    print STDERR "...deleting data (curl -xDELETE $url)\n" if $VERBOSE;

    my $ua = LWP::UserAgent->new;

    my $response = $ua->delete($url);

    if ($response->is_success) {
        if ($response->decoded_content ne ''){
            die "delete failed: ".$response->decoded_content;
        }  
        return '';
    } else {
        die $response->status_line;
    }
}
