/**
 * @description Main invocable class for combining multiple Docusign templates into a single envelope
 * @author Paul Huliganga
 * @date 2026-02-23
 */
global with sharing class DocusignCompositeEnvelopeBuilder {
    
    /**
     * @description Invocable method called from Salesforce Screen Flow
     * @param requests List of request objects containing template IDs and metadata
     * @return List of result objects with envelope ID and status
     */
    @InvocableMethod(
        label='Send Composite Docusign Envelope' 
        description='Combines multiple Docusign templates into a single envelope'
        category='Docusign'
    )
    public static List<Result> sendCompositeEnvelope(List<Request> requests) {
        List<Result> results = new List<Result>();
        
        // Process first request (Flow only sends one)
        if (requests == null || requests.isEmpty()) {
            return buildErrorResult('No request provided');
        }
        
        Request req = requests[0];
        Result result = new Result();
        
        try {
            // Validate inputs
            validateInputs(req);
            
            // Remove duplicates and sort alphabetically
            List<String> sortedTemplateIds = sortTemplatesAlphabetically(
                new Set<String>(req.templateIds)
            );
            
            // Build composite envelope JSON
            String envelopeJSON = buildCompositeEnvelopeJSON(
                sortedTemplateIds,
                req.recordId,
                req.language,
                req.emailSubject,
                null  // customFields not supported in InvocableVariable (Phase 2 enhancement)
            );
            
            // Get Docusign credentials
            DocusignCredentials creds = DocusignCredentials.getInstance();
            
            // Call Docusign API
            String envelopeId = DocusignAPIService.createCompositeEnvelope(
                envelopeJSON,
                creds
            );
            
            // Success
            result.envelopeId = envelopeId;
            result.success = true;
            result.errorMessage = null;
            
            // Log success
            logAPICall(req.templateIds.size(), envelopeId, 'Success', null);
            
        } catch (Exception e) {
            // Error handling
            result.success = false;
            result.errorMessage = e.getMessage();
            result.envelopeId = null;
            
            // Log error
            logAPICall(
                req.templateIds != null ? req.templateIds.size() : 0,
                null,
                'Error',
                e.getMessage() + '\n' + e.getStackTraceString()
            );
            
            // Re-throw if critical (governor limits)
            if (e instanceof System.LimitException) {
                throw e;
            }
        }
        
        results.add(result);
        return results;
    }
    
    /**
     * @description Validates input parameters
     * @param req Request object to validate
     * @throws IllegalArgumentException if validation fails
     */
    private static void validateInputs(Request req) {
        if (req.templateIds == null || req.templateIds.isEmpty()) {
            throw new IllegalArgumentException('At least one template ID is required');
        }
        
        if (req.templateIds.size() > 14) {
            throw new IllegalArgumentException('Maximum 14 templates allowed per envelope');
        }
        
        if (String.isBlank(req.recordId)) {
            throw new IllegalArgumentException('Salesforce record ID is required');
        }
        
        // Check for null template IDs
        for (String templateId : req.templateIds) {
            if (String.isBlank(templateId)) {
                throw new IllegalArgumentException('Template ID cannot be blank');
            }
        }
    }
    
    /**
     * @description Removes duplicates and sorts template IDs alphabetically
     * @param templateIdSet Set of template IDs
     * @return Sorted list of unique template IDs
     */
    private static List<String> sortTemplatesAlphabetically(Set<String> templateIdSet) {
        List<String> sortedList = new List<String>(templateIdSet);
        sortedList.sort();
        return sortedList;
    }
    
    /**
     * @description Builds composite envelope JSON for Docusign API
     * @param templateIds List of template IDs to combine
     * @param recordId Salesforce record ID for custom fields
     * @param language Language code (en/es)
     * @param emailSubject Email subject line
     * @param customFields Map of custom field name/value pairs
     * @return JSON string for Docusign API request
     */
    @TestVisible
    private static String buildCompositeEnvelopeJSON(
        List<String> templateIds,
        String recordId,
        String language,
        String emailSubject,
        Map<String, String> customFields
    ) {
        // Build composite templates array
        List<Object> compositeTemplates = new List<Object>();
        
        Integer sequence = 1;
        for (String templateId : templateIds) {
            Map<String, Object> compositeTemplate = new Map<String, Object>{
                'compositeTemplateId' => String.valueOf(sequence),
                'serverTemplates' => new List<Object>{
                    new Map<String, Object>{
                        'sequence' => String.valueOf(sequence),
                        'templateId' => templateId
                    }
                }
            };
            
            // Add custom fields if this is the first template
            if (sequence == 1 && (String.isNotBlank(recordId) || customFields != null)) {
                compositeTemplate.put('inlineTemplates', buildInlineTemplates(recordId, language, customFields));
            }
            
            compositeTemplates.add(compositeTemplate);
            sequence++;
        }
        
        // Build envelope object
        Map<String, Object> envelope = new Map<String, Object>{
            'status' => 'sent',
            'emailSubject' => String.isNotBlank(emailSubject) 
                ? emailSubject 
                : 'Please review and sign these forms',
            'compositeTemplates' => compositeTemplates
        };
        
        return JSON.serialize(envelope);
    }
    
    /**
     * @description Builds inline templates for custom fields
     * @param recordId Salesforce record ID
     * @param language Language code
     * @param customFields Additional custom fields
     * @return List of inline template objects
     */
    private static List<Object> buildInlineTemplates(
        String recordId,
        String language,
        Map<String, String> customFields
    ) {
        List<Object> textCustomFields = new List<Object>();
        
        // Add Salesforce record ID
        if (String.isNotBlank(recordId)) {
            textCustomFields.add(new Map<String, Object>{
                'name' => 'SalesforceRecordId',
                'value' => recordId,
                'show' => 'false',
                'required' => 'false'
            });
        }
        
        // Add language
        if (String.isNotBlank(language)) {
            textCustomFields.add(new Map<String, Object>{
                'name' => 'Language',
                'value' => language,
                'show' => 'false',
                'required' => 'false'
            });
        }
        
        // Add additional custom fields
        if (customFields != null && !customFields.isEmpty()) {
            for (String fieldName : customFields.keySet()) {
                textCustomFields.add(new Map<String, Object>{
                    'name' => fieldName,
                    'value' => customFields.get(fieldName),
                    'show' => 'false',
                    'required' => 'false'
                });
            }
        }
        
        return new List<Object>{
            new Map<String, Object>{
                'sequence' => '1',
                'customFields' => new Map<String, Object>{
                    'textCustomFields' => textCustomFields
                }
            }
        };
    }
    
    /**
     * @description Logs API call to debug log (future: custom object)
     * @param templateCount Number of templates in envelope
     * @param envelopeId Docusign envelope ID
     * @param status Success or Error
     * @param errorMessage Error message if applicable
     */
    private static void logAPICall(
        Integer templateCount,
        String envelopeId,
        String status,
        String errorMessage
    ) {
        System.debug(LoggingLevel.INFO, '=== Docusign Composite Envelope API Call ===');
        System.debug(LoggingLevel.INFO, 'Timestamp: ' + System.now());
        System.debug(LoggingLevel.INFO, 'Template Count: ' + templateCount);
        System.debug(LoggingLevel.INFO, 'Envelope ID: ' + envelopeId);
        System.debug(LoggingLevel.INFO, 'Status: ' + status);
        if (String.isNotBlank(errorMessage)) {
            System.debug(LoggingLevel.ERROR, 'Error: ' + errorMessage);
        }
        
        // Future enhancement: Insert into Docusign_API_Log__c custom object
    }
    
    /**
     * @description Helper to build error result
     * @param errorMessage Error message
     * @return List containing single error result
     */
    private static List<Result> buildErrorResult(String errorMessage) {
        Result result = new Result();
        result.success = false;
        result.errorMessage = errorMessage;
        result.envelopeId = null;
        return new List<Result>{ result };
    }
    
    /**
     * @description Input parameters for invocable method (from Screen Flow)
     */
    global class Request {
        @InvocableVariable(
            label='Template IDs' 
            description='List of Docusign template IDs to combine' 
            required=true
        )
        public List<String> templateIds;
        
        @InvocableVariable(
            label='Salesforce Record ID' 
            description='ID of the Salesforce record to attach documents to' 
            required=true
        )
        public String recordId;
        
        @InvocableVariable(
            label='Language' 
            description='Language code (en or es)' 
            required=false
        )
        public String language;
        
        @InvocableVariable(
            label='Email Subject' 
            description='Subject line for envelope email' 
            required=false
        )
        public String emailSubject;
    }
    
    /**
     * @description Output parameters for invocable method (to Screen Flow)
     */
    global class Result {
        @InvocableVariable(
            label='Envelope ID' 
            description='Docusign envelope ID'
        )
        public String envelopeId;
        
        @InvocableVariable(
            label='Success' 
            description='True if envelope was created successfully'
        )
        public Boolean success;
        
        @InvocableVariable(
            label='Error Message' 
            description='Error message if envelope creation failed'
        )
        public String errorMessage;
    }
}
