
#                                      Copyright 2024 WebPros International, LLC
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited.

package Whostmgr::360Monitoring::Setup;

=encoding utf8

=head1 NAME

Whostmgr::360Monitoring::Setup

=head1 DESCRIPTION

Setup related functionality for cpanel-monitoring-plugin.

=cut

use cPstrict;
use Cpanel::AppConfig                                ();
use Cpanel::TaskQueue::Reader                        ();
use Cpanel::Exception                                ();
use Cpanel::Plugins::Components::Rules::Util::NVData ();
use Cpanel::Plugins::Components::Rules::CompanyId    ();
use Cpanel::PwCache                                  ();
use Whostmgr::360Monitoring::Auth                    ();
use Whostmgr::360Monitoring::Agent                   ();
use Whostmgr::360Monitoring::AgentManager            ();
use Whostmgr::360Monitoring::Config                  ();
use Whostmgr::ACLS                                   ();

use Cpanel::Imports;

our $DEBUG = 0;

=head1 FUNCTIONS

=cut

sub is_setup ( $username = '' ) {
    if ( !$username ) {
        require Cpanel::PwCache;
        $username = Cpanel::PwCache::getusername();
    }

    my $agent_360_status = is_agent360_running();
    return $agent_360_status if !$agent_360_status;
    my $default_plugin_status = eval { are_agent360_default_plugins_enabled(); };
    if ( my $exception = $@ ) {
        logger()->warn("The are_agent360_default_plugins_enabled failed due to the following exception: $exception.");
        return 0;
    }
    return $default_plugin_status;

}

sub check_runscript_task ($script) {

    # Check to see if task is queued
    my $error_message;

    my $current_queue = Cpanel::TaskQueue::Reader::read_queue();
    my @waiting_jobs  = grep { $_->{'_command'} eq 'run_script' && $_->{_args}->[0] eq $script } @{ $current_queue->{'waiting_queue'} };
    my @running_jobs  = grep { $_->{'_command'} eq 'run_script' && $_->{_args}->[0] eq $script } @{ $current_queue->{'processing_queue'} };
    if ( scalar @waiting_jobs ) {
        $error_message = "Initialization is queued. Please wait and try again.";
    }
    if ( scalar @running_jobs ) {
        $error_message = "Initialization is in progress. Please wait and try again.";
    }

    return {
        'status' => defined $error_message ? 0 : 1,
        'error'  => $error_message
    };
}

sub is_taskqueue_clear () {
    my $init_status = check_runscript_task("/usr/local/cpanel/scripts/initialize_360monitoring");
    return $init_status->{'status'};
}

sub initialize ( $username = '' ) {
    if ( !$username ) {
        $username = _get_username();
    }

    logger()->info("Initializing the server.");
    if ( !is_360Monitoring_allowed() ) {
        logger()->info("The 360 Monitoring is not allowed, but checking if it was setup previously.");
        if( ! is_360Monitoring_setup_previously() ) {
            logger()->warn("The 360 Monitoring cannot be setup in this server.");
            return;
        }
    }

    my $user = get_credentials($username);

    if ( !is_monitoring_plugin_installed() ) {

        # installing server monitoring will not function as expected, when agent360 is
        # already running and has an account associated with it.
        # temporary workaround is not to install monitoring plugin
        # TODO: DUCKS-1398
        if ( !$user->user_id && is_agent360_running() ) {
            logger()->warn("360 Monitoring will not be installed as it already has an instance of agent360 running.");
            return;
        }
        else {
            install_monitoring_plugin();
        }
    }

    if ( !$user->user_id ) {

        my $auth = Whostmgr::360Monitoring::Auth->new( username => $username );

        $user = eval { $auth->create_user() };
        if ( !$user && ( my $exception = $@ ) ) {
            my $err = Cpanel::Exception->create( 'Unable to create an user account in 360 Monitoring for user: “[_1]”.', [$username] );
            logger()->error("The 360 Monitoring account creation failed due to an exception: $exception.");
            die $err->get_string_no_id();
        }

        my $api_key = $user->api_key();
        my $user_id = $user->user_id();

        logger()->info("created apiKey for userId: '$user_id'");

        $user->enabled(1);
        $user->save_user_info();
    }

    if ( !Whostmgr::360Monitoring::Setup::is_setup() ) {
        setup_staging($user);

        my $output = eval { setup_monitoring( $user->user_id ) };
        if ( my $exception = $@ ) {
            my $err = Cpanel::Exception->create( 'Failed to setup 360 Monitoring for user: “[_1]” with the error: “[_2]”.', [ $username, $exception ] );
            die $err->get_string_no_id();
        }

        # setup miniapp for select company_id by default
        setup_miniapp();
    }

    return $user;
}

