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

# cpanel - t/medium/Cpanel-API-SiteQuality.t       Copyright 2024 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::Cpanel::API::SiteQuality;

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

use Test2::V0;
use Test2::Tools::Explain;
use Test2::Tools::Compare;
use Test2::Plugin::NoWarnings;
use Test2::Tools::Exception;
use Test::MockModule;
use Test::MockFile;

use Cpanel::Args   ();
use Cpanel::Result ();

use Cpanel::API::SiteQuality ();

use base qw(Test::Class);

my $mock_errorlog = Test::MockFile->file( '/usr/local/cpanel/logs/error_log', {} );

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

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

sub create_site_quality_user : Tests(7) {

    my $m_auth = Test::MockModule->new('Cpanel::Koality::Auth');
    $m_auth->redefine( 'new' => sub { return bless {}, shift } );

    my ( $app_token, $enabled, $username, $die ) = ( 'abcd1234', 1, 'kuser', 0 );
    $m_auth->redefine( 'create_user'  => sub { die 'Died' if $die; return bless { app_token => $app_token, enabled => $enabled, username => $username }, 'Cpanel::Koality::MockUser'; } );
    $m_auth->redefine( 'auth_session' => 1 );

    my $args;
    my $result = Cpanel::Result->new();

    $args = Cpanel::Args->new();
    ok( dies { Cpanel::API::SiteQuality::create_site_quality_user( $args, $result ) }, "Dies when the required attributes are not set." );

    $args = Cpanel::Args->new( { foobar => 'bingo' } );
    ok( dies { Cpanel::API::SiteQuality::create_site_quality_user( $args, $result ) }, "Dies when the required attributes are not set and junk is passed as args" );

    $args = Cpanel::Args->new( { password => 'password' } );
    ok( dies { Cpanel::API::SiteQuality::create_site_quality_user( $args, $result ) }, "Dies when the required attributes are not set and only optional args are passed" );

    $args = Cpanel::Args->new( { email => 'foo@bar.baz', password => 'password' } );
    ok( lives { Cpanel::API::SiteQuality::create_site_quality_user( $args, $result ) }, "Lives when the required attributes are set." );

    $args = Cpanel::Args->new( { email => 'foo@bar.baz' } );
    ok( lives { Cpanel::API::SiteQuality::create_site_quality_user( $args, $result ) }, "Lives when optional attributes are not set." );

    $args = Cpanel::Args->new( { email => 'foo@bar.baz' } );
    $die  = 1;
    ok( dies { Cpanel::API::SiteQuality::create_site_quality_user( $args, $result ) }, "Dies if create_user dies." );
    $die = 0;

    my $expected_hr = {
        app_token => 'abcd1234',
        enabled   => 1,
        username  => 'kuser',
    };

    $args = Cpanel::Args->new( { email => 'foo@bar.baz' } );
    Cpanel::API::SiteQuality::create_site_quality_user( $args, $result );
    is( $expected_hr, $result->data(), "Got expected returns with the happy path." );

    return;
}

sub delete_site_quality_user : Tests(9) {

    my ( $die, $deleted, $args ) = ( 0, );
    local $Cpanel::user = 'cp_user';

    my $m_auth = Test::MockModule->new('Cpanel::Koality::Auth');
    $m_auth->redefine( new      => sub { return bless {}, shift } );
    $m_auth->redefine( get_user => sub { return bless {}, 'Cpanel::Koality::User'; } );
    $m_auth->redefine(
        delete_user => sub {
            die if $die;
            return $deleted;
        }
    );

    my $result = Cpanel::Result->new();

    $deleted = 1;
    $args    = Cpanel::Args->new();
    ok( lives { Cpanel::API::SiteQuality::delete_site_quality_user( $args, $result ) }, "Lives when no arguments are set." );
    is( $result->data->{deleted}, 1, "Got expected result for deleted." );

    $args = Cpanel::Args->new( { some => 'junk' } );
    ok( lives { Cpanel::API::SiteQuality::delete_site_quality_user( $args, $result ) }, "Lives when junk arguments are passed in." );
    is( $result->data->{deleted}, 1, "Got expected result for deleted." );

    $deleted = 0;
    ok( lives { Cpanel::API::SiteQuality::delete_site_quality_user( $args, $result ) }, "Lives if the account was not deleted." );
    is( $result->data->{deleted}, 0, "Got expected result for deleted." );
    is( $result->errors->@*,      1, "... but get an error about it." );

    $die = 1;
    ok( dies { Cpanel::API::SiteQuality::delete_site_quality_user( $args, $result ) }, "Dies if the backend call dies." );
    is( $result->errors->@*, 1, "... and get an error about it." );

    return 1;
}

