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

# cpanel - t/medium/scripts-modify_featurelist.t
#                                                  Copyright 2023 cPanel, L.L.C.
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited

package t::medium::scripts::modify_featurelist;

use Test2::V0 -no_utf8 => 1;
use Test2::Tools::Explain;
use Test2::Plugin::NoWarnings;
use Test2::Tools::Exception qw/dies/;
use Test2::Tools::Warnings;

use Test::Warn;
use Test::Cpanel::Policy;
use Test::MockFile;

use Cpanel::ConfigFiles ();

use parent qw(
  Cpanel::TestObj
);

require "/usr/local/cpanel/scripts/modify_featurelist";
$INC{'scripts/modify_featurelist.pm'} = __PACKAGE__;

__PACKAGE__->new()->runtests() if !caller;

#----------------------------------------------------------------------

#e.g., num_method_tests()
sub new ( $class, @args ) {
    my $self = $class->SUPER::new(@args);
    return $self;
}

#e.g., set environs before running tests
sub runtests ( $self, @args ) {
    return $self->SUPER::runtests(@args);
}

sub test__verify_directories : Tests(1) ($self) {

    no warnings qw{redefine once};
    local *mkdir = sub { return 0 };

    my @mocks = (
        $self->mock_subs(
            'Cpanel::FileUtils::TouchFile::touchfile' => sub { return 1 },
        )
    );

    my $mocked_FEATURES_DIR        = Test::MockFile->new_dir($Cpanel::ConfigFiles::FEATURES_DIR);
    my $mocked_VERSION_DIR         = Test::MockFile->new_dir('/var/cpanel/version');
    my $mocked_default_featurelist = Test::MockFile->file('/var/cpanel/features/default');

    my @logged_warnings;
    local *Cpanel::Logger::warn = sub {
        @logged_warnings = @_;
    };

    scripts::modify_featurelist::_verify_directories();

    is( @logged_warnings, L(), 'warns when directory cannot be created' );
}

sub test_modify_feature_for_all_feature_lists : Tests(6) ($self) {

    my $mocked_FEATURES_DIR = Test::MockFile->dir($Cpanel::ConfigFiles::FEATURES_DIR);
    my @feature_lists;
    my %features = (
        'filemanager'     => 1,
        'popaccts'        => 1,
        'addondomains'    => 1,
        'sslmanager'      => 1,
        'webmail'         => 1,
        'version_control' => 1,
        'postgres'        => 1
    );
    my $mocked_default_featurelist   = Test::MockFile->file( '/var/cpanel/features/default',   \%features );
    my $mocked_disabled_featurelist  = Test::MockFile->file( '/var/cpanel/features/disabled',  '' );
    my $mocked_custom_featurelist    = Test::MockFile->file( '/var/cpanel/features/custom',    \%features );
    my $mocked_mail_only_featurelist = Test::MockFile->file( "/var/cpanel/features/Mail Only", \%features );
    my $mocked_mail_only_flag        = Test::MockFile->file("/var/cpanel/plugins/sitejet/mail_only");

    my ( $feature_list, $feature, $value, $verbose );
    my %modified_feature_list;

    my @logged_warnings;
    local *Cpanel::Logger::warn = sub {
        @logged_warnings = @_;
    };

    my @mocks = (
        $self->mock_subs(
            'scripts::modify_featurelist::_verify_directories' => sub { return 1 },
            'scripts::modify_featurelist::modify_featurelist'  => sub {
                ( $feature_list, $feature, $value, $verbose ) = @_;
                $modified_feature_list{$feature_list}{$feature} = $value;
                return 1;
            },
        )
    );

    my @expected_feature_lists = ( 'custom', 'default', 'Mail Only' );

    my $modify            = scripts::modify_featurelist::modify_feature_for_all_feature_lists( 'sitejet', 1, 0 );
    my @modified_features = sort { fc($a) cmp fc($b) } keys %modified_feature_list;
    is( \@modified_features,                            \@expected_feature_lists, "modify_feature_for_all_feature_lists skips updating 'disabled' feature list as expected." );
    is( $modified_feature_list{'Mail Only'}{'sitejet'}, 0,                        "modify_feature_for_all_feature_lists disables 'sitejet' feature as expected in 'Mail Only' feature list." );
    is( $modified_feature_list{'default'}{'sitejet'},   1,                        "modify_feature_for_all_feature_lists enables 'sitejet' feature as expected in 'default' feature list." );
    is( $modify,                                        1,                        'modify does not fail' ) or diag explain $modify;

    # enable mail_only touch file to have feature enabled in Mail Only feature list
    $mocked_mail_only_flag = '';
    $mocked_mail_only_flag = Test::MockFile->file( "/var/cpanel/plugins/sitejet/mail_only", '' );
    $modify                = scripts::modify_featurelist::modify_feature_for_all_feature_lists( 'sitejet', 1, 0 );
    is( $modified_feature_list{'Mail Only'}{'sitejet'}, 1, "modify_feature_for_all_feature_lists enables 'sitejet' feature if we have a mail_only flag." );
    is( $modify,                                        1, 'modify does not fail' ) or diag explain $modify;
}