sub setup_monitoring ($user_id) {
    logger()->info("Setting up monitoring agent for user: '$user_id'.");
    require Cpanel::SafeRun::Object;
    my $run = Cpanel::SafeRun::Object->new(
        program => "/var/cpanel/plugins/monitoring-agent/install.sh",
        args    => [
            $user_id,
        ]
    );
    if ( $run->CHILD_ERROR() ) {
        my $error = $run->stderr();
        die $error;
    }
    logger()->info( "Output: " . $run->stdout() );

    logger()->info("Setting up the agent plugins to monitor installed services");

    my $agent = Whostmgr::360Monitoring::Agent::load_from_file();
    $agent->install_plugins();
    _restart_agent();
}

sub setup_staging ($user) {
    eval {
        if ( Whostmgr::360Monitoring::Agent::IS_STAGING() ) {
            logger()->info("Setting up staging enviornment");
            my $content = Cpanel::Slurper::read('/var/cpanel/plugins/monitoring/agent360.staging.ini');
            Cpanel::Slurper::write( '/etc/agent360.ini', $content );
        }
    };
    if ( my $exception = $@ ) {
        die Cpanel::Exception->create( 'Failed to setup staging environment with the error: “[_1]”.', [$exception] );
    }

    return;
}

sub is_360Monitoring_allowed {
    my $config = Whostmgr::360Monitoring::Config::load();
    require Cpanel::Plugins::Components::Rules;
    my $rules_obj = Cpanel::Plugins::Components::Rules->new( 'config' => $config->{'enable-plugin'} );
    return $rules_obj->is_allowed();
}

sub is_360Monitoring_setup_previously {

    # return false if monitoring is not setup
    # nothing further to check
    return 0 if !Whostmgr::360Monitoring::Setup::is_setup();

    # re-evaluate some rules again to confirm the recent license changes
    # this is a set of customers who have recently changed the license
    # moved away from allowed list of company-id's but setup 360 before
    # We let them continue gaining access to 360
    my $config                  = Whostmgr::360Monitoring::Config::load();
    my $leave_it_enabled_status = Cpanel::Plugins::Components::Rules->new( config => $config->{'update-plugin'} )->is_allowed();

    return $leave_it_enabled_status;
}

sub is_agent360_running {
    my $status = Whostmgr::360Monitoring::AgentManager::get_status();
    return $status eq 'active' ? 1 : 0;
}

sub are_agent360_default_plugins_enabled {
    my $agent = Whostmgr::360Monitoring::Agent::load_from_file();
    my ( $status, $needs_install ) = ( 0, 0 );
    foreach my $default_plugin ( @{ $agent->default_plugins } ) {
        my $plugin = $agent->get_plugin($default_plugin);
        $needs_install = 1 if !$plugin || !$plugin->enabled;
        last;
    }
    if ($needs_install) {
        logger()->info("Plugin needs install ");
        $status = $agent->install_plugins();
        _restart_agent() if $status;
        return $status;
    }
    return 1;
}

sub _restart_agent {
    logger()->info("Restarting the 360 Agent");
    return Whostmgr::360Monitoring::AgentManager::restart();
}

sub get_credentials ( $username = '' ) {
    $username = _get_username() if !$username;
    my $user = Whostmgr::360Monitoring::User->new( username => $username );

    return $user;
}

sub _get_username {
    require Cpanel::PwCache;
    my $username = Cpanel::PwCache::getusername();
    return $username;
}

sub is_monitoring_plugin_installed {
    my $registered_whm_apps = Cpanel::AppConfig::get_application_list()->{whostmgr};
    my @matches             = grep { $_->{origin} && $_->{origin} eq "whm-360-monitoring.conf" } $registered_whm_apps->@*;
    if (@matches) {
        logger->info("Monitoring installed already") if $DEBUG == 1;
        return 1;
    }
    return 0;
}