sub is_site_quality_user_enabled : Tests(5) {

    my ( $die, $enabled ) = 0;

    my $m_auth = Test::MockModule->new('Cpanel::Koality::Auth');
    $m_auth->redefine( 'new' => sub { return bless {}, shift } );
    $m_auth->redefine( 'get_user' => sub { die 'Died' if $die; return bless { enabled => $enabled }, 'Cpanel::Koality::MockUser'; } );

    my $args;
    my $result = Cpanel::Result->new();

    $enabled = 1;
    $args    = Cpanel::Args->new();
    ok( lives { Cpanel::API::SiteQuality::is_site_quality_user_enabled( $args, $result ) }, "Lives when no arguments are set." );

    is( $result->data->{enabled}, 1, "Got expected result for enabled." );

    $enabled = 0;
    $args    = Cpanel::Args->new( { some => 'junk' } );
    ok( lives { Cpanel::API::SiteQuality::is_site_quality_user_enabled( $args, $result ) }, "Lives when junk arguments are passed in." );
    is( $result->data->{enabled}, 0, "Got expected result for enabled." );

    $die = 1;
    Cpanel::API::SiteQuality::is_site_quality_user_enabled( $args, $result );
    is( $result->errors->@*, 1, "Got errors when the backend dies." );

    return 1;
}

sub get_app_token : Tests(5) {

    my ( $die, $app_token ) = ( 0, '' );
    local $Cpanel::user = 'cp_user';

    my $mock_file = Test::MockFile->file( '/var/cpanel/use_koality_stage', undef );

    my $mock_base = Test::MockModule->new('Cpanel::Koality::Base');
    $mock_base->redefine( _build__check_privs => sub ($) { return 1; } );

    my $m_val = Test::MockModule->new('Cpanel::Validate::Username');
    $m_val->redefine( 'user_exists_or_die' => sub { return 1 } );

    my $m_user = Test::MockModule->new('Cpanel::Koality::User');
    $m_user->redefine(
        'new' => sub {
            die 'Died' if $die;
            return bless { app_token => $app_token }, 'Cpanel::Koality::MockUser';
        }
    );

    my $args;
    my $result = Cpanel::Result->new();

    $app_token = 'abcd1234';
    $args      = Cpanel::Args->new();
    ok( lives { Cpanel::API::SiteQuality::get_app_token( $args, $result ) }, "Lives when no arguments are set." );
    is( $result->data->{app_token}, 'abcd1234', "Got expected result for session_token." );

    $app_token = '1234abcd';
    $args      = Cpanel::Args->new( { some => 'junk' } );
    ok( lives { Cpanel::API::SiteQuality::get_app_token( $args, $result ) }, "Lives when junk arguments are passed in." );
    is( $result->data->{app_token}, '1234abcd', "Got expected result for session_token." );

    my $mock_locale_user = Test::MockModule->new('Cpanel::Locale::Utils::User');
    $mock_locale_user->redefine(
        'get_user_locale' => sub {
            return 'en';
        }
    );

    my $mock_companyid = Test::MockModule->new('Cpanel::License::CompanyID');
    $mock_companyid->redefine(
        'get_company_id' => sub {
            return 42;
        }
    );

    my $mock_serverid = Test::MockModule->new('Cpanel::Plugins::ServerId');
    $mock_serverid->redefine(
        'get_server_id' => sub {
            return "010101";
        }
    );

    $die = 1;
    Cpanel::API::SiteQuality::is_site_quality_user_enabled( $args, $result );
    is( $result->errors->@*, 1, "Got errors when the backend dies." );

    return 1;
}