sub test_do_once : Tests(7) ($self) {
    my $touchfile_succeeds = 1;
    my $version            = "11.110_disable_sitejet";
    my $VERSION_DIR        = '/var/cpanel/version';

    my $mocked_lockfile = Test::MockFile->file( $VERSION_DIR . '/cpanel' . "$version" );

    my @mocks = (
        $self->mock_subs(
            'scripts::modify_featurelist::_verify_directories' => sub { return 1 },
            'Cpanel::FileUtils::TouchFile::touchfile'          => sub {
                $mocked_lockfile->touch() if $touchfile_succeeds;
                return $touchfile_succeeds;
            },
            'scripts::modify_featurelist::modify_feature_for_all_feature_lists' => sub { return 1 },
        )
    );

    # test invalid params
    my $invalid_params = scripts::modify_featurelist::do_once();
    is( $invalid_params, undef, 'returns early if no params' );

    # test happy path
    my $do_once = eval {
        scripts::modify_featurelist::do_once(
            'version' => "$version",
            'eol'     => 'never',
            'code'    => sub { scripts::modify_featurelist::modify_feature_for_all_feature_lists( 'sitejet', 1 ) },
        );
    };
    is( $do_once, 1, 'do_once executes and calls method to modify feature lists' );

    # test when the touchfile already exists
    $touchfile_succeeds = 1;
    my $ret_preexist_touchfile;
    ok(
        lives {
            $ret_preexist_touchfile = scripts::modify_featurelist::do_once(
                'version' => "$version",
                'eol'     => 'never',
                'code'    => sub { scripts::modify_featurelist::modify_feature_for_all_feature_lists( 'sitejet', 1 ) },
            );
        },
        "did not die, despite touchfile problem"
    ) or note($@);

    is( $ret_preexist_touchfile, undef, 'does nothing and returns undef if touchfile already exists' );

    # test when there's a problem creating the touchfile
    $mocked_lockfile->unlink();
    $touchfile_succeeds = 0;
    my $ret_no_touchfile;
    warnings_like {
        ok(
            lives {
                $ret_no_touchfile = scripts::modify_featurelist::do_once(
                    'version' => "$version",
                    'eol'     => 'never',
                    'code'    => sub { scripts::modify_featurelist::modify_feature_for_all_feature_lists( 'sitejet', 1 ) },
                );
            },
            "does not die, even if there's a problem creating the touchfile"
        ) or note($@);
    }
    [qr/Failed to touch.*version file/], "do_once warns and returns if touch file can't be created";

    is( $ret_no_touchfile, 1, 'return 1 if it fails to touch the touchfile successfully' );
}

sub test_run : Tests(3) ($self) {
    my $touchfile_succeeds = 1;
    my $VERSION_DIR        = '/var/cpanel/version';

    my $company_id_enabled_file_mock = $self->_get_file_mocks();
    my ( $feature, $value, $is_caller );
    my $is_called;
    my @mocks;
    my @warn;
    my $setup_run = sub ( $company_id, $version_name ) {

        # clear state
        $is_called = undef;
        @warn      = ();
        my $mock_file = Test::MockFile->file( $VERSION_DIR . '/cpanel' . $version_name );

        # my $mock_file2 = Test::MockFile->file( '/test.jsonc');
        @mocks = (
            $self->mock_subs(
                'Cpanel::Version::getversionnumber'                => sub { return '11.110.1'; },
                'scripts::modify_featurelist::_verify_directories' => sub { return 1 },
                'Cpanel::HelpfulScript::getopt'                    => sub {
                    my ( $self, $in ) = @_;
                    return 'doesnt_exist.jsonc' if $in eq 'config';
                    return 'sitejet'            if $in eq 'feature';
                    return 'enable'             if $in eq 'enable';
                },
                'scripts::modify_featurelist::modify_feature_for_all_feature_lists' => sub {
                    ( $feature, $value ) = @_;
                    $is_called = 1;
                    return 1;
                },
                'Cpanel::License::CompanyID::get_company_id' => sub { return $company_id },
                'Cpanel::FileUtils::TouchFile::touchfile'    => sub {
                    $mock_file->touch() if $touchfile_succeeds;
                    return $touchfile_succeeds;
                },
                'Cpanel::Logger::warn' => sub {
                    shift @_;
                    @warn = @_;
                    return;
                },
                'Common::JSONC::load_jsonc' => sub {
                    return {
                        "flag"      => "11.110_enable_sitejet",
                        "condition" => {
                            "compare-version" => {
                                "operator" => ">=",
                                "version"  => "11.110"
                            }
                        },
                        "all"     => 1,
                        "feature" => "sitejet",
                        "enable"  => 1
                    };
                }
            )
        );
        return @mocks;
    };
    {
        note "Test that the modify_featurelist script enables sitejet for all feature lists in 110 for selective company id's";

        @mocks = $setup_run->( 7, '11.110_enable_sitejet' );

        scripts::modify_featurelist->run();

        is( $feature,   'sitejet', "modify_featurelist script receives the right feature name" );
        is( $value,     1,         "modify_featurelist script enables sitejet feature" );
        is( $is_called, 1,         "modify_featurelist script does get called" );

        @mocks = ();
    }

}

sub _get_file_mocks ($self) {
    my $file               = '/var/cpanel/plugins/sitejet/.enabled_companyid_list.csv';
    my @allowed_company_id = (
        '251,LeaseWeb',
        '265,cPanel Direct',
        '336,The Constant Company LLC (Vultr)',
        '425,SoftLayer Technologies Inc.',
        '597,OVH',
        '758,WorldStream',
        '898,Home Ltd.',
        '1162,Hetzner',
        '1226,Ctrls Datacenters Ltd.',
        '1326,phoenixNAP Global IT Services',
        '2463,MilesWeb Internet Services',
        '5839,Name Hero LLC',
        ',missing company id',
        '99,',
        ','
    );
    my $file_contents = join( "\n", @allowed_company_id );
    my $mock_file     = Test::MockFile->file( $file, $file_contents );
    return $mock_file;
}
