#! /usr/bin/env perl
# vim: set filetype=perl ts=4 sw=4 sts=4 et:
use common::sense;
use Term::ANSIColor;

use File::Basename;
use lib dirname(__FILE__) . '/../../../lib/perl';
use OVH::Result;
use OVH::Bastion;
use OVH::Bastion::Plugin qw( :DEFAULT help );

# globally allow sys_getpw* and sys_getgr* cache use
$ENV{'PW_GR_CACHE'} = 1;

my $remainingOptions = OVH::Bastion::Plugin::begin(
    argv     => \@ARGV,
    header   => "OSH help",
    options  => {},
    helptext => <<'EOF',
I'm So Meta, Even This Acronym

Usage: --osh SCRIPT_NAME
EOF
);

#
# code
#
my $fnret;

my @knownPlugins = (
    'MANAGE YOUR ACCOUNT' => [
        'manage your ingress credentials (you->bastion)' => [
            qw{ selfListIngressKeys selfResetIngressKeys selfAddIngressKey selfDelIngressKey
              selfGenerateProxyPassword selfMFASetupPassword selfMFAResetPassword selfMFASetupTOTP selfMFAResetTOTP }
        ],
        'manage your egress credentials (bastion->server)' =>
          [qw{ selfListEgressKeys selfGenerateEgressKey selfDelEgressKey selfGeneratePassword selfListPasswords }],
        'manage your accesses to servers' =>
          [qw{ selfListAccesses selfAddPersonalAccess selfDelPersonalAccess selfForgetHostKey }],
        'manage your current sessions' => [qw{ lock unlock }],
        'review past sessions'         => [qw{ selfListSessions selfPlaySession }],
        'other commands'               => [qw{ selfModify }],
    ],
    'MANAGE OTHER ACCOUNTS' => [
        'manage bastion accounts' => [
            qw{ accountList accountInfo accountCreate accountDelete accountUnexpire accountUnlock accountFreeze accountUnfreeze accountModify accountPIV }
        ],
        'manage accounts ingress credentials (them->bastion)' =>
          [qw{ accountListIngressKeys accountResetIngressKeys accountMFAResetPassword accountMFAResetTOTP }],
        'manage accounts egress credentials (bastion->server)' =>
          [qw{ accountListEgressKeys accountGeneratePassword accountListPasswords }],
        'manage access to restricted commands'       => [qw{ accountGrantCommand accountRevokeCommand }],
        'manage another account accesses to servers' =>
          [qw{ accountListAccesses accountAddPersonalAccess accountDelPersonalAccess whoHasAccessTo }],
        'review past sessions' => [qw{ accountListSessions globalListSessions }],
    ],
    'MANAGE GROUPS' => [
        'information and lifecycle' => [qw{ groupInfo groupListServers groupList groupCreate groupDelete }],
        'group owner commands'      => [
            qw{ groupAddGatekeeper groupDelGatekeeper groupAddAclkeeper groupDelAclkeeper
              groupAddOwner groupDelOwner groupTransmitOwnership groupGenerateEgressKey groupDelEgressKey groupModify groupDestroy }
        ],
        'egress passwords commands'             => [qw{ groupListPasswords groupGeneratePassword groupDelPassword }],
        'gatekeeper commands to manage members' => [qw{ groupAddMember groupDelMember }],
        'gatekeeper commands to manage guests'  =>
          [qw{ groupListGuestAccesses groupAddGuestAccess groupDelGuestAccess }],
        'aclkeeper commands to manage group servers' => [qw{ groupAddServer groupDelServer }],
    ],
    'BASTION ADMIN' => [
        'other commands' => [qw{ adminSudo adminMaintenance }],
    ],
    'MISC COMMANDS' => [
        'basic commands'   => [qw{ help info }],
        'utility commands' => [qw{ nc ping mtr alive clush scp sftp batch }],
        'realm commands'   => [qw{ realmList realmInfo realmCreate realmDelete }],
        'audit commands'   => [qw{ rootListIngressKeys }],
        'other specific commands',
    ],
);

my %colorpanel = (
    'open'             => 'green',
    'restricted'       => 'cyan',
    'group-aclkeeper'  => 'yellow',
    'group-gatekeeper' => 'yellow',
    'group-owner'      => 'magenta',
    'admin'            => 'red',
);

# create a hash with all the plugins listed above
my %alreadySeenPlugins;
my $i = 1;
while ($i < scalar @knownPlugins) {
    my $j = 1;
    while ($j < scalar @{$knownPlugins[$i]}) {
        foreach (@{$knownPlugins[$i]->[$j]}) {
            $alreadySeenPlugins{$_} = 1;
        }
        $j += 2;
    }
    $i += 2;
}

# then get the real list of this bastion
$fnret = OVH::Bastion::get_plugin_list();
$fnret or osh_exit $fnret;

# and add plugins not listed above to the 'other specific commands' section
my @otherPlugins;
foreach my $plugin (sort keys %{$fnret->value}) {
    next if exists $alreadySeenPlugins{$plugin};
    osh_debug("an unknown but valid command $plugin in " . $fnret->value->{$plugin}{'dir'});
    push @otherPlugins, $plugin;
}
$knownPlugins[-1][scalar(@{$knownPlugins[-1]})] = \@otherPlugins;

$i = 0;
while ($i < scalar @knownPlugins) {
    my $mainCategoryName        = $knownPlugins[$i++];
    my $mainCategoryArray       = $knownPlugins[$i++];
    my $mainCategoryNamePrinted = 0;
    my $j                       = 0;
    while ($j < scalar @$mainCategoryArray) {
        my $subCategoryName = $mainCategoryArray->[$j++];
        my @plugins         = @{$mainCategoryArray->[$j++]};

        osh_debug("working on cat [$mainCategoryName // $subCategoryName] with " . (scalar @plugins) . " commands");
        osh_debug("plugins are: " . join('/', @plugins));

        my @availableCommands;
        my $curLen;
        my $curIndex;
        foreach my $cmd (@plugins) {
            $fnret = OVH::Bastion::can_account_execute_plugin(account => $self, plugin => $cmd, cache => 1);
            next unless $fnret;
            if (($curLen + length($fnret->value->{'plugin'})) > 80) {
                $curIndex++;
                $curLen = 0;
            }
            $curLen += length($fnret->value->{'plugin'});
            push @{$availableCommands[$curIndex]},
              colored($fnret->value->{'plugin'}, $colorpanel{$fnret->value->{'type'}});
        }
        if (@availableCommands) {
            if (not $mainCategoryNamePrinted) {
                osh_info " > $mainCategoryName";
                $mainCategoryNamePrinted = 1;
            }
            osh_info "   - $subCategoryName:";
            osh_info "      " . join(' ', @$_) for @availableCommands;
        }
    }
    osh_info ' ';
}

osh_info "\nUse --help to get extra help when entering a command";
my $bastionName = OVH::Bastion::config('bastionName');
if ($bastionName) {
    $bastionName = $bastionName->value;
    osh_info "i.e. $bastionName --osh info --help";
}
my $docURL = OVH::Bastion::config('documentationURL');
if ($docURL && $docURL->value) {
    osh_info "Documentation: " . $docURL->value;
}

if (OVH::Bastion::config('readOnlySlaveMode')->value) {
    osh_warn "\nNOTICE: This bastion is part of a cluster, and this instance is a read-only one (slave),\n"
      . "so only read-only compliant commands are available.\nIf you need to use write/modify commands, "
      . "please do it on the master of the cluster instead.";
}

osh_ok;
