/***********************************************************************************************************
 * Name         : NR_SendDataToQaBatch
 * Purpose      : Job to send updated Contributors and Societies to QA
 **********************************************************************************************************/
public with sharing class NR_SendDataToQaBatch implements Database.Batchable<SObject>, Schedulable, Database.Stateful, Database.Allowscallouts {
    public String requestor;
    public SalesforceAuthResponseWrapper authResponseWrapper;
    public Boolean isContributor;
    public Map<String, NR_RestReceiveData.ReceiveDataResult> jobResultMap;
    public static Map<String, NR_RestReceiveData.ReceiveDataResult> testJobResultMap;

    public NR_SendDataToQaBatch(
        String requestor,
        Boolean isContributor,
        Map<String, NR_RestReceiveData.ReceiveDataResult> jobResult
    ) {
        this.requestor = requestor;
        this.isContributor = isContributor;
        this.jobResultMap = isContributor ? new Map<String, NR_RestReceiveData.ReceiveDataResult>() : jobResult;
    }

    public NR_SendDataToQaBatch() {
        this.requestor = UserInfo.getUserEmail();
        this.isContributor = true;
        this.jobResultMap = new Map<String, NR_RestReceiveData.ReceiveDataResult>();
    }

    public NR_SendDataToQaBatch(String requestor) {
        this.requestor = requestor;
        this.isContributor = true;
        this.jobResultMap = new Map<String, NR_RestReceiveData.ReceiveDataResult>();
    }

    public Database.QueryLocator start(Database.BatchableContext bc) {
        String query = isContributor
            ? NR_SyncProcessUtils.getContributorQuery()
            : NR_SyncProcessUtils.getSocietyQuery();
        return Database.getQueryLocator(query);
    }

    public void execute(Database.BatchableContext bc, List<SObject> records) {
        try {
            if (authResponseWrapper == null) {
                String tokenEndpoint = ConfigUtils.getConfigString(NR_Constants.NR_DATA_SYNC_QA_ENDPOINT);
                String username = ConfigUtils.getConfigString(NR_Constants.NR_DATA_SYNC_QA_USERNAME);
                String password = ConfigUtils.getConfigString(NR_Constants.NR_DATA_SYNC_QA_PASSWORD);
                String clientId = ConfigUtils.getConfigString(NR_Constants.NR_DATA_SYNC_QA_CLIENT_ID);
                String clientSecret = ConfigUtils.getConfigString(NR_Constants.NR_DATA_SYNC_QA_CLIENT_SECRET);
                authResponseWrapper = NR_SyncProcessUtils.getToken(
                    tokenEndpoint,
                    username,
                    password,
                    clientId,
                    clientSecret
                );
            }
            NR_RestReceiveData.ReceiveDataResult batchResult = NR_SyncProcessUtils.sendData(
                records,
                authResponseWrapper
            );
            mergeResults(batchResult);
        } catch (Exception ex) {
            ErrorLogger.logError(ex);
        }
    }

    public void finish(Database.BatchableContext bc) {
        testJobResultMap = jobResultMap;
        if (isContributor && !Test.isRunningTest()) {
            Database.executeBatch(new NR_SendDataToQaBatch(requestor, false, jobResultMap), 2000);
        } else {
            try {
                sendEmail();
                insert new Log__c(
                    Category__c = NR_Constants.NR_DATA_SYNC_LOG_CAT_RESULT,
                    Message__c = JSON.serialize(jobResultMap)
                );
            } catch (Exception ex) {
                ErrorLogger.logError(ex);
            }
        }
    }

    public void execute(SchedulableContext sc) {
        Database.executeBatch(new NR_SendDataToQaBatch(UserInfo.getUserEmail()));
    }

    public static void schedule() {
        String cronExp = ConfigUtils.getConfigString(NR_Constants.NR_DATA_SYNC_CRON_EXP);
        System.schedule('NR_SendDataToQaBatch', cronExp, new NR_SendDataToQaBatch(UserInfo.getUserEmail()));
    }

    @TestVisible
    private void mergeResults(NR_RestReceiveData.ReceiveDataResult batchResult) {
        NR_RestReceiveData.ReceiveDataResult previousResult = jobResultMap.get(batchResult.sObjectType);

        if (previousResult != null) {
            previousResult.received += batchResult.received;
            previousResult.inserted += batchResult.inserted;
            previousResult.updated += batchResult.updated;
            if (!batchResult.errors.isEmpty()) {
                previousResult.errors.addAll(batchResult.errors);
            }
            previousResult.failedInserts += batchResult.failedInserts;
            previousResult.failedUpdates += batchResult.failedUpdates;
        } else {
            jobResultMap.put(batchResult.sObjectType, batchResult);
        }
    }

    private void sendEmail() {
        Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
        email.setToAddresses(new List<String>{ requestor });
        email.setSenderDisplayName('Sync Data Job');
        email.setSubject('Sync Data Job' + Date.today().format());
        email.setPlainTextBody('Sync finished successfully\n\n' + JSON.serialize(jobResultMap));
        Messaging.sendEmail(new List<Messaging.SingleEmailMessage>{ email });
    }
}
