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

#                                      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.

package t::small::Cpanel::Exception::Sitejet::API;

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

use Test2::V0;

use Test::Exception;
use Test::MockObject;

use parent qw(
  Cpanel::TestObj
);

use Cpanel::Exception::Sitejet::API ();

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

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

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

sub runtests ( $self, @args ) {
    return $self->SUPER::runtests(@args);
}

sub test_exception_creation : Tests(3) ($self) {

    # Test basic exception creation
    my $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type => 'account_exists',
            user       => 'testuser'
        }
    );

    isa_ok( $exception, 'Cpanel::Exception::Sitejet::API' );
    isa_ok( $exception, 'Cpanel::Exception' );
    is( $exception->get('error_type'), 'account_exists', 'Exception stores error_type correctly' );
}

sub test_account_management_errors : Tests(5) ($self) {

    # Test account_exists error
    my $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type => 'account_exists',
            user       => 'testuser'
        }
    );

    my $phrase = $exception->_default_phrase();

    # The module returns a string directly, not a LocaleString object
    like( $phrase, qr/Sitejet account already exists for user "testuser"/, 'account_exists message correct' );

    # Test account_creation_failed error
    $exception = Cpanel::Exception::create( 'Sitejet::API', { error_type => 'account_creation_failed' } );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Failed to create a Sitejet account/, 'account_creation_failed message correct' );

    # Test token_not_found error
    $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type => 'token_not_found',
            user       => 'testuser'
        }
    );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/No Sitejet API token found for user "testuser"/, 'token_not_found message correct' );

    # Test with missing user parameter (should use 'unknown')
    $exception = Cpanel::Exception::create( 'Sitejet::API', { error_type => 'account_exists' } );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Sitejet account already exists for user "unknown"/, 'account_exists handles missing user' );

    # Test with empty user parameter (should use 'unknown')
    $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type => 'token_not_found',
            user       => ''
        }
    );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/No Sitejet API token found for user "unknown"/, 'token_not_found handles empty user' );
}

sub test_website_management_errors : Tests(5) ($self) {

    # Test website_id_missing error
    my $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type => 'website_id_missing',
            domain     => 'example.com'
        }
    );

    my $phrase = $exception->_default_phrase();
    like( $phrase, qr/Unable to obtain websiteId for domain "example\.com"/, 'website_id_missing message correct' );

    # Test website_assets_failed error with operation
    $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type => 'website_assets_failed',
            operation  => 'get',
            domain     => 'example.com'
        }
    );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Failed to get website assets for domain "example\.com"/, 'website_assets_failed message correct' );

    # Test website_not_found error
    $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type => 'website_not_found',
            domain     => 'example.com'
        }
    );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Website not found for domain "example\.com"/, 'website_not_found message correct' );

    # Test with missing domain parameter
    $exception = Cpanel::Exception::create( 'Sitejet::API', { error_type => 'website_id_missing' } );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Unable to obtain websiteId for domain "unknown"/, 'website_id_missing handles missing domain' );

    # Test website_assets_failed with missing operation
    $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type => 'website_assets_failed',
            domain     => 'example.com'
        }
    );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Failed to unknown website assets for domain "example\.com"/, 'website_assets_failed handles missing operation' );
}

sub test_api_communication_errors : Tests(4) ($self) {

    # Test request_failed error
    my $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type  => 'request_failed',
            endpoint    => '/website',
            status_code => 404,
            response    => 'Not Found'
        }
    );

    my $phrase = $exception->_default_phrase();
    like( $phrase, qr/The API call to "\/website" failed with error code "404" and response "Not Found"/, 'request_failed message correct' );

    # Test with missing parameters
    $exception = Cpanel::Exception::create( 'Sitejet::API', { error_type => 'request_failed' } );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/The API call to "unknown" failed with error code "unknown" and response "unknown"/, 'request_failed handles missing parameters' );

    # Test invalid_response error (not defined in module, should use fallback)
    $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type => 'invalid_response',
            endpoint   => '/website',
            details    => 'Invalid JSON'
        }
    );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Sitejet API error: invalid_response/, 'invalid_response uses fallback message' );

    # Test authentication_failed error (not defined in module, should use fallback)
    $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type => 'authentication_failed',
            user       => 'testuser'
        }
    );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Sitejet API error: authentication_failed/, 'authentication_failed uses fallback message' );
}

