#!/usr/local/cpanel/3rdparty/bin/perl

#                                      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.

use cPstrict;
use Test::Cpanel::Policy;

use Test2::V0;
use Test2::Tools::Explain;
use Test2::Tools::Compare;
use Test2::Tools::Exception;

use Test::MockModule;
use Test::MockFile;
use Cpanel::JSON;

# Pre-load stub modules to prevent dependencies from failing
BEGIN {
    $INC{'Cpanel/Plugins/Components/Rules.pm'}             = 1;
    $INC{'Cpanel/Plugins/Components/Rules/Util/NVData.pm'} = 1;
    $INC{'Cpanel/Plugins/Components/Rules/CompanyId.pm'}   = 1;

    # Pre-load Cpanel modules
    $INC{'Cpanel/Server/Type.pm'} = 1;

    package Cpanel::Server::Type;
    sub get_products { return { 'cometbackup' => 1 }; }

    $INC{'Cpanel/PwCache.pm'} = 1;

    package Cpanel::PwCache;
    sub new { return bless {}, shift; }

    $INC{'Cpanel/Config/Users.pm'} = 1;

    package Cpanel::Config::Users;
    sub getcpusers { return qw(user1 user2); }

    # Pre-load CometBackup modules with stubs that can be overridden
    $INC{'Whostmgr/CometBackup/Setup.pm'} = 1;

    package Whostmgr::CometBackup::Setup;
    our @EXPORT = qw(is_comet_agent_installed is_agent_logged_in setup_comet_agent install_comet_agent);
    sub is_comet_agent_installed { return 0; }
    sub is_agent_logged_in       { return 0; }
    sub setup_comet_agent        { return 1; }
    sub install_comet_agent      { return 1; }

    $INC{'Whostmgr/CometBackup/AgentManager.pm'} = 1;

    package Whostmgr::CometBackup::AgentManager;
    our @EXPORT = qw(get_status start);
    sub get_status { return ( 1, 'active' ); }
    sub start      { return ( 1, '' ); }

    $INC{'Whostmgr/CometBackup/CometBackupAPI.pm'} = 1;

    package Whostmgr::CometBackup::CometBackupAPI;
    our @EXPORT = qw(get_server_config);
    sub get_server_config { return {}; }

    $INC{'Whostmgr/CometBackup/BackupRunLogs.pm'} = 1;

    package Whostmgr::CometBackup::BackupRunLogs;
    sub new             { return bless {}, shift; }
    sub get_recent_runs { return (); }

    $INC{'Whostmgr/CometBackup/BackupJobs.pm'} = 1;

    package Whostmgr::CometBackup::BackupJobs;
    sub new               { return bless {}, shift; }
    sub modify_backup_job { return 1; }
}

# Mock the Constants module BEFORE importing CometBackup
BEGIN {
    $INC{'Whostmgr/CometBackup/Constants.pm'} = 1;

    package Whostmgr::CometBackup::Constants;
    our $DISABLED_TOUCHFILE = '/var/cpanel/comet/comet_backup_disabled';
    our $BACKUP_RUNNER_PATH = '/usr/local/cpanel/backup-runner.pl';
    our $RUN_LOG_DB         = '/var/cpanel/comet/backup_run_logs.db';
    1;
}

use Whostmgr::API::1::CometBackup ();

# Mock the logger globally to prevent file access
my $mock_logger_global = Test::MockModule->new('Cpanel::Logger');
$mock_logger_global->redefine(
    'info'  => sub { return 1; },
    'error' => sub { return 1; },
    'warn'  => sub { return 1; },
    'debug' => sub { return 1; }
);

subtest 'modify_backup_job - missing JSON parameter' => sub {
    my $mock_utils = Test::MockModule->new('Whostmgr::API::1::Utils');
    $mock_utils->redefine(
        'set_metadata_not_ok' => sub {
            my ( $metadata, $error ) = @_;
            $metadata->{ok}    = 0;
            $metadata->{error} = $error;
            return 1;
        }
    );

    my $args     = {};
    my $metadata = {};

    my $response = Whostmgr::API::1::CometBackup::modify_backup_job( $args, $metadata );

    is $metadata->{ok}, 0, 'Metadata is set to not ok';
    like $metadata->{error}, qr/Missing required 'json'/i, 'Error mentions missing json';

    return;
};