sub has_site_quality_user : Tests(6) {

    my ( $die, $app_token ) = ( 0, '' );

    local $Cpanel::user = 'cp_user';
    my $m_val = Test::MockModule->new('Cpanel::Validate::Username');
    $m_val->redefine( 'user_exists_or_die' => sub { return 1 } );

    my $m_user = Test::MockModule->new('Cpanel::Koality::User');
    $m_user->redefine(
        'new' => sub {
            die 'Died' if $die;
            return bless { app_token => $app_token }, 'Cpanel::Koality::MockUser';
        }
    );

    my $args;
    my $result = Cpanel::Result->new();

    $app_token = 'abcd1234';
    $args      = Cpanel::Args->new();
    ok( lives { Cpanel::API::SiteQuality::has_site_quality_user( $args, $result ) }, "Lives when no arguments are set." );
    is( $result->data->{has_site_quality_user}, '1', "Got expected result when user exists." );

    $app_token = undef;
    $args      = Cpanel::Args->new();
    ok( lives { Cpanel::API::SiteQuality::has_site_quality_user( $args, $result ) }, "Lives when no arguments are set." );
    is( $result->data->{has_site_quality_user}, '0', "Got expected result when user does not exist." );

    $app_token = undef;
    $args      = Cpanel::Args->new();
    $die       = 1;
    ok( lives { Cpanel::API::SiteQuality::has_site_quality_user( $args, $result ) }, "Lives when no arguments are set and backend dies." );
    is( $result->errors->@*, 1, "Got errors when the backend dies." );

    return 1;
}

sub get_environment : Tests(4) {
    local $Cpanel::user = 'fake';
    my $mock_file = Test::MockFile->file( '/var/cpanel/use_koality_stage', undef );

    my $mock_validation = Test::MockModule->new('Cpanel::Validate::Username');
    $mock_validation->redefine( user_exists_or_die => sub { } );

    my $mock_base = Test::MockModule->new('Cpanel::Koality::Base');
    $mock_base->redefine( _build__check_privs => sub ($) { return 1; } );

    my $mock_companyid = Test::MockModule->new('Cpanel::License::CompanyID');
    $mock_companyid->redefine(
        'get_company_id' => sub {
            return 42;
        }
    );

    my $mock_serverid = Test::MockModule->new('Cpanel::Plugins::ServerId');
    $mock_serverid->redefine(
        'get_server_id' => sub {
            return "010101";
        }
    );

    my $args;
    my $result = Cpanel::Result->new();

    $args = Cpanel::Args->new();
    ok( Cpanel::API::SiteQuality::get_environment( $args, $result ), "returns success" );
    is( $result->data->{environment}, 'production', "Got production environment when the touch file is missing." );

    $mock_file->touch();
    $args = Cpanel::Args->new();
    ok( Cpanel::API::SiteQuality::get_environment( $args, $result ), "returns success" );
    is( $result->data->{environment}, 'staging', "Got staging environment when the touch file exists." );

    return 1;
}

