#!/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 get_backup_jobs   { return ( { job_uuid => 'test-uuid', status => 'running', run_uuid => 'run-123' } ); }
    sub update_job_status { return 1; }

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

    package Cpanel::FileUtils::TouchFile;
    sub touchfile { 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';

    sub get_cancel_file_path {
        my ($job_uuid) = @_;
        return "/var/cpanel/comet/cancel_$job_uuid";
    }
    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 'cancel_running_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::cancel_running_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 'cancel_running_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( { run_uuid => 'run-123' } ) };
    my $metadata = {};

    my $response = Whostmgr::API::1::CometBackup::cancel_running_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->{cancelled}, 'Response has cancelled key';

    return;
};

subtest 'cancel_running_backup_job - job_uuid is empty string' => 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 => '' } ) };
    my $metadata = {};

    my $response = Whostmgr::API::1::CometBackup::cancel_running_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';

    return;
};

subtest 'cancel_running_backup_job - successful cancellation' => 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(
        'get_backup_jobs' => sub {
            return ( { job_uuid => 'test-uuid', status => 'running', run_uuid => 'run-uuid-123' } );
        },
        'update_job_status' => sub {
            return 1;
        }
    );

    my $mock_touchfile = Test::MockModule->new('Cpanel::FileUtils::TouchFile');
    my $file_touched   = 0;
    $mock_touchfile->redefine(
        'touchfile' => sub {
            $file_touched = 1;
            return 1;
        }
    );

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

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

    is $metadata->{ok}, 1, 'Metadata is set to ok';
    ok exists $response->{cancelled}, 'Response has cancelled key';
    is $response->{cancelled}->{job_uuid}, 'test-uuid',  'Job UUID in response';
    is $response->{cancelled}->{status},   'cancelling', 'Status is cancelling';
    ok $file_touched, 'Cancel file was touched';

    return;
};

subtest 'cancel_running_backup_job - job not found' => 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(
        'get_backup_jobs' => sub {
            return ();
        }
    );

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

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

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

    return;
};

subtest 'cancel_running_backup_job - job not running' => 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(
        'get_backup_jobs' => sub {
            return ( { job_uuid => 'test-uuid', status => 'disabled' } );
        }
    );

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

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

    is $metadata->{ok}, 0, 'Metadata is set to not ok';
    like $metadata->{error}, qr/not currently running/i, 'Error mentions job not running';

    return;
};

subtest 'cancel_running_backup_job - run_uuid mismatch' => 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(
        'get_backup_jobs' => sub {
            return ( { job_uuid => 'test-uuid', status => 'running', run_uuid => 'run-uuid-123' } );
        }
    );

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

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

    is $metadata->{ok}, 0, 'Metadata is set to not ok';
    like $metadata->{error}, qr/run_uuid does not match/i, 'Error mentions run_uuid mismatch';

    return;
};

subtest 'cancel_running_backup_job - with matching run_uuid' => 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(
        'get_backup_jobs' => sub {
            return ( { job_uuid => 'test-uuid', status => 'running', run_uuid => 'run-uuid-123' } );
        },
        'update_job_status' => sub {
            return 1;
        }
    );

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

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

    is $metadata->{ok},                    1,              'Metadata is set to ok';
    is $response->{cancelled}->{run_uuid}, 'run-uuid-123', 'Run UUID in response matches input';

    return;
};

subtest 'cancel_running_backup_job - exception handling' => 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(
        'get_backup_jobs' => sub {
            die "Database error";
        }
    );

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

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

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

    return;
};

subtest 'cancel_running_backup_job - response structure on success' => 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 $args     = { json => Cpanel::JSON::Dump( { job_uuid => 'test-uuid' } ) };
    my $metadata = {};

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

    ok ref $response eq 'HASH',                   'Response is a hash reference';
    ok exists $response->{cancelled},             'Response has cancelled key';
    ok ref $response->{cancelled} eq 'HASH',      'cancelled is a hash reference';
    ok exists $response->{cancelled}->{job_uuid}, 'cancelled contains job_uuid';
    ok exists $response->{cancelled}->{status},   'cancelled contains status';
    ok exists $response->{cancelled}->{message},  'cancelled contains message';

    return;
};

done_testing();
