#!/usr/bin/perl

use strict;
use warnings;

use Getopt::Long qw(:config posix_default no_ignore_case);
use List::Util qw(any);
use Term::ReadKey;
use YAML::XS;

my ($progname) = $0 =~ m{(?:.*/)?([^/]*)};

my $cfg = '/etc/ngcp-config/constants.yml';

my $auth_def = 'SHA-512';
my @auth_algos = qw(
    SHA-512
    SHA-384
    SHA-256
    SHA-224
);
my %auth_algo = map { $_ => 1 } @auth_algos;

my $priv_def = 'AES-256';
my @priv_algos = qw(
    AES-256
    AES-192
    AES-128
);
my %priv_algo = map { $_ => 1 } @priv_algos;

sub usage
{
    print <<HELP;
Usage: $progname [<options>...] <username>

Options:
  -a, --auth <auth-algo>  Specify the auth algorithm to use (default: $auth_def)
  -p, --priv <priv-algo>  Specify the priv algorithm to use (default: $priv_def)
  -r, --replace           Replace an existing user.
      --help              Display this help text.

Supported algorithms:
  Auth: @auth_algos
  Priv: @priv_algos
HELP
}

sub error
{
    my @args = @_;

    print { \*STDERR } "error: @args\n";
    exit 1;
}

sub prompt_pass
{
    my $msg = shift;

    print "$msg: ";
    ReadMode('noecho');
    my $pass = <STDIN>;
    ReadMode('normal');
    print "\n";

    chomp $pass;

    return $pass;
}

my %opts = (
    auth => $auth_def,
    priv => $priv_def,
    replace => 0,
);

my @opts_spec = (
    'auth|a=s' => sub {
        my (undef, $algo) = @_;
        if (not exists $auth_algo{$algo}) {
            error("unknown authentication algorithm $algo");
        }
        $opts{auth} = $algo;
    },
    'priv|p=s' => sub {
        my (undef, $algo) = @_;
        if (not exists $priv_algo{$algo}) {
            error("unknown encryption algorithm $algo");
        }
        $opts{priv} = $algo;
    },
    'replace|r' => \$opts{replace},
    'help' => sub { usage(); exit 0; },
);

GetOptions(@opts_spec);

if (@ARGV != 1) {
    error("expects a single username");
}

my $user = shift;

my $yaml = YAML::XS::LoadFile($cfg);

# Check for existing users.
if (any { $_->{u} eq $user } @{$yaml->{credentials}{snmpd}{users}}) {
    if (! $opts{replace}) {
        error("user already exists; use --replace to override");
    }
}

my $pass = prompt_pass('Type password');
if (length $pass < 8) {
    error("password must be at least 8 characters long");
}
my $pass_check = prompt_pass('Re-type password');
if ($pass ne $pass_check) {
    error("password does not match");
}

my $new_entry = {
    u => $user,
    p => $pass,
    auth_type => $opts{auth},
    priv_proto => $opts{priv},
};

# Add or replace user.
my $found = 0;

# Check whether we need to replace an existing user.
foreach my $entry (@{$yaml->{credentials}{snmpd}{users}}) {
    next if $entry->{u} ne $user;

    $entry = $new_entry;
    $found = 1;
    last;
}

# Otherwise add it anew.
if (! $found) {
    push @{$yaml->{credentials}{snmpd}{users}}, $new_entry;
}

YAML::XS::DumpFile($cfg, $yaml);

system { 'ngcpcfg' } 'ngcpcfg', 'apply', "Add SNMP $user";

system { 'ngcpcfg' } 'ngcpcfg', 'push', 'all';

END {
    ReadMode('restore');
}

1;