sub test_preview_and_sso_errors : Tests(4) ($self) {

    # Test preview_failed error
    my $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type => 'preview_failed',
            domain     => 'example.com'
        }
    );

    my $phrase = $exception->_default_phrase();
    like( $phrase, qr/Failed to get preview data for domain "example\.com"/, 'preview_failed message correct' );

    # Test sso_failed error
    $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type => 'sso_failed',
            domain     => 'example.com'
        }
    );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Failed to get SSO link for domain "example\.com"/, 'sso_failed message correct' );

    # Test with missing domain
    $exception = Cpanel::Exception::create( 'Sitejet::API', { error_type => 'preview_failed' } );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Failed to get preview data for domain "unknown"/, 'preview_failed handles missing domain' );

    # Test sso_failed with missing domain
    $exception = Cpanel::Exception::create( 'Sitejet::API', { error_type => 'sso_failed' } );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Failed to get SSO link for domain "unknown"/, 'sso_failed handles missing domain' );
}

sub test_template_management_errors : Tests(4) ($self) {

    # Test templates_unavailable error
    my $exception = Cpanel::Exception::create( 'Sitejet::API', { error_type => 'templates_unavailable' } );

    my $phrase = $exception->_default_phrase();
    like( $phrase, qr/Unable to access Sitejet templates/, 'templates_unavailable message correct' );

    # Test template_selection_failed error
    $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type => 'template_selection_failed',
            domain     => 'example.com'
        }
    );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Unable to set Sitejet template for "example\.com"/, 'template_selection_failed message correct' );

    # Test template_selection_failed with missing domain
    $exception = Cpanel::Exception::create( 'Sitejet::API', { error_type => 'template_selection_failed' } );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Unable to set Sitejet template for "unknown"/, 'template_selection_failed handles missing domain' );

    # Test templates_unavailable doesn't require parameters
    $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type  => 'templates_unavailable',
            extra_param => 'should_be_ignored'
        }
    );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Unable to access Sitejet templates/, 'templates_unavailable ignores extra parameters' );
}

sub test_access_permission_errors : Tests(7) ($self) {

    # Test migration_in_progress error
    my $exception = Cpanel::Exception::create( 'Sitejet::API', { error_type => 'migration_in_progress' } );

    my $phrase = $exception->_default_phrase();
    like( $phrase, qr/The publication process is not available during website migration/, 'migration_in_progress message correct' );

    # Test insufficient_permissions error
    $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type => 'insufficient_permissions',
            team_user  => 'teamuser'
        }
    );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/The Team User "teamuser" cannot access Sitejet/, 'insufficient_permissions message correct' );

    # Test domain_not_owned error
    $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type => 'domain_not_owned',
            domain     => 'example.com'
        }
    );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/The "example\.com" domain is not owned by this account/, 'domain_not_owned message correct' );

    # Test domains_fetch_failed error
    $exception = Cpanel::Exception::create( 'Sitejet::API', { error_type => 'domains_fetch_failed' } );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Unable to fetch all the domains/, 'domains_fetch_failed message correct' );

    # Test insufficient_permissions with missing team_user
    $exception = Cpanel::Exception::create( 'Sitejet::API', { error_type => 'insufficient_permissions' } );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/The Team User "unknown" cannot access Sitejet/, 'insufficient_permissions handles missing team_user' );

    # Test domain_not_owned with missing domain
    $exception = Cpanel::Exception::create( 'Sitejet::API', { error_type => 'domain_not_owned' } );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/The "unknown" domain is not owned by this account/, 'domain_not_owned handles missing domain' );

    # Test migration_in_progress doesn't require parameters
    $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type  => 'migration_in_progress',
            extra_param => 'ignored'
        }
    );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/The publication process is not available during website migration/, 'migration_in_progress ignores extra parameters' );
}

