/***********************************************************************************************************
 * Name         : MailingListUnsubscribeBatchProcess
 * Purpose      : Job to unsubscribe fans from inactive mailiing lists
 **********************************************************************************************************/
public with sharing class MailingListUnsubscribeBatchProcess implements Database.Batchable<SObject>, Database.Stateful, Schedulable, Database.RaisesPlatformEvents {
    public Set<Id> mlIdsToUpdate = new Set<Id>();
    public Map<Id, Subscription_History__c> failedRecords = new Map<Id, Subscription_History__c>();

    public Database.QueryLocator start(Database.BatchableContext bc) {
        Date dt = Date.valueOf(System.today() - ConfigUtils.getConfigInt(ConfigUtils.ML_UNSUBSCRIBE_DAYS));
        String query =
            'SELECT Id, Fan_ID__c, Mailing_List_ID__c, Promo_ID__c, Promo_ID__r.Form_ID__c, ' +
            'Promo_ID__r.Tla__c, Mailing_List_ID__r.ML_Unsub_Reason__c FROM Subscription__c ' +
            'WHERE Active__c = TRUE AND Mailing_List_Id__r.Active__c = FALSE AND Mailing_List_ID__r.ML_UnSub_Reason__c != NULL ' +
            'AND Mailing_List_ID__r.IsProcessed__c = FALSE AND Mailing_List_ID__r.Unsub_Initialize__c =: dt';
        return Database.getQueryLocator(query);
    }

    public void execute(Database.BatchableContext bc, List<Subscription__c> subscriptions) {
        List<Subscription_History__c> subHistoriesToInsert = new List<Subscription_History__c>();

        for (Subscription__c sub : subscriptions) {
            subHistoriesToInsert.add(
                new Subscription_History__c(
                    Subscription_ID__c = sub.Id,
                    Fan_ID__c = sub.Fan_ID__c,
                    Mailing_List_ID__c = sub.Mailing_List_ID__c,
                    Promo_ID__c = sub.Promo_ID__c,
                    Form_ID__c = sub.Promo_ID__r.Form_ID__c,
                    Tla_Id__c = sub.Promo_ID__r.Tla__c,
                    Unsub_Reason__c = sub.Mailing_List_ID__r.ML_Unsub_Reason__c,
                    Action__c = UtilConstants.SH_ACTION_UNSUB
                )
            );
            mlIdsToUpdate.add(sub.Mailing_List_ID__c);
        }

        Database.SaveResult[] saveResult = Database.insert(subHistoriesToInsert, false);
        for (Integer i = 0; i < saveResult.size(); i++) {
            if (!saveResult[i].isSuccess()) {
                failedRecords.put(subHistoriesToInsert[i].Mailing_List_ID__c, subHistoriesToInsert[i]);
            }
        }
    }

    public void finish(Database.BatchableContext bc) {
        if(!mlIdsToUpdate.isEmpty()){
            List<Mailing_List__c> mlsToUpdate = new List<Mailing_List__c>();
            Boolean isProcessed;
            for (Id mlId : mlIdsToUpdate) {
                isProcessed = !failedRecords.containsKey(mlId);
                mlsToUpdate.add(new Mailing_List__c(Id = mlId, isProcessed__c = isProcessed));
            }
    
            update mlsToUpdate;
    
            BatchJobUtils.sendEmail(
                ConfigUtils.ML_UNSUBSCRIBE_RECIPIENTS,
                ConfigUtils.ML_UNSUBSCRIBE_SENDER,
                ConfigUtils.ML_UNSUBSCRIBE_SUBJECT,
                ConfigUtils.ML_UNSUBSCRIBE_BODIES,
                'failedRecords.csv',
                failedRecords.values()
            );
        }
    }

    public void execute(SchedulableContext sc) {
        Database.executeBatch(new MailingListUnsubscribeBatchProcess(), 2000);
    }

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