/***********************************************************************************************************
 * Name         : BatchJobUtils
 * Purpose      : Helper class to manage jobs
 **********************************************************************************************************/
public with sharing class BatchJobUtils {
    public static final Set<String> ACTIVE_JOB_STATUS = new Set<String>{
        'Holding',
        'Preparing',
        'Processing',
        'Queued'
    };

    /**
     * @description generic method to send emails
     * @param  String email recipients
     * @param  String email sender
     * @param  String email subject
     * @param  String email bodies, expected value:
     * 'ExecutionWithErrorsBody;SuccessfulExecutionBody' (separated by semicolon)
     * @param  String email csv attachment file name
     * @param  SObject list records to be added to the attachment
     */
    public static void sendEmail(
        String recipients,
        String sender,
        String subject,
        String textBodies,
        String fileName,
        List<SObject> SObjectList
    ) {
        Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
        email.setToAddresses(ConfigUtils.getConfigMultipleStrings(recipients));
        email.setSenderDisplayName(ConfigUtils.getConfigString(sender));
        email.setSubject(ConfigUtils.getConfigString(subject).replace('{{NOW}}', Datetime.now().format()));
        List<String> bodies = ConfigUtils.getConfigMultipleStrings(textBodies);
        if (!SObjectList.isEmpty()) {
            Messaging.EmailFileAttachment csvAttc = new Messaging.EmailFileAttachment();
            Blob csvBlob = FormatUtils.createCsvBlobFromSObjectList(SObjectList);
            csvAttc.setFileName(fileName);
            csvAttc.setBody(csvBlob);
            email.setFileAttachments(new List<Messaging.EmailFileAttachment>{ csvAttc });
            email.setPlainTextBody(bodies[0]);
        } else {
            email.setPlainTextBody(bodies.size() > 1 ? bodies[1] : bodies[0]);
        }
        Messaging.sendEmail(new List<Messaging.SingleEmailMessage>{ email });
    }

    /**
     * @description returns a monitor query for the BatchJobsMonitor
     * @param  String name of the class to be monitored
     */
    public static String getMonitorQuery(String className) {
        return 'SELECT Id, ApexClass.Name, CreatedDate, Status, ExtendedStatus, TotalJobItems, JobItemsProcessed, NumberOfErrors, CompletedDate' +
            ' FROM AsyncApexJob WHERE ApexClass.Name = \'' +
            className +
            '\' ORDER BY CreatedDate DESC LIMIT 10';
    }

    /**
     * @description Schedules an instance of a class using the cronExp provided
     * @param  instance   instance of Schedulable
     * @param  cronExp    One or multiple cron expressions separated by ;
     */
    public static void scheduleJob(Schedulable instance, String cronExp) {
        scheduleJob(instance, cronExp, null);
    }

    /**
     * @description Schedules an instance of a class using the cronExp provided
     * @param String    instance of Schedulable
     * @param String    one or multiple cron expressions separated by ;
     * @param String    suffix for the job name
     */
    public static void scheduleJob(Schedulable instance, String cronExp, String suffix) {
        List<String> cronExps = cronExp.split(';');
        String jobName = String.valueOf(instance).split(':')[0];

        jobName = String.isNotBlank(suffix) ? jobName + ' ' + suffix : jobName;

        if (Test.isRunningTest()) {
            killSimilarJobsInTestContext(jobName);
        }
        Integer i = 1;
        for (String cron : cronExps) {
            String jobDesc = cronExps.size() == 1 ? jobName : jobName + ' ' + i;

            System.schedule(jobDesc, cron, instance);
            i++;
        }
    }

    /************************************************************************************
     * Method: getValidFormResponseWrapperComplete
     * Description : This is needed to avoid "The Apex job named X is already scheduled
     *  for execution" error in Test Context.
     *************************************************************************************/
    private static void killSimilarJobsInTestContext(String jobName) {
        for (CronTrigger ct : [SELECT Id FROM CronTrigger WHERE CronJobDetail.Name LIKE :jobName + '%']) {
            try {
                system.abortJob(ct.id);
            } catch (exception e) {
            }
        }
    }
}
