/***********************************************************************************************************
 * Name         : CleanupBatch
 * Purpose      : Job to delete records from sObjects in Cleanup_Setting__mdt
 **********************************************************************************************************/

public with sharing class CleanupBatch implements Database.Batchable<SObject>, Database.Stateful, Schedulable, Database.RaisesPlatformEvents {
    Integer index = 0;
    List<Cleanup_Setting__mdt> cleanupSettings;
    Map<String, Integer> resultMap;
    String sObjectType;
    Integer deletedRecords = 0;

    public CleanupBatch() {
        this.index = 0;
        this.resultMap = new Map<String, Integer>();
    }

    public CleanupBatch(Integer index, Map<String, Integer> resultMap) {
        this.index = index;
        this.resultMap = resultMap;
    }

    public Database.QueryLocator start(Database.BatchableContext bc) {
        cleanupSettings = [
            SELECT Label, Days_To_Keep__c
            FROM Cleanup_Setting__mdt
            WHERE Active__c = TRUE
            ORDER BY Label ASC
        ];

        String query;
        if (index < cleanupSettings.size()) {
            sObjectType = cleanupSettings[index].Label;
            Integer deletionRange = Integer.valueOf(cleanupSettings[index].Days_To_Keep__c);
            if (deletionRange > 0) {
                query = 'SELECT Id FROM ' + sObjectType + ' WHERE CreatedDate < LAST_N_DAYS: ' + deletionRange;
            } else {
                query = 'SELECT Id FROM ' + sObjectType;
            }

            resultMap.put(sObjectType, 0);
        } else {
            query = 'SELECT Id FROM Account LIMIT 0';
        }

        return Database.getQueryLocator(query);
    }

    public void execute(Database.BatchableContext bc, List<SObject> records) {
        delete records;
        deletedRecords += records.size();
    }

    public void finish(Database.BatchableContext bc) {
        if (String.isNotBlank(sObjectType)) {
            resultMap.put(sObjectType, deletedRecords);
        }
        index++;
        if (index < cleanupSettings.size()) {
            Database.executeBatch(new CleanupBatch(index, resultMap), 2000);
        } else if (!resultMap.isEmpty()) {
            List<String> body = new List<String>();
            for (String sObjectName : resultMap.keySet()) {
                body.add(sObjectName + ': ' + resultMap.get(sObjectName).format());
            }

            Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
            message.setToAddresses(ConfigUtils.getConfigMultipleStrings(ConfigUtils.CLEANUP_EMAIL_RECIPIENTS));
            message.setSenderDisplayName(ConfigUtils.getConfigString(ConfigUtils.CLEANUP_EMAIL_SENDER));
            message.setSubject('Cleanup Job ' + Datetime.now().format());

            message.setPlainTextBody(String.join(body, '\n'));
            Messaging.sendEmail(new List<Messaging.SingleEmailMessage>{ message });
        }
    }

    public void execute(SchedulableContext sc) {
        Database.executeBatch(new CleanupBatch());
    }

    public static void schedule() {
        BatchJobUtils.scheduleJob(new CleanupBatch(), ConfigUtils.getConfigString(ConfigUtils.CLEANUP_CRON_EXP));
    }
}
