#                                     Copyright 2025 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 Common::Plugins::Setup::Steps;

use cPstrict;
use Cpanel::SafeRun::Object ();
use Cpanel::Imports;

=encoding utf8

=head1 NAME

Common::Steps

=head1 DESCRIPTION

Helper functions for running process steps.

=head1 FUNCTIONS

=head2 run_steps(APPNAME, STEPS)

Run a series of steps. Failures are logged, but do not stop the process.

This mimics the behavior used in the package scriptlets where we appended `|| true` to
the end of most commands to make sure that minor plugin installation procedures do not
stop YUM/APT updates by breaking the transations. As this code still runs in the package
install context this behavior was preserved.

=head3 ARGUMENTS

=over

=item APPNAME - string

The name of the application or plugin running the steps to use in log messages.

=item STEPS - list of key/value pairs

The list of steps to run. The odd slots are the program to run, and the even slots a hashref with the following structures:

=over

=item message - string

The info message to log before running the step.

=item args - arrayref of string

Any additional arguments to pass to the program.

=back

=back

=head3 RETURNS

A boolean indicating success or failure, and an arrayref of failure messages.

=cut

sub run_steps ( $APP_NAME, @STEPS ) {

    die "No app name provided." unless $APP_NAME;
    die "No steps to run."      unless @STEPS;
    die "Steps must have an even number of elements: name => { config }." if @STEPS % 2;

    my @failed;

    foreach my $i ( grep { $_ % 2 == 0 } 0 .. $#STEPS ) {
        my ( $program, $config ) = @STEPS[ $i, $i + 1 ];
        my $args = $config->{args};

        logger->info( $config->{message} ) if $config->{message};

        my $run = Cpanel::SafeRun::Object->new(
            'program' => $program,
            ( $args ? ( 'args' => $args ) : () ),
        );
        if ( $run->CHILD_ERROR() ) {
            my $cli_error = join( q< >, map { $run->$_() // () } qw( stdout stderr ) );
            my $error     = $cli_error ? sprintf( "The system failed to setup %s while running %s: %s", $APP_NAME, $program, $cli_error ) : sprintf( "The system failed to setup %s while running %s.", $APP_NAME, $program );
            my $cli       = $program . join( q< >, $args ? @$args : () );
            logger->error($cli);
            logger->error($error);
            push @failed, "$cli\n$error";
        }
        else {
            logger->info( "Output: " . $run->stdout() );
        }
    }

    #        status,              list of failures
    return ( @failed ? !!0 : !!1, \@failed );
}

1;
