/**
 * @description Test class for DocusignCompositeEnvelopeBuilder
 * @author Paul Huliganga
 * @date 2026-02-23
 */
@isTest
private class DocusignCompositeEnvelopeBuilderTest {
    
    /**
     * @description Mock HTTP callout for Docusign API
     */
    private class DocusignMock implements HttpCalloutMock {
        private Integer statusCode;
        private String responseBody;
        
        public DocusignMock(Integer statusCode, String responseBody) {
            this.statusCode = statusCode;
            this.responseBody = responseBody;
        }
        
        public HTTPResponse respond(HTTPRequest req) {
            HttpResponse res = new HttpResponse();
            res.setStatusCode(this.statusCode);
            res.setBody(this.responseBody);
            res.setStatus(this.statusCode == 201 ? 'Created' : 'Error');
            return res;
        }
    }
    
    /**
     * @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 successful envelope creation with 3 templates
     */
    @isTest
    static void testSuccessfulEnvelopeCreation() {
        // Arrange
        String envelopeId = 'envelope-12345-abcde';
        String mockResponse = '{"envelopeId":"' + envelopeId + '","status":"sent"}';
        Test.setMock(HttpCalloutMock.class, new DocusignMock(201, mockResponse));
        
        DocusignCredentials.setTestCredentials('test-account-id', 'callout:DocusignAPI', 'test-token');
        
        DocusignCompositeEnvelopeBuilder.Request req = new DocusignCompositeEnvelopeBuilder.Request();
        req.templateIds = new List<String>{'template-1', 'template-2', 'template-3'};
        req.recordId = '001000000ABC123';
        req.language = 'en';
        req.emailSubject = 'Test Envelope';
        
        // Act
        Test.startTest();
        List<DocusignCompositeEnvelopeBuilder.Result> results = 
            DocusignCompositeEnvelopeBuilder.sendCompositeEnvelope(new List<DocusignCompositeEnvelopeBuilder.Request>{req});
        Test.stopTest();
        
        // Assert
        System.assertEquals(1, results.size(), 'Should return 1 result');
        System.assertEquals(true, results[0].success, 'Should be successful');
        System.assertEquals(envelopeId, results[0].envelopeId, 'Should return envelope ID');
        System.assertEquals(null, results[0].errorMessage, 'Should have no error message');
    }
    
    /**
     * @description Test with single template (minimum)
     */
    @isTest
    static void testSingleTemplate() {
        // Arrange
        String mockResponse = '{"envelopeId":"envelope-single","status":"sent"}';
        Test.setMock(HttpCalloutMock.class, new DocusignMock(201, mockResponse));
        
        DocusignCredentials.setTestCredentials('test-account-id', 'callout:DocusignAPI', 'test-token');
        
        DocusignCompositeEnvelopeBuilder.Request req = new DocusignCompositeEnvelopeBuilder.Request();
        req.templateIds = new List<String>{'template-1'};
        req.recordId = '001000000ABC123';
        
        // Act
        Test.startTest();
        List<DocusignCompositeEnvelopeBuilder.Result> results = 
            DocusignCompositeEnvelopeBuilder.sendCompositeEnvelope(new List<DocusignCompositeEnvelopeBuilder.Request>{req});
        Test.stopTest();
        
        // Assert
        System.assertEquals(true, results[0].success, 'Should be successful with 1 template');
    }
    
    /**
     * @description Test with 14 templates (maximum)
     */
    @isTest
    static void testMaximumTemplates() {
        // Arrange
        String mockResponse = '{"envelopeId":"envelope-max","status":"sent"}';
        Test.setMock(HttpCalloutMock.class, new DocusignMock(201, mockResponse));
        
        DocusignCredentials.setTestCredentials('test-account-id', 'callout:DocusignAPI', 'test-token');
        
        List<String> templateIds = new List<String>();
        for (Integer i = 1; i <= 14; i++) {
            templateIds.add('template-' + i);
        }
        
        DocusignCompositeEnvelopeBuilder.Request req = new DocusignCompositeEnvelopeBuilder.Request();
        req.templateIds = templateIds;
        req.recordId = '001000000ABC123';
        
        // Act
        Test.startTest();
        List<DocusignCompositeEnvelopeBuilder.Result> results = 
            DocusignCompositeEnvelopeBuilder.sendCompositeEnvelope(new List<DocusignCompositeEnvelopeBuilder.Request>{req});
        Test.stopTest();
        
        // Assert
        System.assertEquals(true, results[0].success, 'Should be successful with 14 templates');
    }
    