subtest 'modify_backup_job - missing job_uuid' => sub {
    my $mock_utils = Test::MockModule->new('Whostmgr::API::1::Utils');
    $mock_utils->redefine(
        'set_metadata_not_ok' => sub {
            my ( $metadata, $error ) = @_;
            $metadata->{ok}    = 0;
            $metadata->{error} = $error;
            return 1;
        }
    );

    my $args     = { json => Cpanel::JSON::Dump( { job_config => {} } ) };
    my $metadata = {};

    my $response = Whostmgr::API::1::CometBackup::modify_backup_job( $args, $metadata );

    is $metadata->{ok}, 0, 'Metadata is set to not ok';
    like $metadata->{error}, qr/job_uuid.*required/i, 'Error mentions job_uuid required';
    ok exists $response->{job}, 'Response has job key';

    return;
};

subtest 'modify_backup_job - missing job_config' => sub {
    my $mock_utils = Test::MockModule->new('Whostmgr::API::1::Utils');
    $mock_utils->redefine(
        'set_metadata_not_ok' => sub {
            my ( $metadata, $error ) = @_;
            $metadata->{ok}    = 0;
            $metadata->{error} = $error;
            return 1;
        }
    );

    my $args     = { json => Cpanel::JSON::Dump( { job_uuid => 'test-uuid' } ) };
    my $metadata = {};

    my $response = Whostmgr::API::1::CometBackup::modify_backup_job( $args, $metadata );

    is $metadata->{ok}, 0, 'Metadata is set to not ok';
    like $metadata->{error}, qr/job_config.*required.*hash/i, 'Error mentions job_config and hash';

    return;
};

subtest 'modify_backup_job - job_config not a hash' => sub {
    my $mock_utils = Test::MockModule->new('Whostmgr::API::1::Utils');
    $mock_utils->redefine(
        'set_metadata_not_ok' => sub {
            my ( $metadata, $error ) = @_;
            $metadata->{ok}    = 0;
            $metadata->{error} = $error;
            return 1;
        }
    );

    my $args     = { json => Cpanel::JSON::Dump( { job_uuid => 'test-uuid', job_config => 'not a hash' } ) };
    my $metadata = {};

    my $response = Whostmgr::API::1::CometBackup::modify_backup_job( $args, $metadata );

    is $metadata->{ok}, 0, 'Metadata is set to not ok';
    like $metadata->{error}, qr/job_config.*hash/i, 'Error mentions hash reference';

    return;
};

subtest 'modify_backup_job - missing required field (name)' => sub {
    my $mock_utils = Test::MockModule->new('Whostmgr::API::1::Utils');
    $mock_utils->redefine(
        'set_metadata_not_ok' => sub {
            my ( $metadata, $error ) = @_;
            $metadata->{ok}    = 0;
            $metadata->{error} = $error;
            return 1;
        }
    );

    my $args = {
        json => Cpanel::JSON::Dump(
            {
                job_uuid   => 'test-uuid',
                job_config => {
                    schedule_type  => 'daily',
                    backup_options => {},
                    destinations   => [],
                    all_accounts   => 1,
                }
            }
        )
    };
    my $metadata = {};

    my $response = Whostmgr::API::1::CometBackup::modify_backup_job( $args, $metadata );

    is $metadata->{ok}, 0, 'Metadata is set to not ok';
    like $metadata->{error}, qr/name.*required/i, 'Error mentions name field';

    return;
};