sub install_monitoring_plugin {
    my %steps = (
        "/var/cpanel/plugins/monitoring/install.sh"    => { message => "Installing the server monitoring plugin" },
        "/usr/local/cpanel/scripts/rebuild_whm_chrome" => { message => "Rebuiding the WHM Chrome" },
        "/usr/local/cpanel/bin/manage_hooks"           => { args    => [qw/add module Whostmgr::360Monitoring::Hooks::ServerRoleChange/], message => "Installing the server role change hook" },
    );

    require Cpanel::SafeRun::Object;

    foreach my $program ( keys %steps ) {
        my $args = $steps{$program}{args};
        logger->info( $steps{$program}{message} );
        my $run = Cpanel::SafeRun::Object->new(
            'program' => $program,
            ( $args ? ( 'args' => $args ) : () ),
        );

        if ( $run->CHILD_ERROR() ) {
            my $error = join( q< >, map { $run->$_() // () } qw( stdout stderr ) );
            logger->error( sprintf( "The system failed failed to setup server monitoring while running %s: %s", $program, $error ) );
        }
        else {
            logger->info( "Output: " . $run->stdout() );
        }
    }

    return;
}

sub setup_miniapp {

    # The ACLS have to be set for NVData::set to update the exact store
    # if not provided, it will default the store to root.yaml
    local $ENV{'REMOTE_USER'} = ( Cpanel::PwCache::getpwuid($>) )[0];
    Whostmgr::ACLS::init_acls();

    my $config = Whostmgr::360Monitoring::Config::load();
    if ( Cpanel::Plugins::Components::Rules->new( 'config' => $config->{'auto-enable-miniapp'} )->is_allowed() ) {
        my $status = Cpanel::Plugins::Components::Rules::Util::NVData::set_nv_data( "cpanel.360-monitoring.plugin", "homepage-app-enabled", 1 );
        if ($status) {
            logger()->info("Enabled miniapp for default company_id list");
        }
        else {
            logger()->warn("set_nv_data failed and miniapp will not be enabled");
        }
    }
    return;
}

=head2 local_enable()

This function allows a local administrator to enable a plugin, bypassing
checks for company_id, but not cpanel-360Monitoring-disabled.

=cut

sub local_enable {
    require Cpanel::FeatureFlags;
    require Cpanel::Plugins::State;

    # Remove the local disable flag.
    Cpanel::FeatureFlags::disable('cpanel.360-monitoring.plugin.local.disable');

    # Respect the Webpros disable flag.
    if ( Cpanel::FeatureFlags::is_feature_enabled('cpanel-360Monitoring-disabled') ) {
        die "360 Monitoring cannot be installed currently.";
    }

    # Touch the local enable flag first, since it gets checked in initialize.
    Cpanel::FeatureFlags::enable('cpanel.360-monitoring.plugin.local.enable');

    initialize();

    Cpanel::Plugins::State::install('cpanel.360-monitoring.plugin');

    return;
}

=head2 local_disable()

This function allows a local administrator to disable a plugin, bypassing
any checks from Webpros.

=cut

sub local_disable {
    require Cpanel::FeatureFlags;
    require Cpanel::Plugins::State;
    require Whostmgr::360Monitoring::AgentManager;

    # Remove the local enable flag.
    Cpanel::FeatureFlags::disable('cpanel.360-monitoring.plugin.local.enable');

    Cpanel::Plugins::State::uninstall('cpanel.360-monitoring.plugin');

    # Uninstall the UI.
    my $run = _safe_run( "/var/cpanel/plugins/monitoring/uninstall.sh", [], );
    if ( $run->CHILD_ERROR() ) {
        my $error = $run->stderr();
        die $error;
    }
    logger()->info( "Output: " . $run->stdout() );

    Whostmgr::360Monitoring::AgentManager::disable();

    # Touch the local disable flag if not already there.
    Cpanel::FeatureFlags::enable('cpanel.360-monitoring.plugin.local.disable');

    return;
}

# Args
# $program - string    - shell command to run
# $args    - array ref - arguments
sub _safe_run ( $program, $args ) {
    require Cpanel::SafeRun::Object;
    my $run = Cpanel::SafeRun::Object->new(
        program => $program,
        args    => $args,
    );
}

1;