    /**
     * @description Test with duplicate template IDs (should be deduplicated)
     */
    @isTest
    static void testDuplicateTemplates() {
        // Arrange
        String mockResponse = '{"envelopeId":"envelope-dedup","status":"sent"}';
        Test.setMock(HttpCalloutMock.class, new DocusignMock(201, mockResponse));
        
        DocusignCredentials.setTestCredentials('test-account-id', 'callout:DocusignAPI', 'test-token');
        
        DocusignCompositeEnvelopeBuilder.Request req = new DocusignCompositeEnvelopeBuilder.Request();
        req.templateIds = new List<String>{'template-1', 'template-2', 'template-1'}; // Duplicate
        req.recordId = '001000000ABC123';
        
        // Act
        Test.startTest();
        List<DocusignCompositeEnvelopeBuilder.Result> results = 
            DocusignCompositeEnvelopeBuilder.sendCompositeEnvelope(new List<DocusignCompositeEnvelopeBuilder.Request>{req});
        Test.stopTest();
        
        // Assert
        System.assertEquals(true, results[0].success, 'Should handle duplicates');
    }
    
    // Custom fields test removed - not supported in InvocableVariable (Phase 2 enhancement)
    
    /**
     * @description Test validation failure - no template IDs
     */
    @isTest
    static void testValidationNoTemplates() {
        // Arrange
        DocusignCredentials.setTestCredentials('test-account-id', 'callout:DocusignAPI', 'test-token');
        
        DocusignCompositeEnvelopeBuilder.Request req = new DocusignCompositeEnvelopeBuilder.Request();
        req.templateIds = new List<String>(); // Empty
        req.recordId = '001000000ABC123';
        
        // Act
        Test.startTest();
        List<DocusignCompositeEnvelopeBuilder.Result> results = 
            DocusignCompositeEnvelopeBuilder.sendCompositeEnvelope(new List<DocusignCompositeEnvelopeBuilder.Request>{req});
        Test.stopTest();
        
        // Assert
        System.assertEquals(false, results[0].success, 'Should fail validation');
        System.assert(results[0].errorMessage.containsIgnoreCase('template'), 'Error should mention templates');
    }
    
    /**
     * @description Test validation failure - too many templates
     */
    @isTest
    static void testValidationTooManyTemplates() {
        // Arrange
        DocusignCredentials.setTestCredentials('test-account-id', 'callout:DocusignAPI', 'test-token');
        
        List<String> templateIds = new List<String>();
        for (Integer i = 1; i <= 15; i++) {
            templateIds.add('template-' + i);
        }
        
        DocusignCompositeEnvelopeBuilder.Request req = new DocusignCompositeEnvelopeBuilder.Request();
        req.templateIds = templateIds; // 15 templates (> max of 14)
        req.recordId = '001000000ABC123';
        
        // Act
        Test.startTest();
        List<DocusignCompositeEnvelopeBuilder.Result> results = 
            DocusignCompositeEnvelopeBuilder.sendCompositeEnvelope(new List<DocusignCompositeEnvelopeBuilder.Request>{req});
        Test.stopTest();
        
        // Assert
        System.assertEquals(false, results[0].success, 'Should fail validation');
        System.assert(results[0].errorMessage.contains('Maximum 14'), 'Error should mention limit');
    }
    
