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

#                                      Copyright 2024 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 v5.36;
use Test::More;

use experimental 'builtin';

use Whostmgr::360Monitoring::Util ();
use Whostmgr::360Monitoring::Agent;

my $plugins = Whostmgr::360Monitoring::Util::get_all_classes_under("Whostmgr::360Monitoring::Agent::Plugins");

# Somes classes have required attributes, for these to test instantiation
# supply the following values if required
my $garbage_data = {
    status_page_url => 'http://invalid.local.invalid/status_page',
    interfaces      => 'eth0',
    jail            => '/bin/dontrun',
    hosts           => 'cpanel.plays.minecraft.invalid',
    dirs            => '/srv/opt/secret/p12n',
    host_ports      => 'host.cpanel.net.invalid:80',
    host            => 'host.cpanel.net.invalid',
    password        => 'mypass',
    username        => 'myusername',
    timeout         => 60,
    port            => 31337,
};

sub _filter_required ($conf) { $conf->{required} // 0 == 1; }

foreach my $class ( $plugins->@* ) {
    subtest "Plugin $class" => sub {
        my $agent = new_ok('Whostmgr::360Monitoring::Agent');

        my $abs_path = Whostmgr::360Monitoring::Util::class_to_file($class);
        require_ok($abs_path);

        my $attrs               = Whostmgr::360Monitoring::Util::get_all_attrs($class);
        my $required_attributes = Whostmgr::360Monitoring::Util::get_all_attrs( $class, \&_filter_required );
        my %arguments_to_new;
        @arguments_to_new{ keys %$required_attributes } = @$garbage_data{ keys %$required_attributes };

        my $plugin;
        if (%$required_attributes) {
            $plugin = new_ok( $class, [ $agent, \%arguments_to_new ] );

            local $@;
            eval { $class->new( $agent, { required_arguments_missing => 1 } ); };
            my $err = $@;
            like(
                $err,
                qr/Missing required arguments/,
                sprintf( "Plugin requires %s, and it failed to create without it", join ', ', keys %$required_attributes )
            );
        }
        else {
            $plugin = new_ok( $class, [$agent] );
        }

        ok( $plugin isa "Whostmgr::360Monitoring::Agent::Plugins", "extends W:360:A:Plugins" );
        foreach my $role (qw/Enableable Runnable Serializable/) {
            ok(
                $plugin->does("Whostmgr::360Monitoring::Agent::Plugins::Roles::$role"),
                "does $role by way extending Whostmgr::360Monitoring::Agent::Plugins"
            );
        }

        foreach my $attr (qw/enabled ttl interval/) {
            ok( $plugin->can($attr), "can $attr" );
        }

        # the $table_name is typically the plugin name, but it's just the thing
        # between the `[]`
        my $table_name = $class;
        $table_name =~ s/.*:://;
        $table_name = lc($table_name);
        $agent->add_to_plugins( $table_name, \%arguments_to_new );
        my $plugin2 = $agent->get_plugin($table_name);
        is_deeply(
            $plugin->serialize,
            $plugin2->serialize,
            "Plugin serializes the same when loaded through add_to_plugins"
        );

        is( $agent->get_plugin($table_name)->enabled, 1, "[enabled] defaults right in code" );

        {
            my %attributes = %arguments_to_new;
            $attributes{enabled} = 'false';
            my $ini = gen_test_ini( $table_name, {}, \%attributes );

            my $agent = Whostmgr::360Monitoring::Agent::from_INI( \$ini );
            is( $agent->get_plugin($table_name)->enabled, 0, "[enabled] can be read from plugin's ini" );
        }

        {
            my %attributes = %arguments_to_new;
            $attributes{max_data_age} = 30;
            my $ini = gen_test_ini( $table_name, { interval => 60 }, \%attributes );

            my $agent = Whostmgr::360Monitoring::Agent::from_INI( \$ini );
            is( $agent->get_plugin($table_name)->interval, 60, "[interval] reads from global plugin defaults" );
        }

        {
            my %attributes = %arguments_to_new;
            $attributes{max_data_age} = 30;
            $attributes{interval}     = 60;
            my $ini = gen_test_ini( $table_name, { interval => 90 }, \%attributes );

            my $agent = Whostmgr::360Monitoring::Agent::from_INI( \$ini );
            my $p     = $agent->get_plugin($table_name);
            is( $p->name,     $table_name, '[name] set properly' );
            is( $p->interval, 60,          "[interval] plugin-specific ini will override DEFAULT" );
            $p->interval(120);
            is( $p->interval,                             120, "[interval] can change in the plugin" );
            is( $agent->settings_global_plugin->interval, 90,  "... without affecting the global plugin settings" );
            $p->interval(10);
            is( $p->interval,                             10, "[interval] can change in the plugin" );
            is( $agent->settings_global_plugin->interval, 90, "... without affecting the global plugin settings" );

            $agent->settings_global_plugin->interval(100);
            is( $agent->settings_global_plugin->interval,        100, "[interval] can be changed in global plugin settings" );
            is( scalar @{ [ $agent->to_INI =~ m/interval=/g ] }, 5,   "after we do that the INI has 5 copies of interval= plugin+global settings+Settings" );

            is( ref $agent->serialize->{DEFAULT},                   'HASH', "We have a HashRef for default section" );
            is( scalar @{ [ $agent->to_INI =~ m'\[DEFAULT\]'gs ] }, 1,      "... we have one [DEFAULT] section in INI" );
        }
    }
}

sub gen_test_ini ( $table_name, $global_plugin_options, $plugin_options ) {
    my $table_global_plugin_options = join "\n",
      '[DEFAULT]',
      map "$_ = $global_plugin_options->{$_}",
      keys %$global_plugin_options;

    my $table_plugin_options = join "\n",
      "[$table_name]",
      map "$_ = $plugin_options->{$_}",
      keys %$plugin_options;

    my @tables;
    push @tables, $table_global_plugin_options if %$global_plugin_options;
    push @tables, $table_plugin_options        if %$plugin_options;

    return join "\n\n", @tables;
}

done_testing();
