/***********************************************************************************************************
 * Name         : AwalClosedWonEmailQueueable
 * Purpose      : Send and email with a .pdf and .csv files attached
 **********************************************************************************************************/

public class AwalClosedWonEmailQueueable implements Queueable, Database.AllowsCallouts {
    public final static String ARTIST_OR_LABEL_NAME_FIELD = 'ArtistOrLabelName__c';
    public final static String MASTER_APPROVER_FIELD = 'MasterApproverId__c';
    public final static List<String> LOOKUP_FIELDS = new List<String>{
        ARTIST_OR_LABEL_NAME_FIELD,
        MASTER_APPROVER_FIELD
    };

    private List<Opportunity> opportunities;

    public AwalClosedWonEmailQueueable(List<Opportunity> opportunities) {
        this.opportunities = opportunities;
    }

    public void execute(QueueableContext context) {
        Integer batchSize = opportunities.size() < 10 ? opportunities.size() : 10;

        Map<Id, MasterApprovers__c> masterApproversMap = new Map<Id, MasterApprovers__c>(
            MasterApproverUtils.getRecordsByCustomerAndTeam(
                new Set<String>{ AWAL_Constants.AWAL },
                new Set<String>{ AWAL_Constants.MASTER_APPROVERS_APPROVERS_TEAM }
            )
        );

        Map<String, String> dealMakersEmailAddresses = AWAL_EmailAlertsHandler.getDealMakersEmailAddresses(
            opportunities
        );
        List<Messaging.Email> emails = new List<Messaging.Email>();
        Map<String, String> accountMap = getNamesFromLookup(opportunities, ARTIST_OR_LABEL_NAME_FIELD);
        Map<String, String> approversMap = getNamesFromLookup(opportunities, MASTER_APPROVER_FIELD);

        for (Integer i = 0; i < batchSize; i++) {
            Opportunity opp = opportunities[0];
            Messaging.EmailFileAttachment csvAttachment = generateCsvAttachment(opp, accountMap, approversMap);
            Messaging.EmailFileAttachment pdfAttachment = generatePdfAttachment(opp);

            Messaging.singleEmailMessage message = new Messaging.SingleEmailMessage();

            List<String> recipients = new List<String>{ UserInfo.getUserEmail() };

            String leadDealMaker = opp.LeadDealMaker__c;
            if (String.isNotBlank(leadDealMaker) && dealMakersEmailAddresses.containsKey(leadDealMaker)) {
                EmailUtils.addRecipient(recipients, dealMakersEmailAddresses.get(leadDealMaker));
            }
            if (String.isNotBlank(opp.OtherDealMakers__c)) {
                for (String otherDealMaker : opp.OtherDealMakers__c.split(';')) {
                    if (dealMakersEmailAddresses.containsKey(otherDealMaker)) {
                        EmailUtils.addRecipient(recipients, dealMakersEmailAddresses.get(otherDealMaker));
                    }
                }
            }
            recipients.addAll(ConfigUtils.getConfigMultipleStrings(ConfigUtils.AWAL_CLOSED_WON_EMAIL_RECIPIENTS));
            EmailUtils.addRecipient(recipients, masterApproversMap.get(opp.MasterApproverId__c)?.User__r.Email);
            message.setToAddresses(recipients);
            message.setSubject(
                ConfigUtils.getConfigString(ConfigUtils.AWAL_CLOSED_WON_EMAIL_SUBJECT)
                    .replace(AWAL_EmailAlertsHandler.EMAIL_TOKEN_OPPORTUNITY_NAME, opp.Name)
            );
            message.setHtmlBody(ConfigUtils.getConfigString(ConfigUtils.AWAL_CLOSED_WON_EMAIL_BODY));
            message.setFileAttachments(new List<Messaging.EmailFileAttachment>{ csvAttachment, pdfAttachment });
            emails.add(message);

            opportunities.remove(opportunities.indexOf(opp));
        }

        Messaging.sendEmail(emails);

        if (!opportunities.isEmpty()) {
            System.enqueueJob(new AwalClosedWonEmailQueueable(opportunities));
        }
    }

    /**
     * @description Generates a .PDF email attachment from a VFP template based on the given Opportunity
     * @param opp Opportunity record that will be used as base to generate the .pdf file
     * @return attachment Email attachment object
     */
    private static Messaging.EmailFileAttachment generatePdfAttachment(Opportunity opp) {
        PageReference page = new PageReference('/apex/AwalPdfAttachment');
        page.getParameters().put('opportunityId', opp.Id);

        Messaging.EmailFileAttachment attachment = new Messaging.EmailFileAttachment();
        attachment.setFileName(opp.Name + '.pdf');
        attachment.setContentType('application/pdf');
        if (Test.isRunningTest()) {
            attachment.setBody(Blob.valueOf('PDF Content'));
        } else {
            attachment.setBody(page.getContentAsPDF());
        }

        return attachment;
    }

    /**
     * @description Generates a .CSV email attachment based on the given Opportunity
     * @param opp Opportunity record that will be used as base to generate the .csv file
     * @return attachment Email attachment object
     */
    private static Messaging.EmailFileAttachment generateCsvAttachment(
        Opportunity opp,
        Map<String, String> accountsMap,
        Map<String, String> approversMap
    ) {
        List<Schema.FieldSetMember> fieldSetMembers = SObjectType.Opportunity.FieldSets.AWAL_AllFields.getFields();
        List<String> rows = new List<String>{};

        for (Schema.FieldSetMember fieldSetMember : fieldSetMembers) {
            String field = fieldSetMember.getFieldPath();
            if (LOOKUP_FIELDS.contains(field)) {
                String value = field == ARTIST_OR_LABEL_NAME_FIELD
                    ? accountsMap.get(String.valueOf(opp.get(field)))
                    : approversMap.get(String.valueOf(opp.get(field)));
                rows.add(
                    fieldSetMember.getLabel().escapeCsv() + ',' + (String.isBlank(value) ? '' : value.escapeCsv())
                );
            } else {
                rows.add(
                    fieldSetMember.getLabel().escapeCsv() +
                        ',' +
                        FormatUtils.formatFieldsToString(opp, field, fieldSetMember.getType().toString()).escapeCsv()
                );
            }
        }

        Messaging.EmailFileAttachment attachment = new Messaging.EmailFileAttachment();
        attachment.setFileName(opp.Name + '.csv');
        attachment.setBody(Blob.valueOf(String.join(rows, '\n')));
        attachment.setContentType('text/csv');

        return attachment;
    }

    /**
     * @description Gets the Name of a related record
     * @param List<Opportunity> List of Opportunities
     * @param String Lookup field
     * @return Map <String, Name>
     */
    private static Map<String, String> getNamesFromLookup(List<Opportunity> opps, String field) {
        Map<String, String> result = new Map<String, String>();

        Set<String> ids = new Set<String>();

        for (Opportunity opp : opps) {
            if (opp.get(field) != null) {
                ids.add(String.valueOf(opp.get(field)));
            }
        }

        if (!ids.isEmpty()) {
            String sObjType = field == ARTIST_OR_LABEL_NAME_FIELD ? 'Account' : 'MasterApprovers__c';
            String query = 'SELECT Id, Name FROM ' + sObjType + ' WHERE Id IN :ids';
            for (SObject record : Database.query(query)) {
                result.put(String.valueOf(record.get('Id')), String.valueOf(record.get('Name')));
            }
        }
        return result;
    }
}