    /**
     * @description Test validation failure - no record ID
     */
    @isTest
    static void testValidationNoRecordId() {
        // Arrange
        DocusignCredentials.setTestCredentials('test-account-id', 'callout:DocusignAPI', 'test-token');
        
        DocusignCompositeEnvelopeBuilder.Request req = new DocusignCompositeEnvelopeBuilder.Request();
        req.templateIds = new List<String>{'template-1'};
        req.recordId = ''; // Blank
        
        // Act
        Test.startTest();
        List<DocusignCompositeEnvelopeBuilder.Result> results = 
            DocusignCompositeEnvelopeBuilder.sendCompositeEnvelope(new List<DocusignCompositeEnvelopeBuilder.Request>{req});
        Test.stopTest();
        
        // Assert
        System.assertEquals(false, results[0].success, 'Should fail validation');
        System.assert(results[0].errorMessage.contains('record ID'), 'Error should mention record ID');
    }
    
    /**
     * @description Test API error - 400 Bad Request
     */
    @isTest
    static void testAPIError400() {
        // Arrange
        String mockResponse = '{"errorCode":"INVALID_REQUEST_PARAMETER","message":"Invalid template ID"}';
        Test.setMock(HttpCalloutMock.class, new DocusignMock(400, mockResponse));
        
        DocusignCredentials.setTestCredentials('test-account-id', 'callout:DocusignAPI', 'test-token');
        
        DocusignCompositeEnvelopeBuilder.Request req = new DocusignCompositeEnvelopeBuilder.Request();
        req.templateIds = new List<String>{'invalid-template'};
        req.recordId = '001000000ABC123';
        
        // Act
        Test.startTest();
        List<DocusignCompositeEnvelopeBuilder.Result> results = 
            DocusignCompositeEnvelopeBuilder.sendCompositeEnvelope(new List<DocusignCompositeEnvelopeBuilder.Request>{req});
        Test.stopTest();
        
        // Assert
        System.assertEquals(false, results[0].success, 'Should fail with API error');
        System.assert(results[0].errorMessage.contains('400'), 'Error should include status code');
    }
    
    /**
     * @description Test API error - 401 Unauthorized
     */
    @isTest
    static void testAPIError401() {
        // Arrange
        String mockResponse = '{"errorCode":"USER_AUTHENTICATION_FAILED","message":"Invalid token"}';
        Test.setMock(HttpCalloutMock.class, new DocusignMock(401, mockResponse));
        
        DocusignCredentials.setTestCredentials('test-account-id', 'callout:DocusignAPI', 'invalid-token');
        
        DocusignCompositeEnvelopeBuilder.Request req = new DocusignCompositeEnvelopeBuilder.Request();
        req.templateIds = new List<String>{'template-1'};
        req.recordId = '001000000ABC123';
        
        // Act
        Test.startTest();
        List<DocusignCompositeEnvelopeBuilder.Result> results = 
            DocusignCompositeEnvelopeBuilder.sendCompositeEnvelope(new List<DocusignCompositeEnvelopeBuilder.Request>{req});
        Test.stopTest();
        
        // Assert
        System.assertEquals(false, results[0].success, 'Should fail with auth error');
        System.assertNotEquals(null, results[0].errorMessage, 'Should have error message');
        System.assert(String.isNotBlank(results[0].errorMessage), 'Error message should not be blank');
    }
    
    /**
     * @description Test JSON builder with all parameters
     */
    @isTest
    static void testJSONBuilder() {
        // Arrange
        List<String> templateIds = new List<String>{'template-A', 'template-B'};
        String recordId = '001000000ABC123';
        String language = 'es';
        String emailSubject = 'Custom Subject';
        
        // Act
        Test.startTest();
        String json = DocusignCompositeEnvelopeBuilder.buildCompositeEnvelopeJSON(
            templateIds,
            recordId,
            language,
            emailSubject,
            null  // customFields - not supported in Phase 1
        );
        Test.stopTest();
        
        // Assert
        System.assertNotEquals(null, json, 'JSON should be generated');
        System.assert(json.contains('template-A'), 'Should contain first template');
        System.assert(json.contains('template-B'), 'Should contain second template');
        System.assert(json.contains(emailSubject), 'Should contain email subject');
        System.assert(json.contains('SalesforceRecordId'), 'Should contain record ID field');
        System.assert(json.contains(language), 'Should contain language');
    }
}