sub update_domain : Tests(13) {

    local $Cpanel::user = 'cp_user';

    my $mock_validation = Test::MockModule->new('Cpanel::Validate::Username');
    $mock_validation->redefine( user_exists_or_die => sub { } );

    my $mock_locale = Test::MockModule->new('Cpanel::Locale::Utils::User');
    $mock_locale->redefine( 'get_user_locale' => sub { return 'en' } );

    my $mock_companyid = Test::MockModule->new('Cpanel::License::CompanyID');
    $mock_companyid->redefine( 'get_company_id' => sub { return 42 } );

    my $mock_serverid = Test::MockModule->new('Cpanel::Plugins::ServerId');
    $mock_serverid->redefine( 'get_server_id' => sub { return "010101" } );

    # Mock locale function for maketext calls
    no warnings 'redefine';
    *Cpanel::API::SiteQuality::locale = sub {
        return bless {}, 'MockLocale';
    };

    package MockLocale;
    sub maketext {
        my ($self, $text, @args) = @_;
        # Simple substitution for [_1], [_2], etc.
        for my $i (0..$#args) {
            my $placeholder = '[_' . ($i + 1) . ']';
            $text =~ s/\Q$placeholder\E/$args[$i]/g;
        }
        return $text;
    }
    package t::medium::Cpanel::API::SiteQuality;

    # Mock variables for controlling test behavior
    my ( $auth_die, $proj_die, $found_system, $update_response, $all_projects );

    # Mock Auth module
    my $mock_auth = Test::MockModule->new('Cpanel::Koality::Auth');
    $mock_auth->redefine(
        'new' => sub {
            die 'Auth failed' if $auth_die;
            return bless {}, shift;
        }
    );
    $mock_auth->redefine( 'auth_session' => sub { return bless {}, 'Cpanel::Koality::User' } );

    # Mock Project module
    my $mock_project = Test::MockModule->new('Cpanel::Koality::Project');
    $mock_project->redefine(
        'new' => sub {
            die 'Project creation failed' if $proj_die;
            return bless {}, shift;
        }
    );
    $mock_project->redefine( 'get_all_projects' => sub { return $all_projects } );
    $mock_project->redefine( 'update_domain'    => sub { return $update_response } );
    $mock_project->redefine( 'update_project'   => sub { return { status => 'success' } } );

    my $args;
    my $result = Cpanel::Result->new();

    # Test 1: Missing required old_domain parameter
    $args = Cpanel::Args->new();
    ok(
        dies { Cpanel::API::SiteQuality::update_domain( $args, $result ) },
        "Dies when old_domain parameter is missing"
    );

    # Test 2: Missing required new_domain parameter
    $args = Cpanel::Args->new( { old_domain => 'test.com' } );
    ok(
        dies { Cpanel::API::SiteQuality::update_domain( $args, $result ) },
        "Dies when new_domain parameter is missing"
    );

    # Test 3: Empty old_domain parameter
    $args = Cpanel::Args->new( { old_domain => '', new_domain => 'new.com' } );
    ok(
        dies { Cpanel::API::SiteQuality::update_domain( $args, $result ) },
        "Dies when old_domain parameter is empty"
    );

    # Test 4: Empty new_domain parameter
    $args = Cpanel::Args->new( { old_domain => 'old.com', new_domain => '' } );
    ok(
        dies { Cpanel::API::SiteQuality::update_domain( $args, $result ) },
        "Dies when new_domain parameter is empty"
    );

    # Reset test variables for success scenarios
    $auth_die = 0;
    $proj_die = 0;

    # Test 5: No matching system found
    $all_projects = { '123' => { name => 'different.com', systems => [ { id => 456, domain => 'https://different.com', name => 'different.com' } ] } };
    $args         = Cpanel::Args->new( { old_domain => 'notfound.com', new_domain => 'new.com' } );
    $result       = Cpanel::Result->new();                                                                                       # Fresh result object
    my $no_match_result = Cpanel::API::SiteQuality::update_domain( $args, $result );
    is( $no_match_result, undef, "Returns undef when no matching system is found" );
    is(
        $result->messages->[0],
        'No Site Quality Monitoring system found for domain notfound.com.',
        "Sets appropriate message when no system found"
    );

    # Test 6: Successful domain update
    $all_projects    = { '789' => { name => 'olddomain.com', systems => [ { id => 623358, domain => 'https://olddomain.com', name => 'olddomain.com' } ] } };
    $update_response = {
        data => {
            system => {
                id          => 623358,
                name        => 'newdomain.com',
                system_size => { id => 3, name => 'Unlimited lite' },
                url         => 'https://newdomain.com'
            }
        }
    };

    $args   = Cpanel::Args->new( { old_domain => 'olddomain.com', new_domain => 'newdomain.com' } );
    $result = Cpanel::Result->new();                                                                   # Fresh result object
    my $success_result = Cpanel::API::SiteQuality::update_domain( $args, $result );
    is( $success_result,               1,               "Returns 1 on successful update" );
    is( $result->data->{system}{id},   623358,          "Returns correct system data" );
    is( $result->data->{system}{name}, 'newdomain.com', "Returns updated system name" );
    is(
        $result->messages->[0],
        'The domain for the Site Quality Monitoring project has been updated from olddomain.com to newdomain.com.',
        "Sets appropriate success message"
    );

    # Test 7: Auth module failure
    $auth_die = 1;
    $args     = Cpanel::Args->new( { old_domain => 'test.com', new_domain => 'new.com' } );
    ok(
        dies { Cpanel::API::SiteQuality::update_domain( $args, $result ) },
        "Dies when Auth module fails"
    );

    # Test 8: Project module failure
    $auth_die = 0;
    $proj_die = 1;
    $args     = Cpanel::Args->new( { old_domain => 'test.com', new_domain => 'new.com' } );
    ok(
        dies { Cpanel::API::SiteQuality::update_domain( $args, $result ) },
        "Dies when Project module fails"
    );

    return 1;
}

1;

package Cpanel::Koality::MockUser;

sub new ($args) {
    return bless $args, 'Cpanel::Koality::MockUser';
}

sub app_token ($self) {
    return $self->{app_token};
}

sub enabled ($self) {
    return $self->{enabled};
}

sub koality_username ($self) {
    return $self->{username};
}

sub session_token ($self) {
    return $self->{session_token};
}

1;