subtest 'modify_backup_job - all_accounts false with no accounts array' => sub {
    my $mock_utils = Test::MockModule->new('Whostmgr::API::1::Utils');
    $mock_utils->redefine(
        'set_metadata_not_ok' => sub {
            my ( $metadata, $error ) = @_;
            $metadata->{ok}    = 0;
            $metadata->{error} = $error;
            return 1;
        }
    );

    my $args = {
        json => Cpanel::JSON::Dump(
            {
                job_uuid   => 'test-uuid',
                job_config => {
                    name           => 'Test Job',
                    schedule_type  => 'daily',
                    backup_options => {},
                    destinations   => [],
                    all_accounts   => 0,
                }
            }
        )
    };
    my $metadata = {};

    my $response = Whostmgr::API::1::CometBackup::modify_backup_job( $args, $metadata );

    is $metadata->{ok}, 0, 'Metadata is set to not ok';
    like $metadata->{error}, qr/accounts must be a non-empty array/i, 'Error mentions accounts required';

    return;
};

subtest 'modify_backup_job - successful modification' => sub {
    my $mock_utils = Test::MockModule->new('Whostmgr::API::1::Utils');
    $mock_utils->redefine(
        'set_metadata_ok' => sub {
            my ($metadata) = @_;
            $metadata->{ok} = 1;
            return 1;
        },
        'set_metadata_not_ok' => sub {
            my ( $metadata, $error ) = @_;
            $metadata->{ok}    = 0;
            $metadata->{error} = $error;
            return 1;
        }
    );

    my $mock_jobs = Test::MockModule->new('Whostmgr::CometBackup::BackupJobs');
    $mock_jobs->redefine(
        'modify_backup_job' => sub {
            return 1;
        }
    );

    my $args = {
        json => Cpanel::JSON::Dump(
            {
                job_uuid   => 'test-uuid',
                job_config => {
                    name           => 'Test Job',
                    schedule_type  => 'daily',
                    backup_options => {},
                    destinations   => [],
                    all_accounts   => 1,
                }
            }
        )
    };
    my $metadata = {};

    my $response = Whostmgr::API::1::CometBackup::modify_backup_job( $args, $metadata );

    is $metadata->{ok}, 1, 'Metadata is set to ok';
    ok exists $response->{job}, 'Response has job key';
    is $response->{job}->{job_uuid}, 'test-uuid', 'Job UUID in response matches input';
    is $response->{job}->{modified}, 1,           'Modified flag is set';

    return;
};

subtest 'modify_backup_job - modification fails' => sub {
    my $mock_utils = Test::MockModule->new('Whostmgr::API::1::Utils');
    $mock_utils->redefine(
        'set_metadata_not_ok' => sub {
            my ( $metadata, $error ) = @_;
            $metadata->{ok}    = 0;
            $metadata->{error} = $error;
            return 1;
        }
    );

    my $mock_jobs = Test::MockModule->new('Whostmgr::CometBackup::BackupJobs');
    $mock_jobs->redefine(
        'modify_backup_job' => sub {
            return 0;
        }
    );

    my $args = {
        json => Cpanel::JSON::Dump(
            {
                job_uuid   => 'nonexistent-uuid',
                job_config => {
                    name           => 'Test Job',
                    schedule_type  => 'daily',
                    backup_options => {},
                    destinations   => [],
                    all_accounts   => 1,
                }
            }
        )
    };
    my $metadata = {};

    my $response = Whostmgr::API::1::CometBackup::modify_backup_job( $args, $metadata );

    is $metadata->{ok}, 0, 'Metadata is set to not ok';
    like $metadata->{error}, qr/not found or modification failed/i, 'Error mentions modification failure';

    return;
};

subtest 'modify_backup_job - BackupJobs throws exception' => sub {
    my $mock_utils = Test::MockModule->new('Whostmgr::API::1::Utils');
    $mock_utils->redefine(
        'set_metadata_not_ok' => sub {
            my ( $metadata, $error ) = @_;
            $metadata->{ok}    = 0;
            $metadata->{error} = $error;
            return 1;
        }
    );

    my $mock_jobs = Test::MockModule->new('Whostmgr::CometBackup::BackupJobs');
    $mock_jobs->redefine(
        'modify_backup_job' => sub {
            die "Database error occurred";
        }
    );

    my $args = {
        json => Cpanel::JSON::Dump(
            {
                job_uuid   => 'test-uuid',
                job_config => {
                    name           => 'Test Job',
                    schedule_type  => 'daily',
                    backup_options => {},
                    destinations   => [],
                    all_accounts   => 1,
                }
            }
        )
    };
    my $metadata = {};

    my $response = Whostmgr::API::1::CometBackup::modify_backup_job( $args, $metadata );

    is $metadata->{ok}, 0, 'Metadata is set to not ok';
    like $metadata->{error}, qr/Failed to modify backup job/i, 'Error mentions modification failure';

    return;
};

