#!/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 duplicate_backup_job { return 'new-uuid-generated'; }
    sub sanitize_text_input  { return $_[0]; }
}

# 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 'duplicate_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::duplicate_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 'duplicate_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( { new_name => 'Duplicate' } ) };
    my $metadata = {};

    my $response = Whostmgr::API::1::CometBackup::duplicate_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 'duplicate_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::duplicate_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 'duplicate_backup_job - successful duplication' => 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(
        'duplicate_backup_job' => sub {
            my ( $self, $source_uuid, $new_name ) = @_;
            return 'duplicated-uuid-123';
        }
    );

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

    my $response = Whostmgr::API::1::CometBackup::duplicate_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}, 'duplicated-uuid-123', 'New UUID is in response';

    return;
};

subtest 'duplicate_backup_job - source 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(
        'duplicate_backup_job' => sub {
            return undef;
        }
    );

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

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

    is $metadata->{ok}, 0, 'Metadata is set to not ok';
    like $metadata->{error}, qr/Source backup job not found/i, 'Error mentions source job not found';
    ok exists $response->{job}, 'Response has job key';

    return;
};

subtest 'duplicate_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(
        'duplicate_backup_job' => sub {
            die "Database error occurred";
        }
    );

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

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

    is $metadata->{ok}, 0, 'Metadata is set to not ok';
    like $metadata->{error}, qr/Failed to duplicate backup job/i, 'Error mentions duplication failure';
    ok exists $response->{job}, 'Response has job key';

    return;
};

subtest 'duplicate_backup_job - with new_name parameter' => 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 $passed_name;
    my $mock_jobs = Test::MockModule->new('Whostmgr::CometBackup::BackupJobs');
    $mock_jobs->redefine(
        'duplicate_backup_job' => sub {
            my ( $self, $source_uuid, $new_name ) = @_;
            $passed_name = $new_name;
            return 'new-uuid-456';
        }
    );

    my $args     = { json => Cpanel::JSON::Dump( { job_uuid => 'original-uuid', new_name => 'My Copy' } ) };
    my $metadata = {};

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

    is $metadata->{ok},              1,              'Metadata is set to ok';
    is $passed_name,                 'My Copy',      'new_name was passed to duplicate_backup_job method';
    is $response->{job}->{job_uuid}, 'new-uuid-456', 'New UUID returned';

    return;
};

subtest 'duplicate_backup_job - passes correct source 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 $passed_uuid;
    my $mock_jobs = Test::MockModule->new('Whostmgr::CometBackup::BackupJobs');
    $mock_jobs->redefine(
        'duplicate_backup_job' => sub {
            my ( $self, $source_uuid, $new_name ) = @_;
            $passed_uuid = $source_uuid;
            return 'new-uuid-789';
        }
    );

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

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

    is $passed_uuid,                 $source_uuid,   'Correct source UUID passed to duplicate_backup_job method';
    is $response->{job}->{job_uuid}, 'new-uuid-789', 'Response contains new UUID';

    return;
};

subtest 'duplicate_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 => 'uuid-123' } ) };
    my $metadata = {};

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

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

    return;
};

subtest 'duplicate_backup_job - response structure on error' => 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::duplicate_backup_job( $args, $metadata );

    ok ref $response eq 'HASH',        'Response is a hash reference';
    ok exists $response->{job},        'Response has job key';
    ok ref $response->{job} eq 'HASH', 'job is a hash reference';
    is scalar( keys %{ $response->{job} } ), 0, 'job is empty hash on error';

    return;
};

done_testing();