sub test_unknown_error_type : Tests(2) ($self) {

    # Test unknown error type
    my $exception = Cpanel::Exception::create( 'Sitejet::API', { error_type => 'unknown_error_type' } );

    my $phrase = $exception->_default_phrase();
    like( $phrase, qr/Sitejet API error: unknown_error_type/, 'unknown error type message correct' );

    # Test missing error_type (should default to 'unknown')
    $exception = Cpanel::Exception::create( 'Sitejet::API', {} );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Sitejet API error: unknown/, 'missing error_type defaults to unknown' );
}

sub test_network_error : Tests(2) ($self) {

    # Test network_error (not defined in module, should use fallback)
    my $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type => 'network_error',
            details    => 'Connection timeout'
        }
    );

    my $phrase = $exception->_default_phrase();
    like( $phrase, qr/Sitejet API error: network_error/, 'network_error uses fallback message' );

    # Test network_error with missing details
    $exception = Cpanel::Exception::create( 'Sitejet::API', { error_type => 'network_error' } );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Sitejet API error: network_error/, 'network_error handles missing details with fallback' );
}

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

    # Test that EXCEPTION_DEFINITIONS hash exists and has expected structure
    ok( exists $Cpanel::Exception::Sitejet::API::EXCEPTION_DEFINITIONS{'account_exists'},        'account_exists definition exists' );
    ok( exists $Cpanel::Exception::Sitejet::API::EXCEPTION_DEFINITIONS{'request_failed'},        'request_failed definition exists' );
    ok( exists $Cpanel::Exception::Sitejet::API::EXCEPTION_DEFINITIONS{'templates_unavailable'}, 'templates_unavailable definition exists' );

    # Test structure of a definition
    my $def = $Cpanel::Exception::Sitejet::API::EXCEPTION_DEFINITIONS{'account_exists'};
    ok( exists $def->{message}, 'Definition has message key' );
    ok( exists $def->{params},  'Definition has params key' );
    is( ref( $def->{params} ), 'ARRAY', 'Definition params is an array reference' );
}

sub test_translatable_function : Tests(2) ($self) {

    # Test translatable function
    my $test_string = 'Test message with [_1] placeholder';
    my $result      = Cpanel::Exception::Sitejet::API::translatable($test_string);

    is( $result, $test_string, 'translatable function returns input string unchanged' );

    # Test with empty string
    $result = Cpanel::Exception::Sitejet::API::translatable('');
    is( $result, '', 'translatable function handles empty string' );
}

sub test_edge_cases : Tests(4) ($self) {

    # Test exception with undefined error_type parameter
    my $exception = Cpanel::Exception::create( 'Sitejet::API', { error_type => undef } );

    my $phrase = $exception->_default_phrase();
    like( $phrase, qr/Sitejet API error: unknown/, 'undefined error_type handled correctly' );

    # Test exception with empty string error_type
    $exception = Cpanel::Exception::create( 'Sitejet::API', { error_type => '' } );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Sitejet API error: unknown/, 'empty error_type handled correctly' );

    # Test exception with numeric error_type
    $exception = Cpanel::Exception::create( 'Sitejet::API', { error_type => 123 } );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Sitejet API error: 123/, 'numeric error_type handled correctly' );

    # Test exception with special characters in parameters
    $exception = Cpanel::Exception::create(
        'Sitejet::API',
        {
            error_type => 'account_exists',
            user       => 'test@user.com'
        }
    );

    $phrase = $exception->_default_phrase();
    like( $phrase, qr/Sitejet account already exists for user "test\@user\.com"/, 'special characters in parameters handled correctly' );
}

1;