subtest 'modify_backup_job - with run_now flag' => sub {
    my $mock_utils = Test::MockModule->new('Whostmgr::API::1::Utils');
    $mock_utils->redefine(
        'set_metadata_ok' => sub {
            my ($metadata) = @_;
            $metadata->{ok} = 1;
            return 1;
        },
        'set_metadata_not_ok' => sub {
            my ( $metadata, $error ) = @_;
            $metadata->{ok}    = 0;
            $metadata->{error} = $error;
            return 1;
        }
    );

    my $mock_jobs = Test::MockModule->new('Whostmgr::CometBackup::BackupJobs');
    $mock_jobs->redefine(
        'modify_backup_job' => sub {
            return 1;
        }
    );

    my $run_called   = 0;
    my $original_run = \&Whostmgr::API::1::CometBackup::run_backup_job;
    no warnings 'redefine';
    *Whostmgr::API::1::CometBackup::run_backup_job = sub {
        $run_called = 1;
        return { job => { run_uuid => 'test-run-uuid' } };
    };

    my $args = {
        json => Cpanel::JSON::Dump(
            {
                job_uuid   => 'test-uuid',
                job_config => {
                    name           => 'Test Job',
                    schedule_type  => 'daily',
                    backup_options => {},
                    destinations   => [],
                    all_accounts   => 1,
                    run_now        => 1,
                }
            }
        )
    };
    my $metadata = {};

    my $response = Whostmgr::API::1::CometBackup::modify_backup_job( $args, $metadata );

    is $metadata->{ok}, 1, 'Metadata is set to ok';
    ok $run_called, 'run_backup_job was called due to run_now flag';

    *Whostmgr::API::1::CometBackup::run_backup_job = $original_run;

    return;
};

subtest 'modify_backup_job - with specific accounts' => sub {
    my $mock_utils = Test::MockModule->new('Whostmgr::API::1::Utils');
    $mock_utils->redefine(
        'set_metadata_ok' => sub {
            my ($metadata) = @_;
            $metadata->{ok} = 1;
            return 1;
        },
        'set_metadata_not_ok' => sub {
            my ( $metadata, $error ) = @_;
            $metadata->{ok}    = 0;
            $metadata->{error} = $error;
            return 1;
        }
    );

    my $mock_jobs = Test::MockModule->new('Whostmgr::CometBackup::BackupJobs');
    my $passed_config;
    $mock_jobs->redefine(
        'modify_backup_job' => sub {
            my ( $self, $job_uuid, %config ) = @_;
            $passed_config = \%config;
            return 1;
        }
    );

    my $args = {
        json => Cpanel::JSON::Dump(
            {
                job_uuid   => 'test-uuid',
                job_config => {
                    name           => 'Test Job',
                    schedule_type  => 'daily',
                    backup_options => {},
                    destinations   => [],
                    all_accounts   => 0,
                    accounts       => [ 'user1', 'user2' ],
                }
            }
        )
    };
    my $metadata = {};

    my $response = Whostmgr::API::1::CometBackup::modify_backup_job( $args, $metadata );

    is $metadata->{ok}, 1, 'Metadata is set to ok';
    ok defined $passed_config, 'Config was passed to modify_backup_job';
    is $passed_config->{all_accounts}, 0, 'all_accounts value preserved';
    ok ref $passed_config->{accounts} eq 'ARRAY', 'accounts array passed through';

    return;
};

done_testing();
