/**
 * @description Test class for DocusignCredentials
 * @author Paul Huliganga
 * @date 2026-02-23
 */
@isTest
private class DocusignCredentialsTest {
    
    /**
     * @description Setup test data
     */
    @testSetup
    static void setup() {
        // Create Custom Setting for credentials
        Docusign_Configuration__c config = new Docusign_Configuration__c();
        config.Account_Id__c = 'test-account-id-12345';
        config.Base_URL__c = 'callout:DocusignAPI';
        insert config;
    }
    
    /**
     * @description Test singleton pattern
     */
    @isTest
    static void testGetInstance() {
        // Act
        Test.startTest();
        DocusignCredentials creds1 = DocusignCredentials.getInstance();
        DocusignCredentials creds2 = DocusignCredentials.getInstance();
        Test.stopTest();
        
        // Assert
        System.assertEquals(creds1, creds2, 'Should return same instance (singleton)');
    }
    
    /**
     * @description Test loading credentials from Custom Settings
     */
    @isTest
    static void testLoadCredentialsFromCustomSettings() {
        // Reset instance to force fresh load from Custom Settings
        DocusignCredentials.resetInstance();
        
        // Act
        Test.startTest();
        DocusignCredentials creds = DocusignCredentials.getInstance();
        Test.stopTest();
        
        // Assert
        System.assertEquals('test-account-id-12345', creds.getAccountId(), 'Should load account ID');
        System.assertEquals('callout:DocusignAPI', creds.getBaseUrl(), 'Should load base URL');
        System.assertNotEquals(null, creds.getAccessToken(), 'Should have access token');
    }
    
    /**
     * @description Test getAccessToken method
     */
    @isTest
    static void testGetAccessToken() {
        // Arrange
        DocusignCredentials.resetInstance();
        
        // Act
        Test.startTest();
        DocusignCredentials creds = DocusignCredentials.getInstance();
        String token = creds.getAccessToken();
        Test.stopTest();
        
        // Assert
        System.assertNotEquals(null, token, 'Should return access token');
    }
    
    /**
     * @description Test getAccountId method
     */
    @isTest
    static void testGetAccountId() {
        // Reset instance to force fresh load from Custom Settings
        DocusignCredentials.resetInstance();
        
        // Act
        Test.startTest();
        DocusignCredentials creds = DocusignCredentials.getInstance();
        String accountId = creds.getAccountId();
        Test.stopTest();
        
        // Assert
        System.assertEquals('test-account-id-12345', accountId, 'Should return account ID');
    }
    
    /**
     * @description Test getBaseUrl method
     */
    @isTest
    static void testGetBaseUrl() {
        // Act
        Test.startTest();
        DocusignCredentials creds = DocusignCredentials.getInstance();
        String baseUrl = creds.getBaseUrl();
        Test.stopTest();
        
        // Assert
        System.assertEquals('callout:DocusignAPI', baseUrl, 'Should return base URL');
    }
    
    /**
     * @description Test validate method with valid credentials
     */
    @isTest
    static void testValidateSuccess() {
        // Act
        Test.startTest();
        DocusignCredentials creds = DocusignCredentials.getInstance();
        Boolean isValid = creds.validate();
        Test.stopTest();
        
        // Assert
        System.assertEquals(true, isValid, 'Should validate successfully');
    }
    
    /**
     * @description Test validate method with missing account ID
     */
    @isTest
    static void testValidateMissingAccountId() {
        // Arrange
        DocusignCredentials.resetInstance();
        DocusignCredentials.setTestCredentials('', 'callout:DocusignAPI', 'test-token');
        
        // Act & Assert
        Test.startTest();
        try {
            DocusignCredentials creds = DocusignCredentials.getInstance();
            creds.validate();
            System.assert(false, 'Should have thrown exception');
        } catch (DocusignCredentials.CredentialException e) {
            System.assert(e.getMessage().contains('Account ID'), 'Should mention Account ID');
        }
        Test.stopTest();
    }
    
    /**
     * @description Test validate method with missing base URL
     */
    @isTest
    static void testValidateMissingBaseUrl() {
        // Arrange
        DocusignCredentials.resetInstance();
        DocusignCredentials.setTestCredentials('test-account-id', '', 'test-token');
        
        // Act & Assert
        Test.startTest();
        try {
            DocusignCredentials creds = DocusignCredentials.getInstance();
            creds.validate();
            System.assert(false, 'Should have thrown exception');
        } catch (DocusignCredentials.CredentialException e) {
            System.assert(e.getMessage().contains('Base URL'), 'Should mention Base URL');
        }
        Test.stopTest();
    }
    
    /**
     * @description Test validate method with missing access token
     */
    @isTest
    static void testValidateMissingAccessToken() {
        // Arrange
        DocusignCredentials.resetInstance();
        DocusignCredentials.setTestCredentials('test-account-id', 'callout:DocusignAPI', '');
        
        // Act & Assert
        Test.startTest();
        try {
            DocusignCredentials creds = DocusignCredentials.getInstance();
            creds.validate();
            System.assert(false, 'Should have thrown exception');
        } catch (DocusignCredentials.CredentialException e) {
            System.assert(e.getMessage().contains('Access Token'), 'Should mention Access Token');
        }
        Test.stopTest();
    }
    
    /**
     * @description Test setTestCredentials method
     */
    @isTest
    static void testSetTestCredentials() {
        // Arrange
        DocusignCredentials.resetInstance();
        
        // Act
        Test.startTest();
        DocusignCredentials.setTestCredentials('test-acc-123', 'https://test.docusign.net', 'test-token-xyz');
        DocusignCredentials creds = DocusignCredentials.getInstance();
        Test.stopTest();
        
        // Assert
        System.assertEquals('test-acc-123', creds.getAccountId(), 'Should set test account ID');
        System.assertEquals('https://test.docusign.net', creds.getBaseUrl(), 'Should set test base URL');
        System.assertEquals('test-token-xyz', creds.getAccessToken(), 'Should set test access token');
    }
    
    /**
     * @description Test resetInstance method
     */
    @isTest
    static void testResetInstance() {
        // Arrange
        DocusignCredentials creds1 = DocusignCredentials.getInstance();
        
        // Act
        Test.startTest();
        DocusignCredentials.resetInstance();
        DocusignCredentials creds2 = DocusignCredentials.getInstance();
        Test.stopTest();
        
        // Assert
        System.assertNotEquals(creds1, creds2, 'Should create new instance after reset');
    }
    
    /**
     * @description Test error when Custom Settings not configured
     */
    @isTest
    static void testMissingCustomSettings() {
        // Arrange
        // Reset instance first
        DocusignCredentials.resetInstance();
        // Delete the test setup data
        delete [SELECT Id FROM Docusign_Configuration__c];
        
        // Act & Assert
        Test.startTest();
        try {
            DocusignCredentials creds = DocusignCredentials.getInstance();
            System.assert(false, 'Should have thrown exception');
        } catch (DocusignCredentials.CredentialException e) {
            System.assert(e.getMessage().containsIgnoreCase('not configured'), 'Should mention not configured');
        }
        Test.stopTest();
    }
    
    /**
     * @description Test Named Credential URL format
     */
    @isTest
    static void testNamedCredentialFormat() {
        // Act
        Test.startTest();
        DocusignCredentials creds = DocusignCredentials.getInstance();
        String baseUrl = creds.getBaseUrl();
        Test.stopTest();
        
        // Assert
        System.assert(baseUrl.startsWith('callout:'), 'Named Credential URL should start with callout:');
    }
}
